mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-21 08:02:38 +00:00
fix(datatables): let a retried clone reclaim its own leftover database
A clone creates its target database one request before it copies into it, and the fork that would name it is written a request after that. Any failure in between — a pg_dump error, a bad restore, a dropped connection, the source's roles changing mid-flow — left a registered `wm_fork_*` that no entry names, and every retry then failed on its name. This predates data table roles. `create_pg_database` now reclaims such a leftover before creating: only a `wm_fork_*` database Windmill registered as a data table database and that no data table or ducklake entry names, in any workspace, archived ones included. The drop never terminates connections, so a clone still copying into it makes the reclaim fail instead of being cut off. It is limited to callers who administer the source — reaching it is not enough, since on a data table without roles every member reaches it — and anyone else gets the refusal an existing database always got. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
432ccc0fbd
commit
29fccb0978
@@ -3312,10 +3312,12 @@ async fn create_pg_database(
|
||||
// The copy this database is for is refused a call later, and nothing collects an instance
|
||||
// database that no data table entry names. Refuse here too, so the clone stops before one
|
||||
// exists rather than leaving an empty registered `wm_fork_…` behind.
|
||||
if let Some(reference) = req.source.strip_prefix("datatable://") {
|
||||
let name = datatable_ref_name(reference);
|
||||
ensure_datatable_is_clonable(&db, &w_id, name).await?;
|
||||
}
|
||||
let governing = match req.source.strip_prefix("datatable://") {
|
||||
Some(reference) => {
|
||||
Some(ensure_datatable_is_clonable(&db, &w_id, datatable_ref_name(reference)).await?)
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
|
||||
// Non-superadmin: restrict dbname to wm_fork_ prefix
|
||||
if !windmill_api_auth::is_super_admin_authed(&db, &authed).await? {
|
||||
@@ -3328,6 +3330,21 @@ async fn create_pg_database(
|
||||
}
|
||||
|
||||
if is_instance_datatable_source(&db, &w_id, &req.source).await? {
|
||||
// A retry after a clone that failed past this point finds its own leftover here. Reclaiming
|
||||
// it is a `DROP DATABASE`, so it is for whoever administers the source — not merely whoever
|
||||
// reaches it, which on a data table without roles is every member. Anyone else gets the
|
||||
// refusal an existing database always got.
|
||||
let may_reclaim = match &governing {
|
||||
Some(governing) => crate::datatable_permissions::ensure_governs_datatable(
|
||||
&db, &authed, &w_id, governing,
|
||||
)
|
||||
.await
|
||||
.is_ok(),
|
||||
None => false,
|
||||
};
|
||||
if may_reclaim {
|
||||
windmill_common::reclaim_orphaned_fork_database(&db, &req.target_dbname).await?;
|
||||
}
|
||||
windmill_common::create_custom_instance_database(&db, &req.target_dbname, "datatable")
|
||||
.await?;
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user