Skip to content

2026-07-17 — XP-141 master-data quick wins (PERF-7)

  • packages/service/src/subjects/subject-update.ts — the two level-override COUNTs (student + tutor price overrides, only on the has_levels: true→false guard path) now run via one Promise.all. SubjectHasLevelOverridesError + total unchanged.
  • packages/service/src/academic-years/activate-academic-year.ts — the max-1-ACTIVE guard read (existingActive) + the independent before fetch collapsed into one Promise.all before the UPDATE.
  • packages/service/src/enrollment/student-subject-level-set.tssubject + before fetches hoisted into one Promise.all; the level fetch stays sequential/conditional after subject.has_levels (dependent — must not parallelize). XP-28 archived-level guard untouched.
  • Promise.all (not raw scalar-subquery SQL) is the right rung: same round-trip win, fully type-safe, no fragile SQL. Only the timing of independent reads changed; evaluation order + thrown errors are byte-identical.
  • academic-year guard still evaluates FIRST (if (existingActive) throw sits immediately after the await), so the max-1-ACTIVE invariant + error ordering are preserved per domain-academic-year.md. Extra before read only happens on the guard-fail path; success path is one round-trip fewer.
  • Promise.all is safe inside a drizzle deps.db.transaction: the tx reserves one postgres-js connection and postgres-js pipelines concurrent queries on it — no “another query in progress” error. Common assumption is you must keep tx reads sequential; you don’t, unless one read depends on another’s result. Promoted to service-conventions.md Query-shape section.

.claude/rules/service-conventions.md — Query-shape section gained the “Promise.all is safe inside a tx (postgres-js pipelining)” note (XP-141).