feat(#293): implement retry logic with exponential backoff
Some checks failed
ci/woodpecker/pr/woodpecker Pipeline failed
ci/woodpecker/push/woodpecker Pipeline failed

Add retry capability with exponential backoff for HTTP requests.
- Implement withRetry utility with configurable retry logic
- Exponential backoff: 1s, 2s, 4s, 8s (max)
- Maximum 3 retries by default
- Retry on network errors (ECONNREFUSED, ETIMEDOUT, etc.)
- Retry on 5xx server errors and 429 rate limit
- Do NOT retry on 4xx client errors
- Integrate with connection service for HTTP requests

Fixes #293

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-03 22:07:55 -06:00
parent 43681ca1b1
commit 0b90012947
3 changed files with 353 additions and 6 deletions

View File

@@ -22,6 +22,7 @@ import { firstValueFrom } from "rxjs";
import type { ConnectionRequest, ConnectionDetails } from "./types/connection.types";
import type { PublicInstanceIdentity } from "./types/instance.types";
import { FEDERATION_PROTOCOL_VERSION } from "./constants";
import { withRetry } from "./utils/retry";
@Injectable()
export class ConnectionService {
@@ -87,10 +88,19 @@ export class ConnectionService {
const signature = await this.signatureService.signMessage(request);
const signedRequest: ConnectionRequest = { ...request, signature };
// Send connection request to remote instance
// Send connection request to remote instance with retry logic
try {
await firstValueFrom(
this.httpService.post(`${remoteUrl}/api/v1/federation/incoming/connect`, signedRequest)
await withRetry(
async () => {
return await firstValueFrom(
this.httpService.post(`${remoteUrl}/api/v1/federation/incoming/connect`, signedRequest)
);
},
{
maxRetries: 3,
initialDelay: 1000, // 1s
maxDelay: 8000, // 8s
}
);
this.logger.log(`Connection request sent to ${remoteUrl}`);
} catch (error) {
@@ -368,13 +378,24 @@ export class ConnectionService {
}
/**
* Fetch remote instance identity via HTTP
* Fetch remote instance identity via HTTP with retry logic
*/
private async fetchRemoteIdentity(remoteUrl: string): Promise<PublicInstanceIdentity> {
try {
const normalizedUrl = this.normalizeUrl(remoteUrl);
const response = await firstValueFrom(
this.httpService.get<PublicInstanceIdentity>(`${normalizedUrl}/api/v1/federation/instance`)
const response = await withRetry(
async () => {
return await firstValueFrom(
this.httpService.get<PublicInstanceIdentity>(
`${normalizedUrl}/api/v1/federation/instance`
)
);
},
{
maxRetries: 3,
initialDelay: 1000, // 1s
maxDelay: 8000, // 8s
}
);
return response.data;