Files
support_backend/specs/016-load-concurrency-testing/plan.md
T
saqib mirandClaude Sonnet 5 015ef62b71 plan(016-load-concurrency-testing): design assignment/SLA/escalation race fixes
research.md nails down the exact mechanism for each real race the audit
found: a partial unique index for assignment double-assignment, a
Ticket-style version counter for SLA pause/resume/sweep, and a partial
unique index for escalation-rule idempotency — each traced to the specific
repository/service code that has the gap today. data-model.md and plan.md
carry the resulting schema and repository-contract changes; quickstart.md
defines the real-infra verification steps for each user story.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-09 16:55:32 +05:30

7.9 KiB

Implementation Plan: Load and Concurrency Testing

Branch: 016-load-concurrency-testing | Date: 2026-09-09 | Spec: spec.md

Input: Feature specification from /specs/016-load-concurrency-testing/spec.md

Summary

Prove — with real, genuinely-concurrent requests against real Postgres/Redis, never mocked timers — three concurrency guarantees that a prior codebase audit found are NOT currently held (assignment double-assignment, SLA pause/resume/sweep races, escalation duplicate-event risk), fix each real race the tests reveal with a minimal, idiomatic DB-level guard consistent with this codebase's existing patterns, add one new concurrency test proving the existing ticket optimistic-concurrency guarantee holds under genuine concurrency, and add repeatable autocannon-based HTTP load-test tooling for the three named critical endpoint groups.

Technical Context

Language/Version: TypeScript 5.4 / Node.js >=20

Primary Dependencies: Fastify 4.26, Prisma, ioredis/BullMQ, Vitest (existing stack — no new runtime dependency for the concurrency tests); autocannon added as a new devDependency for the load-test tooling (pure npm package, no external binary, scriptable in TS, matches this project's existing Node-native toolchain rather than introducing a separate Go binary like k6)

Storage: PostgreSQL via Prisma (existing Assignment, SLARun, EscalationEvent models — one additive schema change per race fix, see data-model.md), Redis (existing, unchanged)

Testing: Vitest, run against the existing throwaway Docker Postgres/Redis (supporthub-test-pg/supporthub-test-redis) already used by tests/concurrency/; load tests run with autocannon against a real running instance of the dev server

Target Platform: Linux/Windows server (existing deployment target, unchanged)

Project Type: Backend service (existing modular monolith, unchanged)

Performance Goals: NEEDS CLARIFICATION resolved in research.md — no business-specified throughput/latency targets exist yet; FR-009 requires these be marked OPEN BUSINESS DECISION rather than invented, so this feature ships tooling + a baseline report, not a numeric SLA

Constraints: Every fix must be additive/backward-compatible (no breaking change to existing Assignment/SLARun/EscalationEvent consumers — 012-admin-list-views and 015-reporting-dashboards both already query these tables); every concurrency claim must be proven against real Docker-provisioned infrastructure per this project's standing verification discipline, never asserted from code review alone

Scale/Scope: 3 real races to prove-and-fix (assignment, SLA, escalation), 1 race to prove already-safe (ticket status), 3 endpoint groups to load-test (ticket creation, AI support flow, admin reporting) — entirely within supporthub-api, no supporthub-web changes

Constitution Check

GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.

Principle Check Status
I. SaaS Is Sole Identity Authority N/A — no identity/tenant/product-access logic touched PASS
II. Configuration Over Hardcoding Load-test pass/fail thresholds are NOT hardcoded — explicitly marked OPEN BUSINESS DECISION per FR-009, matching roadmap convention PASS
III. Layered Architecture / Module Boundaries All three fixes stay inside their owning module (orchestration/assignments, orchestration/sla, orchestration/escalation) — repository-layer changes only, no new cross-module imports PASS
IV. AI Recommends, Policy Decides N/A — no AI/tool-permission logic touched PASS
V. Evidence-Based Verification This entire feature IS evidence-based verification — every claimed guarantee must be proven by a real concurrency test against real infra before being considered fixed PASS (this principle is the feature's own thesis)
VI. Durable Audit & History No audit-log shape changes; EscalationEvent's idempotency fix preserves the existing audit row for the winning attempt, silently no-ops the loser rather than deleting anything PASS
VII. Concurrency-Safe, Durable Job Handling (NON-NEGOTIABLE) This feature directly implements this principle's own stated requirement ("Assignment and escalation logic MUST be tested under concurrency... job handlers MUST be idempotent") — it is the principle's own overdue test coverage PASS — this feature exists to close this exact gap
VIII. Ticket/Problem Separation N/A — no Ticket/Problem model changes PASS

No violations. No Complexity Tracking entries needed.

Project Structure

Documentation (this feature)

specs/016-load-concurrency-testing/
├── plan.md              # This file
├── research.md          # Phase 0 output
├── data-model.md         # Phase 1 output
├── quickstart.md        # Phase 1 output
└── tasks.md             # Phase 2 output (/speckit-tasks — not yet created)

No contracts/ directory: this feature adds no new HTTP endpoints or request/response contracts — it hardens existing internal behavior and adds test/tooling infrastructure only.

Source Code (repository root)

prisma/
└── schema.prisma                                   # +1 field (SLARun.version), +2 raw partial
                                                      # unique indexes (migration SQL)

src/modules/orchestration/assignments/
├── repository/assignment.repository.ts              # createAssignment: catch+retry on the new
                                                       # partial-unique-index conflict
└── ...                                               # (engine/service unchanged)

src/modules/orchestration/sla/
├── repository/sla-run.repository.ts                 # update() becomes version-checked; add
│                                                       updateWithVersion(id, expectedVersion, data)
└── service/sla.service.ts                            # pause/resume/complete: read-modify-retry
                                                        loop on version conflict (bounded attempts)

src/modules/orchestration/escalation/
├── repository/escalation-event.repository.ts        # create(): catch the new partial-unique
│                                                       -index conflict, return existing row
└── service/escalation.service.ts                     # fire(): treat a duplicate-conflict as a
                                                        no-op, not an error

tests/concurrency/
├── round-robin.test.ts                               # existing — untouched
├── queue.test.ts                                     # existing — untouched
├── assignment-race.test.ts                           # NEW — User Story 1 / FR-001
├── sla-race.test.ts                                   # NEW — User Story 2 / FR-002
├── escalation-idempotency.test.ts                     # NEW — User Story 3 / FR-003
└── ticket-status-race.test.ts                         # NEW — User Story 4 / FR-004

tests/load/
├── autocannon.config.ts                               # NEW — shared runner + report shape
├── ticket-creation.load.ts                            # NEW — User Story 5 / FR-007, FR-008
├── ai-support-flow.load.ts                             # NEW
└── admin-reporting.load.ts                             # NEW

Structure Decision: Single backend project (existing supporthub-api modular monolith). Fixes live inside their owning module's existing repository/service files (Principle III); new tests live in the existing tests/concurrency/ directory (already established by round-robin.test.ts) plus a new tests/load/ directory for the load-test tooling, mirroring the existing tests/{unit,integration,e2e,concurrency} layout with one new sibling rather than overloading tests/concurrency/ with non-correctness-proving load scripts.

Complexity Tracking

No violations — table omitted.