mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-08-30 12:09:15 +00:00
perf(statuses): align stack-status cache TTL and invalidation with polling (#1814)
The 3s stack-statuses cache TTL never survived the 10s dashboard poll, so every ordinary poll recomputed. Raise the TTL to 15s and move the git-source label and self-identity enrichment inside the cached payload so cache hits serve fully decorated statuses with zero per-request work. Invalidation closes the gaps the longer TTL would otherwise widen: - DockerEventService drops stack-statuses:<nodeId> on container state events so the UI's state-invalidate refetch recomputes instead of hitting a stale entry. The narrow key only: container events do not reshape stack identity or file roots, and the stats key self-refreshes on its own 2s TTL. - git-source link and unlink invalidate node caches before responding, so the source label stays fresh without waiting for the TTL. - a payload whose enrichment degraded (identity probe failure or git-source scan failure) is never cached, so a mislabeled not-self or 'local' badge cannot persist for a full TTL window. Running outside Docker is not degradation, so host installs cache normally.
This commit is contained in:
@@ -90,6 +90,7 @@ function makeStream(): FakeStream {
|
||||
// ── Setup ──────────────────────────────────────────────────────────────
|
||||
|
||||
import { DockerEventService } from '../services/DockerEventService';
|
||||
import { CacheService } from '../services/CacheService';
|
||||
|
||||
let stream: FakeStream;
|
||||
let service: DockerEventService;
|
||||
@@ -1324,7 +1325,17 @@ describe('DockerEventService - hardening', () => {
|
||||
// ── State-invalidate broadcasts ────────────────────────────────────────
|
||||
|
||||
describe('DockerEventService - state-invalidate broadcasts', () => {
|
||||
it('broadcasts state-invalidate on container start', async () => {
|
||||
let invalidateSpy: ReturnType<typeof vi.spyOn>;
|
||||
|
||||
beforeEach(() => {
|
||||
invalidateSpy = vi.spyOn(CacheService.getInstance(), 'invalidate');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
invalidateSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('broadcasts state-invalidate and drops the statuses cache on container start', async () => {
|
||||
service = new DockerEventService(7, 'node-7');
|
||||
await service.start();
|
||||
|
||||
@@ -1344,6 +1355,11 @@ describe('DockerEventService - state-invalidate broadcasts', () => {
|
||||
containerId: 'aaa',
|
||||
action: 'start',
|
||||
}));
|
||||
// The UI refetch this broadcast triggers must recompute, not hit a
|
||||
// cache entry made stale by the event. Only the statuses key is
|
||||
// touched; the full invalidateNodeCaches helper is not used here.
|
||||
expect(invalidateSpy).toHaveBeenCalledTimes(1);
|
||||
expect(invalidateSpy).toHaveBeenCalledWith('stack-statuses:7');
|
||||
});
|
||||
|
||||
it('does not broadcast stack state-invalidate for Sencho self-container events', async () => {
|
||||
@@ -1368,6 +1384,7 @@ describe('DockerEventService - state-invalidate broadcasts', () => {
|
||||
await vi.advanceTimersByTimeAsync(1);
|
||||
|
||||
expect(mockBroadcastEvent).not.toHaveBeenCalled();
|
||||
expect(invalidateSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('broadcasts state-invalidate on health_status:unhealthy', async () => {
|
||||
@@ -1386,6 +1403,7 @@ describe('DockerEventService - state-invalidate broadcasts', () => {
|
||||
(c[0] as { type?: string }).type === 'state-invalidate');
|
||||
expect(states.length).toBeGreaterThan(0);
|
||||
expect(states[0][0]).toMatchObject({ action: 'health_status', stackName: 'api' });
|
||||
expect(invalidateSpy).toHaveBeenCalledWith('stack-statuses:1');
|
||||
});
|
||||
|
||||
it('does not broadcast state-invalidate on non-state actions like exec_create', async () => {
|
||||
@@ -1401,6 +1419,7 @@ describe('DockerEventService - state-invalidate broadcasts', () => {
|
||||
await vi.advanceTimersByTimeAsync(1);
|
||||
|
||||
expect(mockBroadcastEvent).not.toHaveBeenCalled();
|
||||
expect(invalidateSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user