mirror of
https://github.com/nexu-io/open-design.git
synced 2026-10-27 09:51:12 +01:00
fix(telemetry): deliver stability metrics when the app config cannot be read
An unreadable app config made the identity gate throw into 'pending', so metrics were held and eventually dropped instead of being delivered through captureSafety's fallback identity. Treat a config read failure as 'none'.
This commit is contained in:
@@ -158,7 +158,10 @@ export function registerTelemetryRoutes(app: Express, deps: RegisterTelemetryRou
|
||||
const launchEventQueue = createIdentityGatedQueue<Parameters<typeof analyticsService.captureSafety>[0]>({
|
||||
limit: LAUNCH_EVENT_RETENTION_LIMIT,
|
||||
resolveIdentity: async () => {
|
||||
const appCfg = await deps.readAppConfig(dataDir);
|
||||
// An unreadable config can never yield an id, so deliver immediately
|
||||
// through captureSafety's fallback identity instead of holding.
|
||||
let appCfg: Awaited<ReturnType<typeof deps.readAppConfig>>;
|
||||
try { appCfg = await deps.readAppConfig(dataDir); } catch { return 'none'; }
|
||||
if (typeof appCfg.installationId === 'string' && appCfg.installationId) return 'ready';
|
||||
if (appCfg.telemetry?.metrics === false) return 'none';
|
||||
// Only a readable consent that is still on can produce an id later. An
|
||||
|
||||
@@ -179,3 +179,28 @@ it('does not create host fault incidents for clean exits or recovery attempts an
|
||||
expect(fault).not.toHaveBeenCalled();
|
||||
}, fault);
|
||||
});
|
||||
|
||||
it('delivers stability metrics immediately when the app config cannot be read', async () => {
|
||||
version.mockResolvedValue({ version: '2.0.0', channel: 'stable' });
|
||||
const app = express();
|
||||
const telemetry = registerTelemetryRoutes(app, {
|
||||
dataDir: '/unused', readAppConfig: vi.fn(async () => { throw new Error('EACCES'); }) as never,
|
||||
writeAppConfig: vi.fn() as never,
|
||||
});
|
||||
const server = app.listen(0, '127.0.0.1');
|
||||
try {
|
||||
if (!server.listening) await new Promise<void>(resolve => server.once('listening', resolve));
|
||||
const url = `http://127.0.0.1:${(server.address() as AddressInfo).port}`;
|
||||
for (let i = 0; i < 3; i += 1) {
|
||||
const response = await fetch(`${url}/api/observability/event`, {
|
||||
method: 'POST', headers: { 'content-type': 'application/json' },
|
||||
body: JSON.stringify({ event: 'client_process_memory_sample', properties: { ...metric, event_id: `unreadable-${i}` } }),
|
||||
});
|
||||
expect(response.status).toBe(200);
|
||||
}
|
||||
expect(captureSafety).toHaveBeenCalledTimes(3);
|
||||
} finally {
|
||||
telemetry.disposeFatalHandlers();
|
||||
await new Promise<void>((resolve, reject) => server.close(error => error ? reject(error) : resolve()));
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user