feat(datastore): refuse deleting a container virtual upstream part 9/15 (S17 Phase 6 plan: 42/42)

Why

ADR-009 requires a 409 under either destructive value for any repository a virtual repository lists as an upstream. Parts 1 to 8 gave the container family a junction table with a registered writer, so that state is now reachable.

Without this arm the delete falls through to the foreign key, and mapRepositoryDeleteError collapses every 23503 to ErrRepositoryNotEmpty, the sentinel a destructive delete tombstones past.

The refusal's catalog scenarios, e2e.docker.upstreams.upstream-delete-refused and e2e.oci.upstreams.upstream-delete-refused, need an association that only the part 11 routes can create through the API, so they land in part 14, test(managementapi): cover the container upstre... (!2859 - merged) • Hayley Swimelar • 19.5.

No user doc changes here. docs/user/troubleshooting.md names only the Maven and npm refusal, which stays true while no route writes a container association, and part 11 makes the user-doc update with those routes, feat(managementapi): container virtual upstream... (!2856 - merged) • Hayley Swimelar • 19.5.

The new read's plan

containerVirtualUpstreamReferenceStmt on Postgres 17.10, after ANALYZE, over one namespace holding 2,000 junction rows. The scan reads one partition, container_virtual_repository_upstreams_p21, through container_virtual_repository_namespace_id_upstream_reposi_idx21, which is that partition's copy of index_cvru_on_ns_id_upstream_repository_id.

Limit  (cost=0.28..8.30 rows=1 width=16) (actual time=0.009..0.010 rows=1 loops=1)
  Buffers: shared hit=3
  ->  Index Scan using container_virtual_repository_namespace_id_upstream_reposi_idx21 on container_virtual_repository_upstreams_p21 container_virtual_repository_upstreams  (cost=0.28..8.30 rows=1 width=16) (actual time=0.009..0.009 rows=1 loops=1)
        Index Cond: ((namespace_id = '01a0d891-ee4a-7273-8a27-2526e400b3f7'::uuid) AND (upstream_repository_id = '01a0d891-f11a-702e-8948-e717687540f4'::uuid))
        Buffers: shared hit=3
Planning:
  Buffers: shared hit=338
Planning Time: 1.050 ms
Execution Time: 0.023 ms

The stack

Fifteen parts, each stacked on the last, each green on its own, reviewed in this order. Each part stays under 700 changed lines, counted as insertions plus deletions against the part it targets.

Part MR What
1 feat(datastore): container virtual upstream sto... (!2778 - merged) • Hayley Swimelar • 19.5 store types and the list read
2 chore(datastore): add the container upstream po... (!2839 - merged) • Hayley Swimelar • 19.5 the position helpers
3 chore(datastore): add the container upstream wr... (!2841 - merged) • Hayley Swimelar • 19.5 the write statements
4 feat(datastore): the container upstream write e... (!2843 - merged) • Hayley Swimelar • 19.5 the write envelope and associate
5 feat(datastore): container upstream reposition ... (!2844 - merged) • Hayley Swimelar • 19.5 reposition and dissociate
6 test(datastore): cover the container upstream a... (!2846 - merged) • Hayley Swimelar • 19.5 associate against Postgres
7 test(datastore): cover the container upstream m... (!2852 - merged) • Hayley Swimelar • 19.5 the mutations against Postgres
8 test(datastore): cover the container upstream l... (!2853 - merged) • Hayley Swimelar • 19.5 the list read and repairs against Postgres
9 feat(datastore): refuse deleting a container vi... (!2854 - merged) • Hayley Swimelar • 19.5 the repository delete guard
10 Draft: chore(managementapi): empty part 10/15, ... (!2855 - closed) • Hayley Swimelar • 19.5 closed unmerged, its core landed with Step 41
11 feat(managementapi): container virtual upstream... (!2856 - merged) • Hayley Swimelar • 19.5 the association routes
12 test(managementapi): cover the container upstre... (!2857 - merged) • Hayley Swimelar • 19.5 handler reads and associate
13 test(managementapi): cover the container upstre... (!2858 - merged) • Hayley Swimelar • 19.5 handler refusals and update
14 test(managementapi): cover the container upstre... (!2859 - merged) • Hayley Swimelar • 19.5 handler dissociate and the catalogs
15 test(managementapi): the container upstream ass... (!2860 - merged) • Hayley Swimelar • 19.5 the integration walk

The delete guard in part 9 must be deployed before part 11's associate route serves traffic. Merge order alone does not close the window: within one rollout, a pod without the guard can answer 202 on destructive=true for an upstream that a new pod has just associated, leaving a tombstone the reaper refuses.

Related to #314 (closed)

Edited by Hayley Swimelar

Merge request reports

Loading
Loading