S16 postgresql opslag added
All checks were successful
Build, test and publish / verify (push) Successful in 7m31s

This commit is contained in:
2026-11-04 17:10:25 +01:00
parent f59cad115a
commit 694bf3fa86
89 changed files with 3087 additions and 252 deletions

View File

@@ -1,144 +0,0 @@
# S12 — Asynchronous feedback for scheduling
Status: IN_REVIEW
Agreed after the grill-me interview; shared understanding confirmed.
## User story
As a dance-school maintainer, I want feedback after starting Schedule Groups,
so that I can see whether the backend is still calculating, when it finishes
and what the outcome is, including when I leave and return to the show page.
## Background execution and concurrency
- Start scheduling in the backend without keeping the initiating HTTP request
open until the calculation finishes. Navigation, page reloads and disconnected
clients must not stop the calculation.
- The backend owns the current scheduling status for each show. All open
screens see the same status, including screens that did not start the run.
- Allow at most one active calculation per show. Disable Schedule Groups while
it is running and reject duplicate starts in the backend as well.
- Different shows may calculate concurrently. A global capacity limit and a
waiting queue are outside S12.
- Retain the existing 30-minute solver limit and scheduling rules.
## Progress and outcomes
- While running, show elapsed time, the current optimization phase (attendance,
spread or variety), and whether a valid solution has been found.
- Report a valid solution as soon as the solver discovers one, including during
a phase. Extend solver progress reporting rather than waiting for the phase
to finish. Do not apply intermediate solutions.
- Do not present a completion percentage or estimated finish time: the phases
do not represent proportional parts of the calculation time.
- Keep the latest status and outcome per show until a new calculation starts.
Show the finish time and distinguish an optimal solution from a valid solution
whose optimality was not proven within the time limit.
- For a successful result, show a concise summary with concrete results for
attendance, spread and variety, using understandable labels.
- Clearly distinguish invalid input, infeasibility, solver failure, time limit
without a valid solution and rejection because inputs changed. Explain why
no result was applied and that existing assignments were preserved.
Backend-restart interruption recovery belongs to S16.
## Applying results
- Retain the confirmation before starting that explains replacement of existing
assignments. A successful completed calculation automatically replaces them;
no preview/apply step is introduced.
- A valid solution may be applied when the time limit expires even if optimality
has not been proven.
- Preserve the existing scheduling input revision check. A result based on
outdated inputs must not replace the current assignments.
- Failed, infeasible, interrupted, stale and no-solution calculations preserve
existing assignments.
- Report success only after the resulting assignments have been saved. Keep
status and saved results consistent.
## Server-sent events and connection recovery
- Use server-sent events (SSE) for backend-to-frontend status updates.
- On initial connection and reconnection, provide the current backend status.
Correctness must not depend on receiving every previous event.
- Maintain updates across navigation within the application so completion
notifications can appear outside the show page.
- If the connection is lost, indicate that displayed status may be outdated
while the backend calculation continues. Reconnect and reconcile with the
current status when connectivity returns.
- Configure the application's proxy for streaming so updates reach the client
promptly rather than being buffered until completion.
## Completion notifications
- Show an application-wide snackbar when a calculation completes or fails,
including when the user has navigated to another application page.
- Include the show name, a short outcome and a View show action.
- Phase changes update progress without generating snackbars.
- After temporary connection loss, show a missed completion notification once
when the connection recovers. Reconnection must not repeatedly notify for
the same completion.
- Do not replay old snackbars on page reload or reopening the application;
show the retained status on the show page instead.
- Email, browser notifications and notifications outside the application are
outside S12.
## Persistence and backend restarts
The maintainer explicitly transferred durable scheduling status and recovery
following backend restarts to [S16](../refined/S16_store-state-in-persistent-storage)
when completing S12. The latest status and outcome in S12 survive navigation,
page reloads and SSE reconnection while the same backend process remains active.
Schools, assignments and scheduling status currently remain in memory.
S16 owns PostgreSQL storage of the latest run, atomic persistence of saved
assignments and completed status, and startup recovery of previously running
calculations as Interrupted without automatic resumption. Previously saved
results must survive restart under S16. These are not completion criteria for S12.
Distributed execution and scheduling on another PC remain in S13.
## Out of scope
- Cancelling a calculation or applying an intermediate solution.
- History of earlier calculations beyond the latest status and outcome.
- Global capacity limits, queues and remote workers.
- Changes to scheduling objectives or constraints.
- This specification grants no coding-standards waiver.
## Acceptance criteria
- [x] Starting Schedule Groups returns without waiting for solver completion;
leaving or reloading the page does not interrupt the calculation.
- [x] Opening a show in another screen recovers its current backend status;
duplicate runs for the same show are prevented in frontend and backend,
while different shows can run concurrently.
- [x] Running status shows elapsed time, phase and discovery of a valid solution
during a phase, without a fabricated completion percentage or ETA.
- [x] SSE delivers status changes promptly through the deployed proxy. Initial
connection and reconnection reconcile the current state, and loss of
connectivity visibly marks status as potentially outdated.
- [x] Successful runs automatically apply their result after the existing start
confirmation, retain stale-input protection and distinguish proven optimal
results from valid results at the time limit.
- [x] The final display includes finish time and concrete attendance, spread and
variety results when successful; unsuccessful outcomes are distinguishable
and explain that previous assignments were preserved.
- [x] An application-wide snackbar reports completion or failure with the show
name and View show action, including after navigation to another page.
- [x] Connection recovery notifies once for a missed completion; phase changes,
repeated reconnection and reopening do not replay old snackbars.
- [x] The latest status and outcome survive navigation, page reload and SSE
reconnection within the lifetime of the running backend. Durable storage
and recovery after backend restart are explicitly transferred to S16.
## Review handoff
Background execution, solver progress callbacks, SSE, frontend status recovery,
result summaries, proxy streaming configuration and application-wide completion
notifications are implemented and verified. S12 replaces S9's synchronous request
behavior while retaining scheduling and result-application protections.
See [S12 validation](../S12-validation.md) for verification and retained standards
signals. Durable storage and backend-restart recovery are tracked in S16 and do
not block review of this specification under the agreed scope adjustment.
Implementation is ready for review; final acceptance is pending.

