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:
Anso
2026-08-09 23:13:59 -04:00
committed by GitHub
parent 9798700401
commit 55ca82abb2
10 changed files with 362 additions and 96 deletions
+14
View File
@@ -226,6 +226,13 @@ stackGitSourceRouter.put('/:stackName/git-source', async (req: Request, res: Res
autoDeployOnApply,
});
// The cached /stacks/statuses payload carries the source label; drop it
// before responding so a client refetch on this response recomputes. The
// full invalidateNodeCaches helper is deliberate here (matching every
// other mutation route): link/unlink is a low-frequency user action, so
// dropping the project-name map and file-root allowlists alongside is
// harmless, unlike the high-frequency container-event path.
invalidateNodeCaches(req.nodeId);
console.log(`[GitSource] Configured git source for ${stackName}`);
res.json(source);
} catch (error) {
@@ -254,6 +261,13 @@ stackGitSourceRouter.delete('/:stackName/git-source', async (req: Request, res:
return;
}
GitSourceService.getInstance().delete(stackName);
// The cached /stacks/statuses payload carries the source label; drop it
// before responding so a client refetch on this response recomputes. The
// full invalidateNodeCaches helper is deliberate here (matching every
// other mutation route): link/unlink is a low-frequency user action, so
// dropping the project-name map and file-root allowlists alongside is
// harmless, unlike the high-frequency container-event path.
invalidateNodeCaches(req.nodeId);
console.log(`[GitSource] Removed git source for ${stackName}`);
res.json({ success: true });
} catch (error) {