Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions crates/storage-sqlite/src/backup.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,8 +15,8 @@ use extenddb_core::types::{
PointInTimeRecoveryDescription, ProvisionedThroughput, SourceTableDetails, TableDescription,
TableKeyInfo,
};
use extenddb_storage::BackupEngine;
use extenddb_storage::error::StorageError;
use extenddb_storage::{BackupEngine, TableEngine};
use futures::future::BoxFuture;

use crate::data::{data_table_name, upsert_item_in_tx};
Expand DownExpand Up@@ -355,7 +355,13 @@ impl BackupEngine for SqliteEngine {
..Default::default()
};

let desc = self.create_table(&account_id, create_input).await?;
// `defer_active`: the target is written CREATING with no scheduled
// transition, so the control-plane worker cannot report it ACTIVE
// while the copy below is still running. The explicit ACTIVE update
// after the copy commits is the only flip.
let desc = self
.create_table_impl(&account_id, create_input, true)
.await?;
let key_info = TableKeyInfo {
table_name: target_table_name.clone(),
account_id: account_id.clone(),
Expand Down
19 changes: 18 additions & 1 deletion crates/storage-sqlite/src/create_table.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,6 +8,9 @@
//! transaction afterward, with catalog cleanup if the data DDL fails. The
//! control-plane delay (`control_plane_delay_seconds`) decides whether the
//! table starts ACTIVE (delay 0) or CREATING with a scheduled transition.
//!
//! The restore path is the exception: see `defer_active` on
//! [`SqliteEngine::create_table_impl`].

use extenddb_core::types::{
BillingMode, BillingModeSummary, CreateTableInput, GsiDescription, LsiDescription,
Expand All@@ -20,10 +23,17 @@ use crate::sqlite_util::{format_timestamp, is_unique_violation};
use crate::store::SqliteEngine;

impl SqliteEngine {
/// Create a table. When `defer_active` is set (the restore path), the row
/// is written `CREATING` with **no** scheduled transition, so the
/// background control-plane worker cannot flip it to `ACTIVE` while the
/// caller is still populating it; the caller sets `ACTIVE` itself once the
/// data copy completes. Normal `CreateTable` passes `false` and gets the
/// usual timed transition.
pub(crate) async fn create_table_impl(
&self,
account_id: &str,
input: CreateTableInput,
defer_active: bool,
) -> Result<TableDescription, StorageError> {
Self::validate_account_id(account_id)?;
let table_id = uuid::Uuid::new_v4().to_string();
Expand DownExpand Up@@ -76,7 +86,14 @@ impl SqliteEngine {
let creation_ts = format_timestamp(now);
#[allow(clippy::cast_precision_loss)]
let creation_epoch = now.unix_timestamp() as f64;
let (initial_status, status_transition_at) = if delay_secs <= 0.0 {
let (initial_status, status_transition_at) = if defer_active {
// CREATING with no scheduled transition: the control-plane worker
// matches only rows with a non-NULL, matured `status_transition_at`
// (see `worker.rs`), so it can never flip this row mid-copy. The
// restore caller performs the single ACTIVE flip when the copy
// commits.
("CREATING", None)
} else if delay_secs <= 0.0 {
("ACTIVE", None)
} else {
(
Expand Down
2 changes: 1 addition & 1 deletion crates/storage-sqlite/src/table_engine.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -20,7 +20,7 @@ impl TableEngine for SqliteEngine {
input: CreateTableInput,
) -> BoxFuture<'_, Result<TableDescription, StorageError>> {
let account_id = account_id.to_owned();
Box::pin(async move { self.create_table_impl(&account_id, input).await })
Box::pin(async move { self.create_table_impl(&account_id, input, false).await })
}

fn delete_table(
Expand Down
10 changes: 5 additions & 5 deletions crates/storage-sqlite/src/update_table.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -1191,7 +1191,7 @@ mod reconciler_tests {
}))
.expect("input");
engine
.create_table_impl(account, input)
.create_table_impl(account, input, false)
.await
.expect("create table");

Expand DownExpand Up@@ -1271,7 +1271,7 @@ mod reconciler_tests {
}))
.expect("input");
engine
.create_table_impl(account, input)
.create_table_impl(account, input, false)
.await
.expect("create table");

Expand DownExpand Up@@ -1386,7 +1386,7 @@ mod reconciler_tests {
}))
.expect("input");
engine
.create_table_impl("000000000000", input)
.create_table_impl("000000000000", input, false)
.await
.expect("create table");
let (table_id,): (String,) =
Expand DownExpand Up@@ -1511,7 +1511,7 @@ mod reconciler_tests {
}))
.expect("input");
engine
.create_table_impl("000000000000", input)
.create_table_impl("000000000000", input, false)
.await
.expect("create table");
let (table_id,): (String,) =
Expand DownExpand Up@@ -1598,7 +1598,7 @@ mod reconciler_tests {
}))
.expect("input");
engine
.create_table_impl("000000000000", input)
.create_table_impl("000000000000", input, false)
.await
.expect("create table");
let (table_id,): (String,) =
Expand Down
2 changes: 1 addition & 1 deletion crates/storage-sqlite/src/vector_bench.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -118,7 +118,7 @@ mod tests {
}))
.expect("input");
engine
.create_table_impl(account, input)
.create_table_impl(account, input, false)
.await
.expect("create table");

Expand Down
2 changes: 1 addition & 1 deletion crates/storage-sqlite/src/workers.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -855,7 +855,7 @@ mod vector_propagation_tests {
}))
.expect("input");
engine
.create_table_impl("000000000000", input)
.create_table_impl("000000000000", input, false)
.await
.expect("create table");
let (table_id,): (String,) =
Expand Down
Loading