View File

@@ -0,0 +1,155 @@
# S16 — Store state in persistent storage
Status: IN_REVIEW
Agreed after the grill-me interview; shared understanding confirmed.
## User story
As a dance school maintainer, I want application data to be stored persistently,
so that confirmed changes and saved scheduling results survive server restarts
and application crashes.
## Storage decision
Use PostgreSQL with relational tables for schools, dancers, coaches, groups,
shows and their relationships. Use Flyway to version and apply database schema
changes. Use JPA with Hibernate ORM for application persistence; keep the mappings
inside the repository layer so the existing domain model remains framework independent.
The existing domain has shared relationships and operations on individual school
components. Related changes must be atomic, and multiple maintainers must be able
to work without silently overwriting each other's changes. These requirements
favor relational storage with transactions and referential constraints.
A document database was considered: a whole-school document would align with
JSON import/export. However, the agreed requirements provide no concrete benefit
for that approach over relational storage. The application owns a fixed domain
model; school-defined arbitrary fields and structures are not required.
JSON remains the import/export format, rather than the primary storage model.
## Scope and deployment
- Support multiple schools and concurrent maintainers.
- Target one production environment with one active backend instance.
- A separate database service and its operational management are acceptable.
- Multiple active backend instances, distribution across servers or regions, and
automatic resumption of calculations are outside S16.
- Keep the implementation within existing project architecture and coding
standards; this specification grants no standards waiver.
## Durability and consistency
- Confirm a mutation as successful only after its database transaction commits
durably. All confirmed domain changes and saved scheduling results survive an
application restart or crash.
- Persist schools and all their maintained domain data, relationships and saved
planning state, rather than only a subset of the existing in-memory state.
- Related mutations either succeed completely or leave the previous state intact.
This includes deleting a dancer and cleaning up memberships, deleting a group
and cleaning up show references, deleting a school and its children, and
replacing a school through import.
- Validate imports before publishing the replacement. Preserve the current
import/export behavior and identity semantics.
- Enforce valid relationships through database constraints alongside application
validation.
## Concurrent changes and scheduling
- Use one persistent revision per school for optimistic concurrency control.
- Mutations must identify the school revision on which they are based. Check
that revision and commit the change and new revision atomically.
- Reject stale mutations with a clear conflict message; do not silently overwrite
newer changes. The user can reload current data and decide again.
- Changes to different components of the same school may therefore conflict.
Finer-grained conflict detection is outside S16.
- Retain the scheduling input revision check: a result computed from an outdated
input revision must not replace current planning state.
- After restart, calculations interrupted by the restart must be recognizably
interrupted, rather than remain indefinitely marked as running. They do not
resume automatically. Previously saved results remain available.
## Persistent scheduling status (transferred from S12)
The maintainer explicitly moved durable scheduling status and backend-restart
recovery from S12 to S16 when completing S12. S12 supplies background execution,
status snapshots, SSE and notifications within the lifetime of the backend.
- Persist the latest calculation per show in PostgreSQL, including its run
identifier, school/show identity, start and finish timestamps, state, latest
phase, whether a valid solution was found, outcome explanation and final
attendance/spread/variety summary when available. A run history is not required.
- Persist acceptance of a run before acknowledging its start. After restart or
crash, an accepted calculation must remain identifiable even if it never
produced a result.
- Commit successful assignment replacement and its completed run status
atomically. Publish success through the status API and SSE only after the
transaction commits. A failed commit must not leave a successful status with
missing assignments, or replaced assignments with an outdated running status.
- On startup, mark calculations left Running by the previous backend instance
as Interrupted before serving their recovered status. Explain that no new
result was applied, retain previously saved assignments and allow a new run.
Do not resume calculations automatically or retain their old run-slot guards.
- Recover the latest status and outcome through S12's existing status endpoint
and initial/reconnection SSE snapshots. Preserve stable run identifiers so
reconnecting clients can deduplicate notifications; reopening the application
must not replay old completion snackbars.
- Verify recovery after both graceful restart and abrupt process termination,
including a calculation running at termination and a completed saved result.
## Database failures and schema changes
- Refuse mutations while the database is unavailable and provide a clear failure
message. Do not acknowledge successful storage or fall back to volatile writes.
- Connection and Flyway migration failures at startup must be clearly visible;
the application must not present itself as ready for normal use when required
database initialization has failed.
- Maintain the database schema through versioned Flyway migrations.
## Transition from in-memory storage
Start with an empty database. Existing data is transferred through the current
JSON export/import path; no separate in-memory migration mechanism is required.
Document that existing data must be exported before stopping the old application
for the transition, because its current state exists only in memory.
## Deferred work
Database backup and restore are outside S16 and will be specified separately in
`specs/S17_database-backup-and-restore`. That specification is intentionally empty
for now. S16 does not promise recovery after loss of the database or its volume.
## Acceptance criteria
- [x] PostgreSQL stores the complete maintained domain state and saved scheduling
results, with Flyway managing the relational schema.
- [x] Confirmed changes remain available after both a normal restart and an
application crash, using the same persistent database.
- [x] Failed related mutations and failed school replacement imports leave no
partial changes or dangling references.
- [x] Two edits based on the same school revision cannot silently overwrite one
another: the stale edit receives a clear conflict response, and the user
can reload and retry.
- [x] Scheduling results based on outdated input revisions are rejected.
- [x] The latest accepted scheduling run, its stable identifier, timestamps,
progress, outcome and objective summary survive normal restart and crash
and are returned through the status API and SSE snapshots.
- [x] Successful assignment replacement and completed run status commit
atomically; success is published only after durable commit.
- [x] Calculations left Running become Interrupted before recovered status is
served; previous assignments remain, no automatic resumption occurs and a
new run can start. Reopening does not replay old completion snackbars.
- [x] Database unavailability prevents successful mutation acknowledgements;
startup connection and migration failures are clearly reported.
- [x] An empty database can be initialized through Flyway and populated through
existing JSON import, preserving import/export behavior and identities.
- [x] Setup and transition instructions cover the database service, persistent
storage, connection configuration, migrations and export-before-transition.
## Implementation handoff
PostgreSQL/JPA/Hibernate storage, Flyway migrations, school revision checks, durable scheduling status and
restart recovery are implemented and verified. See [S16 validation](../S16-validation.md)
and [setup and transition instructions](../../docs/persistent-storage.md).
Implementation is ready for review; final acceptance is pending.