Concurrent-edit protection on app deployment page (optimistic locking) #147

Open
opened 2026-04-22 20:56:32 +02:00 by claude · 0 comments
Owner

Context

Surfaced during brainstorming of the unified app deployment page (spec: docs/superpowers/specs/2026-04-22-app-deployment-page-design.md).

The new deployment page lets multiple users (OPERATOR/ADMIN) stage JAR + monitoring/resources/variables/sensitive-keys changes, then hit Save → Redeploy. Two users on the same (app, environment) at once currently have no protection: last write wins, silently clobbering the other user's staged state.

This was deliberately deferred out of the v1 spec — acceptable risk for initial rollout — but should be addressed before this surface sees production use with more than one OPERATOR active.

Proposal

Optimistic locking via If-Match / ETag using app.updated_at (or a dedicated config_version counter):

  • GET /api/v1/environments/{env}/apps/{slug} returns ETag: "<updated_at_millis>".
  • PUT /api/v1/environments/{env}/apps/{slug}/config?environment=... (staged writes) requires If-Match: "<updated_at_millis>".
  • Mismatch → 412 Precondition Failed with a body describing whose write won.
  • UI on 412: show a dialog offering "Reload and discard my changes" vs "Inspect differences" (future-facing — v1 could be just a toast + reload).

Container config writes (PUT /apps/{slug}/container-config) and JAR uploads (POST /apps/{slug}/versions) should participate as well.

Not in scope

  • Real-time collaborative editing.
  • Presence awareness ("Alice is also viewing this app").

Acceptance criteria

  • Two browser sessions editing the same app's Monitoring tab. First Save succeeds. Second Save returns 412, and the UI surfaces a non-lossy recovery path.
  • The live-apply endpoints (Traces & Taps, Route Recording) are exempt — they're intentionally real-time.

References

  • Brainstorming edge case 6e in the linked spec.
  • Existing ApplicationConfigController for config writes.
  • Existing app.updatedAt column already used for dirty-state detection in the current UI.
## Context Surfaced during brainstorming of the unified app deployment page (spec: `docs/superpowers/specs/2026-04-22-app-deployment-page-design.md`). The new deployment page lets multiple users (OPERATOR/ADMIN) stage JAR + monitoring/resources/variables/sensitive-keys changes, then hit Save → Redeploy. Two users on the same `(app, environment)` at once currently have no protection: last write wins, silently clobbering the other user's staged state. This was deliberately deferred out of the v1 spec — acceptable risk for initial rollout — but should be addressed before this surface sees production use with more than one OPERATOR active. ## Proposal Optimistic locking via `If-Match` / `ETag` using `app.updated_at` (or a dedicated `config_version` counter): - `GET /api/v1/environments/{env}/apps/{slug}` returns `ETag: "<updated_at_millis>"`. - `PUT /api/v1/environments/{env}/apps/{slug}/config?environment=...` (staged writes) requires `If-Match: "<updated_at_millis>"`. - Mismatch → `412 Precondition Failed` with a body describing whose write won. - UI on 412: show a dialog offering "Reload and discard my changes" vs "Inspect differences" (future-facing — v1 could be just a toast + reload). Container config writes (`PUT /apps/{slug}/container-config`) and JAR uploads (`POST /apps/{slug}/versions`) should participate as well. ## Not in scope - Real-time collaborative editing. - Presence awareness ("Alice is also viewing this app"). ## Acceptance criteria - Two browser sessions editing the same app's Monitoring tab. First Save succeeds. Second Save returns 412, and the UI surfaces a non-lossy recovery path. - The live-apply endpoints (Traces & Taps, Route Recording) are exempt — they're intentionally real-time. ## References - Brainstorming edge case 6e in the linked spec. - Existing `ApplicationConfigController` for config writes. - Existing `app.updatedAt` column already used for dirty-state detection in the current UI.
Sign in to join this conversation.