mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-21 08:02:38 +00:00
fix(datatables): keep the fork schema baseline, and bounce streams on every removal
Three fixes from review. `edit_datatable_config` took `forked_from` wholesale from the stored entry, so the fork schema diff's save of an advanced baseline was silently discarded and an applied change was offered again. Whether an entry carries a clone stamp is still carried from the store, since that is what marks its database droppable, but the baseline inside it is now taken from the request. The stranded-pointer warning and the stream bounce ran over the optional `deleted_datatables` hint, which the settings-sync CLI never sends, so removing a governing data table through `wmill` bounced nothing. Removals are now derived from the stored configuration against the saved one. `delete_workspace` read the pointers to bounce before its transaction, so a fork committing a pointer during the deletion was missed. The read now happens inside the transaction, after the workspace row is deleted: a fork's insert key-share locks that row through its parent foreign key, so it is either seen or fails on the missing parent. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
92c723f735
commit
b68b97f5ea
@@ -995,21 +995,6 @@ pub(crate) async fn delete_workspace(
|
||||
// but the destructive cleanup itself runs only after the commit below: a delete that
|
||||
// fails mid-way must never leave a live workspace with its fork data destroyed and no
|
||||
// registry row to retry from. Read-only: nothing is dropped here.
|
||||
// Read before the delete: another workspace's data table entry can point at one of this
|
||||
// workspace's, and deleting the workspace it names leaves that pointer resolving to nothing.
|
||||
// Nothing sweeps them — turning them back into copies would hand each fork the database
|
||||
// outright — so the deleter is told which data tables they just stranded.
|
||||
let stranded_pointers = sqlx::query!(
|
||||
r#"SELECT ws.workspace_id AS "workspace_id!", dt.key AS "datatable!"
|
||||
FROM workspace_settings ws
|
||||
CROSS JOIN LATERAL jsonb_each(COALESCE(ws.datatable->'datatables', '{}'::jsonb)) dt
|
||||
WHERE dt.value->'reference'->>'workspace_id' = $1
|
||||
ORDER BY ws.workspace_id, dt.key"#,
|
||||
&w_id,
|
||||
)
|
||||
.fetch_all(&db)
|
||||
.await
|
||||
.unwrap_or_default();
|
||||
|
||||
let fork_ducklake_cleanups = prepare_fork_ducklake_cleanups(&db, &w_id, None)
|
||||
.await
|
||||
@@ -1265,6 +1250,22 @@ pub(crate) async fn delete_workspace(
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
// Who points here, read after the delete above and inside this transaction. A fork writes its
|
||||
// pointer in the transaction that inserts it, and that insert key-share locks this row through
|
||||
// the parent foreign key: so the fork either committed before the delete and is seen here, or
|
||||
// waits on it and then fails on the missing parent. No pointer can escape this list. Nothing
|
||||
// sweeps them afterwards — turning them back into copies would hand each fork the database
|
||||
// outright — so the deleter is told which data tables they stranded.
|
||||
let stranded_pointers = sqlx::query!(
|
||||
r#"SELECT ws.workspace_id AS "workspace_id!", dt.key AS "datatable!"
|
||||
FROM workspace_settings ws
|
||||
CROSS JOIN LATERAL jsonb_each(COALESCE(ws.datatable->'datatables', '{}'::jsonb)) dt
|
||||
WHERE dt.value->'reference'->>'workspace_id' = $1
|
||||
ORDER BY ws.workspace_id, dt.key"#,
|
||||
&w_id,
|
||||
)
|
||||
.fetch_all(&mut *tx)
|
||||
.await?;
|
||||
// Each fork pointing here keeps the replication stream it opened while this workspace
|
||||
// governed it. Bounced in this transaction, so a listener that reconnects finds its pointer
|
||||
// dangling instead of streaming on.
|
||||
|
||||
Reference in New Issue
Block a user