fix: guard AbortError from retry counting; eviction policy removes stale keys

This commit is contained in:
Ilia Mashkov
2026-04-04 09:40:21 +03:00
parent d21de1bf78
commit f88729cc77
3 changed files with 29 additions and 0 deletions

View File

@@ -202,6 +202,11 @@ export class AppliedFontsManager {
if (results[j].status === 'rejected') {
const [key, config] = chunk[j];
const reason = (results[j] as PromiseRejectedResult).reason;
// Aborted fetches are not retriable failures — skip silently
const isAbort = reason instanceof FontFetchError
&& reason.cause instanceof Error
&& reason.cause.name === 'AbortError';
if (isAbort) continue;
if (reason instanceof FontFetchError) {
console.error(`Font fetch failed: ${config.name}`, reason);
}
@@ -276,6 +281,7 @@ export class AppliedFontsManager {
// Clean up remaining state
this.#loadedFonts.delete(key);
this.statuses.delete(key);
this.#eviction.remove(key);
}
}

View File

@@ -42,6 +42,23 @@ describe('FontEvictionPolicy', () => {
expect(Array.from(policy.keys())).toEqual(expect.arrayContaining(['a@400', 'b@vf']));
});
it('remove deletes key from tracking so it no longer appears in keys()', () => {
policy.touch('a@400', t0);
policy.touch('b@vf', t0);
policy.remove('a@400');
expect(Array.from(policy.keys())).not.toContain('a@400');
expect(Array.from(policy.keys())).toContain('b@vf');
});
it('remove unpins the key so a subsequent touch + TTL would evict it', () => {
policy.touch('a@400', t0);
policy.pin('a@400');
policy.remove('a@400');
// re-touch and check it can be evicted again
policy.touch('a@400', t0);
expect(policy.shouldEvict('a@400', t0 + TTL)).toBe(true);
});
it('clear resets all state', () => {
policy.touch('a@400', t0);
policy.pin('a@400');

View File

@@ -58,6 +58,12 @@ export class FontEvictionPolicy {
return this.#usageTracker.keys();
}
/** Removes a font key from tracking. Called by the orchestrator after eviction. */
remove(key: string): void {
this.#usageTracker.delete(key);
this.#pinnedFonts.delete(key);
}
/** Clears all usage timestamps and pinned keys. */
clear(): void {
this.#usageTracker.clear();