Skip to content

Commit 228ee65

Browse files
committed
chore: fix retry
1 parent 41b34d3 commit 228ee65

File tree

5 files changed

+483
-27
lines changed

5 files changed

+483
-27
lines changed

infrastructure/web3-adapter/src/evault/evault.ts

Lines changed: 160 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ const CONFIG = {
1717
MAX_RETRIES: 3,
1818
RETRY_DELAY: 1000, // 1 second base delay
1919
CONNECTION_POOL_SIZE: 10,
20+
HEALTH_CHECK_TIMEOUT: 5000, // 5 seconds for health check
21+
MAX_HEALTH_CHECK_FAILURES: 3, // Max consecutive failures before re-resolution
2022
} as const;
2123

2224
const STORE_META_ENVELOPE = `
@@ -126,6 +128,10 @@ export class EVaultClient {
126128
private endpoints: Map<string, string> = new Map();
127129
private tokenInfo: TokenInfo | null = null;
128130
private isDisposed = false;
131+
132+
// Health check tracking
133+
private healthCheckFailures: Map<string, number> = new Map();
134+
private lastHealthCheck: Map<string, number> = new Map();
129135

130136
constructor(
131137
private registryUrl: string,
@@ -141,6 +147,8 @@ export class EVaultClient {
141147
this.isDisposed = true;
142148
this.clients.clear();
143149
this.endpoints.clear();
150+
this.healthCheckFailures.clear();
151+
this.lastHealthCheck.clear();
144152
this.tokenInfo = null;
145153
}
146154

@@ -274,8 +282,15 @@ export class EVaultClient {
274282
if (this.clients.has(w3id)) {
275283
const client = this.clients.get(w3id)!;
276284
const endpoint = this.endpoints.get(w3id)!;
277-
console.log('reusing existing client for w3id:', w3id, 'endpoint:', endpoint);
278-
return client;
285+
286+
// Check if the cached endpoint is still healthy
287+
if (await this.isEndpointHealthy(w3id, endpoint)) {
288+
console.log('reusing existing client for w3id:', w3id, 'endpoint:', endpoint);
289+
return client;
290+
} else {
291+
console.log('cached endpoint is unhealthy, removing and re-resolving for w3id:', w3id);
292+
this.removeCachedClient(w3id);
293+
}
279294
}
280295

281296
// Resolve endpoint for this specific w3id
@@ -294,10 +309,153 @@ export class EVaultClient {
294309
this.clients.set(w3id, client);
295310
this.endpoints.set(w3id, endpoint);
296311

312+
// Initialize health check tracking
313+
this.healthCheckFailures.set(w3id, 0);
314+
this.lastHealthCheck.set(w3id, Date.now());
315+
297316
console.log('created new client for w3id:', w3id, 'endpoint:', endpoint);
298317
return client;
299318
}
300319

320+
/**
321+
* Check if a cached endpoint is still healthy
322+
*/
323+
private async isEndpointHealthy(w3id: string, endpoint: string): Promise<boolean> {
324+
try {
325+
// Extract base URL from GraphQL endpoint
326+
const baseUrl = endpoint.replace('/graphql', '');
327+
328+
// Check if we should perform health check (avoid too frequent checks)
329+
const now = Date.now();
330+
const lastCheck = this.lastHealthCheck.get(w3id) || 0;
331+
const timeSinceLastCheck = now - lastCheck;
332+
333+
// Only check every 30 seconds to avoid performance impact
334+
if (timeSinceLastCheck < 30000) {
335+
return true; // Assume healthy if checked recently
336+
}
337+
338+
// Perform health check on the whois endpoint
339+
const healthCheckUrl = `${baseUrl}/whois`;
340+
console.log(`Health checking endpoint for ${w3id}: ${healthCheckUrl}`);
341+
342+
const controller = new AbortController();
343+
const timeoutId = setTimeout(() => controller.abort(), CONFIG.HEALTH_CHECK_TIMEOUT);
344+
345+
const response = await fetch(healthCheckUrl, {
346+
method: 'HEAD',
347+
signal: controller.signal,
348+
});
349+
350+
clearTimeout(timeoutId);
351+
352+
if (response.ok) {
353+
// Reset failure count on success
354+
this.healthCheckFailures.set(w3id, 0);
355+
this.lastHealthCheck.set(w3id, now);
356+
return true;
357+
} else {
358+
throw new Error(`Health check failed with status: ${response.status}`);
359+
}
360+
361+
} catch (error) {
362+
console.log(`Health check failed for ${w3id}:`, error instanceof Error ? error.message : 'Unknown error');
363+
364+
// Increment failure count
365+
const currentFailures = this.healthCheckFailures.get(w3id) || 0;
366+
const newFailures = currentFailures + 1;
367+
this.healthCheckFailures.set(w3id, newFailures);
368+
this.lastHealthCheck.set(w3id, Date.now());
369+
370+
// If we've had too many consecutive failures, mark as unhealthy
371+
if (newFailures >= CONFIG.MAX_HEALTH_CHECK_FAILURES) {
372+
console.log(`Endpoint for ${w3id} marked as unhealthy after ${newFailures} consecutive failures`);
373+
return false;
374+
}
375+
376+
// Still allow some failures before marking as unhealthy
377+
return true;
378+
}
379+
}
380+
381+
/**
382+
* Remove cached client and endpoint for a specific w3id
383+
*/
384+
private removeCachedClient(w3id: string): void {
385+
this.clients.delete(w3id);
386+
this.endpoints.delete(w3id);
387+
this.healthCheckFailures.delete(w3id);
388+
this.lastHealthCheck.delete(w3id);
389+
console.log(`Removed cached client for ${w3id}`);
390+
}
391+
392+
/**
393+
* Manually trigger a health check for a specific w3id
394+
* Useful for testing or forcing re-resolution
395+
*/
396+
public async forceHealthCheck(w3id: string): Promise<boolean> {
397+
if (!this.clients.has(w3id)) {
398+
console.log(`No cached client found for ${w3id}`);
399+
return false;
400+
}
401+
402+
const endpoint = this.endpoints.get(w3id);
403+
if (!endpoint) {
404+
console.log(`No cached endpoint found for ${w3id}`);
405+
return false;
406+
}
407+
408+
// Force health check by clearing last check time
409+
this.lastHealthCheck.set(w3id, 0);
410+
411+
const isHealthy = await this.isEndpointHealthy(w3id, endpoint);
412+
413+
if (!isHealthy) {
414+
console.log(`Forced health check failed for ${w3id}, removing cached client`);
415+
this.removeCachedClient(w3id);
416+
}
417+
418+
return isHealthy;
419+
}
420+
421+
/**
422+
* Get health status for all cached endpoints
423+
*/
424+
public getHealthStatus(): Record<string, {
425+
endpoint: string;
426+
failures: number;
427+
lastCheck: number;
428+
isHealthy: boolean;
429+
}> {
430+
const status: Record<string, any> = {};
431+
432+
for (const [w3id, endpoint] of this.endpoints) {
433+
const failures = this.healthCheckFailures.get(w3id) || 0;
434+
const lastCheck = this.lastHealthCheck.get(w3id) || 0;
435+
const isHealthy = failures < CONFIG.MAX_HEALTH_CHECK_FAILURES;
436+
437+
status[w3id] = {
438+
endpoint,
439+
failures,
440+
lastCheck,
441+
isHealthy,
442+
};
443+
}
444+
445+
return status;
446+
}
447+
448+
/**
449+
* Clear all cached clients (useful for testing or forcing fresh connections)
450+
*/
451+
public clearCache(): void {
452+
console.log('Clearing all cached clients and endpoints');
453+
this.clients.clear();
454+
this.endpoints.clear();
455+
this.healthCheckFailures.clear();
456+
this.lastHealthCheck.clear();
457+
}
458+
301459
async storeMetaEnvelope(envelope: MetaEnvelope): Promise<string> {
302460
return this.withRetry(async () => {
303461
const client = await this.ensureClient(envelope.w3id).catch(() => {

infrastructure/web3-adapter/tsconfig.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"compilerOptions": {
33
"target": "ES2021",
44
"module": "NodeNext",
5-
"moduleResolution": "node",
5+
"moduleResolution": "NodeNext",
66
"lib": ["ES2020"],
77
"declaration": true,
88
"outDir": "./dist",

platforms/registry/package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,11 @@
1515
"migration:create": "npm run typeorm migration:create"
1616
},
1717
"dependencies": {
18-
"@fastify/cors": "8.4.1",
18+
"@fastify/cors": "^8.4.0",
19+
"@kubernetes/client-node": "^0.20.0",
1920
"@fastify/jwt": "^7.2.3",
2021
"axios": "^1.6.7",
21-
"dotenv": "^16.5.0",
22+
"dotenv": "^16.4.5",
2223
"fastify": "^4.26.1",
2324
"jose": "^5.2.2",
2425
"pg": "^8.11.3",

platforms/registry/src/index.ts

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import dotenv from "dotenv";
44
import path from "path";
55
import { AppDataSource } from "./config/database";
66
import { VaultService } from "./services/VaultService";
7+
import { UriResolutionService } from "./services/UriResolutionService";
78
import cors from "@fastify/cors";
89

910
dotenv.config({ path: path.resolve(__dirname, "../../../.env") });
@@ -32,6 +33,9 @@ const initializeDatabase = async () => {
3233
// Initialize VaultService
3334
const vaultService = new VaultService(AppDataSource.getRepository("Vault"));
3435

36+
// Initialize UriResolutionService for health checks and Kubernetes fallbacks
37+
const uriResolutionService = new UriResolutionService();
38+
3539
// Middleware to check shared secret
3640
const checkSharedSecret = async (request: any, reply: any) => {
3741
const authHeader = request.headers.authorization;
@@ -138,10 +142,15 @@ server.get("/resolve", async (request, reply) => {
138142
return reply.status(404).send({ error: "Service not found" });
139143
}
140144

145+
// Resolve the URI with health check and Kubernetes fallback
146+
const resolvedUri = await uriResolutionService.resolveUri(vault.uri);
147+
141148
return {
142149
ename: vault.ename,
143-
uri: vault.uri,
150+
uri: resolvedUri,
144151
evault: vault.evault,
152+
originalUri: vault.uri, // Include original URI for debugging
153+
resolved: resolvedUri !== vault.uri, // Flag if URI was resolved
145154
};
146155
} catch (error) {
147156
server.log.error(error);
@@ -153,11 +162,22 @@ server.get("/resolve", async (request, reply) => {
153162
server.get("/list", async (request, reply) => {
154163
try {
155164
const vaults = await vaultService.findAll();
156-
return vaults.map(vault => ({
157-
ename: vault.ename,
158-
uri: vault.uri,
159-
evault: vault.evault,
160-
}));
165+
166+
// Resolve URIs for all vaults
167+
const resolvedVaults = await Promise.all(
168+
vaults.map(async (vault) => {
169+
const resolvedUri = await uriResolutionService.resolveUri(vault.uri);
170+
return {
171+
ename: vault.ename,
172+
uri: resolvedUri,
173+
evault: vault.evault,
174+
originalUri: vault.uri,
175+
resolved: resolvedUri !== vault.uri,
176+
};
177+
})
178+
);
179+
180+
return resolvedVaults;
161181
} catch (error) {
162182
server.log.error(error);
163183
reply.status(500).send({ error: "Failed to list vault entries" });

0 commit comments

Comments
 (0)