refactor(lock-publisher): extract retry loop and initialization logic

Refactors `LockPublisher.publish` to reduce complexity and improve readability.
- Extracts initialization logic into `_initialize()`.
- Extracts the retry loop into `_executeRetryLoop()`.
- Adds `_shouldRetry()` helper for clarity.
- Preserves existing behavior and tests.

Co-authored-by: PR0M3TH3AN <[email protected]>
This commit is contained in:
google-labs-jules[bot]andPR0M3TH3AN committed 2026-02-21 18:32:33 +00:00
1 parent 79e261d7b2
commit ab500a84af
1 file changed
+48 -37
+48 -37
View File
@@ -187,6 +187,17 @@ export class LockPublisher {
* @throws {Error} If publication quorum is not met after all attempts.
*/
async publish() {
this._initialize();
try {
this.healthManager.maybeLogSnapshot(this.allRelays, this.healthConfig, this.healthLogger, 'publish:periodic');
return await this._executeRetryLoop(Date.now());
} finally {
this.pool.close(this.allRelays);
}
}
_initialize() {
const {
poolFactory = () => new SimplePool(),
retryAttempts = 4,
@@ -222,49 +233,49 @@ export class LockPublisher {
this.attemptId = diagnostics.attemptId || randomUUID();
this.allRelays = mergeRelayList(this.relays, this.fallbackRelays);
}
try {
this.healthManager.maybeLogSnapshot(this.allRelays, this.healthConfig, this.healthLogger, 'publish:periodic');
let lastAttemptState = null;
const retryTimeline = [];
const overallStartedAt = Date.now();
let terminalFailureCategory = PUBLISH_FAILURE_CATEGORIES.NON_RETRYABLE;
async _executeRetryLoop(overallStartedAt) {
let lastAttemptState = null;
const retryTimeline = [];
let terminalFailureCategory = PUBLISH_FAILURE_CATEGORIES.NON_RETRYABLE;
// Retry loop: attempts publication until success or max attempts reached
for (let attemptNumber = 1; attemptNumber <= this.maxAttempts; attemptNumber += 1) {
const startedAtMs = Date.now();
lastAttemptState = await this.executePublishCycle();
const elapsedMs = Date.now() - startedAtMs;
retryTimeline.push({
publishAttempt: attemptNumber,
successCount: lastAttemptState.successCount,
relayAttemptedCount: lastAttemptState.attempted.size,
elapsedMs,
});
// Retry loop: attempts publication until success or max attempts reached
for (let attemptNumber = 1; attemptNumber <= this.maxAttempts; attemptNumber += 1) {
const startedAtMs = Date.now();
lastAttemptState = await this.executePublishCycle();
const elapsedMs = Date.now() - startedAtMs;
retryTimeline.push({
publishAttempt: attemptNumber,
successCount: lastAttemptState.successCount,
relayAttemptedCount: lastAttemptState.attempted.size,
elapsedMs,
});
if (lastAttemptState.successCount >= this.minSuccesses) {
this.logSuccess(attemptNumber, lastAttemptState, retryTimeline, overallStartedAt);
return this.event;
}
const { hasTransientFailure, hasPermanentFailure } = this.analyzeFailures(lastAttemptState.failures);
const canRetry = attemptNumber < this.maxAttempts && hasTransientFailure && !hasPermanentFailure;
terminalFailureCategory = this.determineFailureCategory(hasTransientFailure, hasPermanentFailure, attemptNumber);
if (!canRetry) {
break;
}
const nextDelayMs = calculateBackoffDelayMs(attemptNumber, this.retryBaseDelayMs, this.retryCapDelayMs, this.randomFn);
this.logRetry(attemptNumber, lastAttemptState.failures, elapsedMs, nextDelayMs);
await this.sleepFn(nextDelayMs);
if (lastAttemptState.successCount >= this.minSuccesses) {
this.logSuccess(attemptNumber, lastAttemptState, retryTimeline, overallStartedAt);
return this.event;
}
this.handleFinalFailure(lastAttemptState, terminalFailureCategory, retryTimeline, overallStartedAt);
} finally {
this.pool.close(this.allRelays);
const { hasTransientFailure, hasPermanentFailure } = this.analyzeFailures(lastAttemptState.failures);
const canRetry = this._shouldRetry(attemptNumber, hasTransientFailure, hasPermanentFailure);
terminalFailureCategory = this.determineFailureCategory(hasTransientFailure, hasPermanentFailure, attemptNumber);
if (!canRetry) {
break;
}
const nextDelayMs = calculateBackoffDelayMs(attemptNumber, this.retryBaseDelayMs, this.retryCapDelayMs, this.randomFn);
this.logRetry(attemptNumber, lastAttemptState.failures, elapsedMs, nextDelayMs);
await this.sleepFn(nextDelayMs);
}
this.handleFinalFailure(lastAttemptState, terminalFailureCategory, retryTimeline, overallStartedAt);
}
_shouldRetry(attemptNumber, hasTransientFailure, hasPermanentFailure) {
return attemptNumber < this.maxAttempts && hasTransientFailure && !hasPermanentFailure;
}
async executePublishCycle() {