fix(dashboard): cache org stats in Redis with versioned invalidation (#883)
* fix(dashboard): cache org stats in Redis with versioned invalidation Replace the never-expiring process-local cache in the org dashboard page load with API-side Redis caching keyed per org. A version stamp prevents slow reads from writing pre-purge data back into the cache. Invalidate on course create/update/delete, student enroll/remove, certificate claim, and progress reset. Remove the dashboard SSR module cache so every page load reads through the API cache layer. Fixes #852 Co-authored-by: Rotimi Best <rotimi-best@users.noreply.github.com> * fix(dashboard): address PR review on stats cache invalidation - Validate bust via ZDashStats instead of raw query access - Use import type for OrganisationAnalytics - Resolve orgId before course delete so invalidation is reliable - Invalidate immediately after persisted writes, before fallible side effects - Invalidate on member role changes and program course access enrollment Co-authored-by: Rotimi Best <rotimi-best@users.noreply.github.com> * fix(dashboard): complete stats cache invalidation coverage Wire org stats cache purge on lesson completion (manual toggle and video-watch auto-complete) so topCourses completion percentages stay fresh. Harden bulk member adds with sequential inserts and a finally invalidation boundary, and only purge org invite enrollments when new group memberships were actually inserted. Co-authored-by: Rotimi Best <rotimi-best@users.noreply.github.com> * fix(core): make lesson completion stats purge best-effort Wrap org stats cache invalidation in try/catch so a lookup or Redis failure cannot turn a successful lesson completion into a 500. Co-authored-by: Rotimi Best <rotimi-best@users.noreply.github.com> * fix(dashboard): keep org stats cache through lesson completion Lesson complete is a high-frequency event and usually does not change the rounded dashboard percentages. Rely on a 10 minute TTL instead of purging the snapshot on every completion. Co-authored-by: Rotimi Best <rotimi-best@users.noreply.github.com> * docs: explain how admin dashboard numbers are calculated Add a user-facing guide covering headline counts, top courses, certificates, login activity, and when the 10 minute snapshot refreshes. Co-authored-by: Rotimi Best <rotimi-best@users.noreply.github.com> ---------
This commit is contained in:
co-authored by
Rotimi Best
parent
9fb75e11d2
commit
2ebb544a81
@@ -2,9 +2,6 @@ import type { DashStatsSuccess, LoginActivityData, LoginActivitySuccess } from '
|
||||
import { classroomio, getApiHeaders } from '$lib/utils/services/api';
|
||||
import { type ServerApiResult, safeServerApi } from '$lib/utils/services/api/server';
|
||||
|
||||
// TODO - Replace with actual cache
|
||||
const cache: Record<string, DashStatsSuccess['data'] | null> = {};
|
||||
|
||||
function loginActivityDataFromSettled(
|
||||
result: PromiseSettledResult<ServerApiResult<LoginActivitySuccess>>
|
||||
): LoginActivityData {
|
||||
@@ -26,18 +23,6 @@ export const load = async ({ params, parent, cookies }) => {
|
||||
};
|
||||
}
|
||||
|
||||
if (orgId in cache) {
|
||||
const loginActivityResult = await safeServerApi<LoginActivitySuccess>(() =>
|
||||
classroomio.dash['login-activity'].$get({ query: { orgId } }, getApiHeaders(cookies, orgId))
|
||||
);
|
||||
|
||||
return {
|
||||
orgName: siteName,
|
||||
stats: cache[orgId],
|
||||
loginActivity: loginActivityResult.ok ? loginActivityResult.body.data : []
|
||||
};
|
||||
}
|
||||
|
||||
const [statsResult, loginActivityResult] = await Promise.allSettled([
|
||||
safeServerApi<DashStatsSuccess>(() =>
|
||||
classroomio.dash.stats.$get({ query: { siteName } }, getApiHeaders(cookies, orgId))
|
||||
@@ -47,11 +32,11 @@ export const load = async ({ params, parent, cookies }) => {
|
||||
)
|
||||
]);
|
||||
|
||||
cache[orgId] = statsResult.status === 'fulfilled' && statsResult.value.ok ? statsResult.value.body.data : null;
|
||||
const stats = statsResult.status === 'fulfilled' && statsResult.value.ok ? statsResult.value.body.data : null;
|
||||
|
||||
return {
|
||||
orgName: siteName,
|
||||
stats: cache[orgId],
|
||||
stats,
|
||||
loginActivity: loginActivityDataFromSettled(loginActivityResult)
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user