pub struct MvStore<Clock: LogicalClock, A: ConcurrentAllocator = TursoAllocator> {
pub rows: SkipMap<RowID, RowVersions<A>, BasicComparator, A>,
pub table_id_to_rootpage: SkipMap<MVTableId, RootEntry, BasicComparator, A>,
pub index_rows: SkipMap<MVTableId, IndexRowsMap<A>, BasicComparator, A>,
/* private fields */
}Expand description
A multi-version concurrency control database.
Fields§
§rows: SkipMap<RowID, RowVersions<A>, BasicComparator, A>§table_id_to_rootpage: SkipMap<MVTableId, RootEntry, BasicComparator, A>Table ID is an opaque identifier that is only meaningful to the MV store. Each checkpointed MVCC table corresponds to a single B-tree on the pager, which naturally has a root page. We cannot use root page as the MVCC table ID directly because:
- We assign table IDs during MVCC commit, but
- we commit pages to the pager only during checkpoint
which means the root page is not easily knowable ahead of time. Hence, we store the mapping here. The value is Option because tables created in an MVCC commit that have not been checkpointed yet have no real root page assigned yet.
Versioned root bindings; passive checkpoints update these at publish, not during collection.
index_rows: SkipMap<MVTableId, IndexRowsMap<A>, BasicComparator, A>Unlike table rows which are stored in a single map, we have a separate map for every index because operations like last() on an index are much easier when we don’t have to take the table identifier into account.
Implementations§
Source§impl<Clock: LogicalClock> MvStore<Clock>
impl<Clock: LogicalClock> MvStore<Clock>
Sourcepub fn new(
clock: Clock,
storage: Arc<dyn DurableStorage>,
experimental_mvcc_passive_checkpoint: bool,
) -> Result<Self>
pub fn new( clock: Clock, storage: Arc<dyn DurableStorage>, experimental_mvcc_passive_checkpoint: bool, ) -> Result<Self>
Creates a new database backed by the default TursoAllocator.
Source§impl<Clock: LogicalClock, A: ConcurrentAllocator> MvStore<Clock, A>
impl<Clock: LogicalClock, A: ConcurrentAllocator> MvStore<Clock, A>
Sourcepub const DEFAULT_GC_VERSION_THRESHOLD: i64
pub const DEFAULT_GC_VERSION_THRESHOLD: i64
Default mvcc_gc_threshold: run an incremental GC pass roughly every
this many newly inserted versions. Small enough that steady-state
memory stays bounded under heavy short-txn concurrency, large enough
that small workloads (and most unit tests) never trigger a pass.
Sourcepub const MAX_CHAINS_PER_GC: usize = 4096
pub const MAX_CHAINS_PER_GC: usize = 4096
Upper bound on table-row chains scanned by one inline gc_incremental
pass on the commit path. Keeps a pass cheap (sub-millisecond) so it
doesn’t noticeably slow the committing connection; steady state relies
on frequent passes resuming via gc_table_cursor.
Sourcepub fn new_in(
clock: Clock,
storage: Arc<dyn DurableStorage>,
alloc: A,
experimental_mvcc_passive_checkpoint: bool,
) -> Result<Self>
pub fn new_in( clock: Clock, storage: Arc<dyn DurableStorage>, alloc: A, experimental_mvcc_passive_checkpoint: bool, ) -> Result<Self>
Creates a new database whose skiplists allocate through alloc.
Sourcepub fn get_table_id_from_root_page(&self, root_page: i64) -> MVTableId
pub fn get_table_id_from_root_page(&self, root_page: i64) -> MVTableId
Get the table ID from the root page, resolving against the current (live) mapping.
Equivalent to get_table_id_from_root_page_at(root_page, u64::MAX).
Sourcepub fn get_table_id_from_root_page_at(
&self,
root_page: i64,
snapshot_ts: u64,
) -> MVTableId
pub fn get_table_id_from_root_page_at( &self, root_page: i64, snapshot_ts: u64, ) -> MVTableId
Get the table ID for root_page as seen by a transaction at snapshot_ts.
Negative root pages are non-checkpointed objects whose table ID equals the root page; they are never reused or versioned, so the snapshot is irrelevant.
For a positive (checkpointed) root page, a PASSIVE checkpoint may have dropped the
object — and possibly reused the page for a new btree — while this transaction still
references it at an older snapshot. Successive owners of a page hold disjoint,
back-to-back lifetimes; we return the owner whose binding has not yet ended at the
snapshot (smallest end > ts, live counting as +inf). We deliberately do NOT gate on
begin here: a transaction’s physical schema (root pages) can run ahead of its data
snapshot, because a checkpoint allocating a root page is not a logical schema change.
Whether the btree should actually be read at the snapshot is decided separately by
[Self::is_btree_allocated_at] / [Self::resolve_root_page_at], which do gate on
begin. u64::MAX resolves the current live owner.
Sourcepub fn try_get_table_id_from_root_page_at(
&self,
root_page: i64,
snapshot_ts: u64,
) -> Option<MVTableId>
pub fn try_get_table_id_from_root_page_at( &self, root_page: i64, snapshot_ts: u64, ) -> Option<MVTableId>
Fallible variant of Self::get_table_id_from_root_page_at: returns None when a
positive root page has no binding that covers snapshot_ts. Under a PASSIVE checkpoint
this is not an invariant violation but a stale-schema read: the transaction captured an
older schema_cookie (the commit that dropped this object published its cookie after the
transaction read the header, even though the drop’s commit ts precedes the transaction’s
begin ts) and compiled a cursor against a table its own snapshot already sees dropped.
The caller turns this into LimboError::SchemaUpdated so the statement reprepares
against the current schema. See the begin/commit schema-coherence note in the passive
checkpoint design.
Sourcepub fn read_snapshot_ts(&self, tx_id: TxID) -> u64
pub fn read_snapshot_ts(&self, tx_id: TxID) -> u64
Snapshot timestamp (begin_ts) of the given transaction, or u64::MAX if it is not
tracked (resolving the live root-page binding). Used to make a transaction’s root-page
lookups snapshot-consistent.
Sourcepub fn read_tx_mark(&self, tx_id: TxID) -> WalPos
pub fn read_tx_mark(&self, tx_id: TxID) -> WalPos
This transaction’s frozen WAL read mark, or WalPos::STAGED (sees everything published)
if untracked. The physical-reachability coordinate of the btree-read gate. See
Self::is_btree_readable_at.
Sourcepub fn insert_table_id_to_rootpage(
&self,
table_id: MVTableId,
root_page: Option<u64>,
)
pub fn insert_table_id_to_rootpage( &self, table_id: MVTableId, root_page: Option<u64>, )
Insert a live table_id -> root_page binding (bootstrap/recovery, or an
uncheckpointed-create with None). Visible to every snapshot. Checkpoint-time
allocation of a real root page goes through Self::record_rootpage_alloc instead.
pub fn remove_table_id_to_rootpage(&self, table_id: &MVTableId)
Sourcepub fn current_root_page(&self, table_id: &MVTableId) -> Option<u64>
pub fn current_root_page(&self, table_id: &MVTableId) -> Option<u64>
The current physical root page of table_id, if it is checkpointed and live.
Sourcepub fn record_rootpage_alloc(
&self,
table_id: MVTableId,
root_page: u64,
begin_ts: u64,
materialized_at: WalPos,
)
pub fn record_rootpage_alloc( &self, table_id: MVTableId, root_page: u64, begin_ts: u64, materialized_at: WalPos, )
Record that a PASSIVE checkpoint allocated root_page for table_id. begin_ts is the
checkpoint’s snapshot ts (base-validity lower bound); materialized_at is the WAL position
the pages reach durability at — WalPos::STAGED at btree_create (pages not committed
yet), lowered to the real position by Self::publish_rootpage_visible in the
post-CommitPagerTxn publish window.
Sourcepub fn publish_rootpage_visible(
&self,
table_id: MVTableId,
materialized_at: WalPos,
)
pub fn publish_rootpage_visible( &self, table_id: MVTableId, materialized_at: WalPos, )
Publish a staged root-page binding: set its materialized_at from WalPos::STAGED to the
WAL position the pages were committed at, making the btree physically readable by any
transaction whose read mark reaches that position. Called in the checkpoint’s post-commit
publish window. No-op if the entry is gone (e.g. dropped same checkpoint).
Sourcepub fn retire_rootpage(&self, table_id: MVTableId, end_ts: u64)
pub fn retire_rootpage(&self, table_id: MVTableId, end_ts: u64)
Close table_id’s binding at end_ts (the drop tombstone commit ts) but keep it, so a
transaction whose snapshot predates the drop can still resolve the (read-mark-protected)
root page. Reclaimed by Self::gc_rootpage_entries once end_ts <= lwm.
Sourcepub fn gc_rootpage_entries(&self, lwm: u64) -> usize
pub fn gc_rootpage_entries(&self, lwm: u64) -> usize
Drop closed (retired) bindings no transaction can still see (dropped, with
end <= lwm). Live bindings have end == u64::MAX and are never reclaimed — important
because compute_lwm() is u64::MAX when no transactions are active. Returns count.
Sourcepub fn bootstrap(&self, bootstrap_conn: Arc<Connection>) -> Result<()>
pub fn bootstrap(&self, bootstrap_conn: Arc<Connection>) -> Result<()>
Bootstrap the MV store from the SQLite schema table and logical log.
- Get all root pages from the already parsed schema object
- Assign table IDs to the root pages (table_id = -1 * root_page)
- Complete interrupted WAL/log checkpoint reconciliation, if needed
- Promote the bootstrap connection to a regular connection so that it reads from the MV store again
- Recover the logical log
- Make sure schema changes reflected from deserialized logical log are captured in the schema
Blocking shim retained for synchronous callers. The open, attach, and
journal-mode state machines drive
MvStore::bootstrap_nonblock directly.
Sourcepub fn get_next_table_id(&self) -> i64
pub fn get_next_table_id(&self) -> i64
MVCC does not use the pager/btree cursors to create pages until checkpoint. This method is used to assign root page numbers when Insn::CreateBtree is used. MVCC table ids are always negative. Their corresponding rootpage entry in sqlite_schema is the same negative value if the table has not been checkpointed yet. Otherwise, the root page will be positive and corresponds to the actual physical page.
pub fn get_next_rowid(&self) -> i64
Sourcepub fn insert(&self, tx_id: TxID, row: Row) -> Result<()>
pub fn insert(&self, tx_id: TxID, row: Row) -> Result<()>
Inserts a new row into a table in the database.
This function inserts a new row into the database within the context
of the transaction tx_id.
§Arguments
tx_id- the ID of the transaction in which to insert the new row.row- the row object containing the values to be inserted.
Sourcepub fn insert_to_table_or_index(
&self,
tx_id: TxID,
row: Row,
maybe_index_id: Option<MVTableId>,
) -> Result<()>
pub fn insert_to_table_or_index( &self, tx_id: TxID, row: Row, maybe_index_id: Option<MVTableId>, ) -> Result<()>
Same as insert() but can insert to a table or an index, indicated by the maybe_index_id argument.
Sourcepub fn insert_tombstone_to_table_or_index(
&self,
tx_id: TxID,
id: RowID,
row: Row,
maybe_index_id: Option<MVTableId>,
) -> Result<()>
pub fn insert_tombstone_to_table_or_index( &self, tx_id: TxID, id: RowID, row: Row, maybe_index_id: Option<MVTableId>, ) -> Result<()>
Inserts a deletion record for a row that does not currently have any versions in the MV store. This is used in cases where the BTree contains that record, but it is logically deleted.
Sourcepub fn insert_btree_resident_to_table_or_index(
&self,
tx_id: TxID,
row: Row,
maybe_index_id: Option<MVTableId>,
) -> Result<()>
pub fn insert_btree_resident_to_table_or_index( &self, tx_id: TxID, row: Row, maybe_index_id: Option<MVTableId>, ) -> Result<()>
Inserts a row that was read from the B-tree (not in MvStore). This is used when updating a row that exists in B-tree but hasn’t been modified in MVCC yet. The btree_resident flag helps the checkpoint logic determine if subsequent deletes should be checkpointed to the B-tree file.
Sourcepub fn update(&self, tx_id: TxID, row: Row) -> Result<bool>
pub fn update(&self, tx_id: TxID, row: Row) -> Result<bool>
Updates a row in a table in the database with new values.
This function updates an existing row in the database within the
context of the transaction tx_id. The row argument identifies the
row to be updated as id and contains the new values to be inserted.
If the row identified by the id does not exist, this function does
nothing and returns false. Otherwise, the function updates the row
with the new values and returns true.
§Arguments
tx_id- the ID of the transaction in which to update the new row.row- the row object containing the values to be updated.
§Returns
Returns true if the row was successfully updated, and false otherwise.
Sourcepub fn update_to_table_or_index(
&self,
tx_id: TxID,
row: Row,
maybe_index_id: Option<MVTableId>,
) -> Result<bool>
pub fn update_to_table_or_index( &self, tx_id: TxID, row: Row, maybe_index_id: Option<MVTableId>, ) -> Result<bool>
Same as update() but can update a table or an index, indicated by the maybe_index_id argument.
Sourcepub fn upsert(&self, tx_id: TxID, row: Row) -> Result<()>
pub fn upsert(&self, tx_id: TxID, row: Row) -> Result<()>
Inserts a row into a table in the database with new values, previously deleting any old data if it existed. Bails on a delete error, e.g. write-write conflict.
Sourcepub fn upsert_to_table_or_index(
&self,
tx_id: TxID,
row: Row,
maybe_index_id: Option<MVTableId>,
) -> Result<()>
pub fn upsert_to_table_or_index( &self, tx_id: TxID, row: Row, maybe_index_id: Option<MVTableId>, ) -> Result<()>
Same as upsert() but can upsert to a table or an index, indicated by the maybe_index_id argument.
Sourcepub fn delete(&self, tx_id: TxID, id: RowID) -> Result<bool>
pub fn delete(&self, tx_id: TxID, id: RowID) -> Result<bool>
Deletes a row from the table with the given id.
This function deletes an existing row id in the database within the
context of the transaction tx_id.
§Arguments
tx_id- the ID of the transaction in which to delete the new row.id- the ID of the row to delete.
§Returns
Returns true if the row was successfully deleted, and false otherwise.
Sourcepub fn delete_from_table_or_index(
&self,
tx_id: TxID,
id: RowID,
maybe_index_id: Option<MVTableId>,
) -> Result<bool>
pub fn delete_from_table_or_index( &self, tx_id: TxID, id: RowID, maybe_index_id: Option<MVTableId>, ) -> Result<bool>
Same as delete() but can delete from a table or an index, indicated by the maybe_index_id argument.
Sourcepub fn read(&self, tx_id: TxID, id: &RowID) -> Result<Option<Row>>
pub fn read(&self, tx_id: TxID, id: &RowID) -> Result<Option<Row>>
Retrieves a row from the table with the given id.
This operation is performed within the scope of the transaction identified
by tx_id.
§Arguments
tx_id- The ID of the transaction to perform the read operation in.id- The ID of the row to retrieve.
§Returns
Returns Some(row) with the row data if the row with the given id exists,
and None otherwise.
Sourcepub fn read_from_table_or_index(
&self,
tx_id: TxID,
id: &RowID,
maybe_index_id: Option<MVTableId>,
) -> Result<Option<Row>>
pub fn read_from_table_or_index( &self, tx_id: TxID, id: &RowID, maybe_index_id: Option<MVTableId>, ) -> Result<Option<Row>>
Same as read() but can read from a table or an index, indicated by the maybe_index_id argument.
Sourcepub fn scan_row_ids(&self) -> Result<Vec<RowID>>
pub fn scan_row_ids(&self) -> Result<Vec<RowID>>
Gets all row ids in the database.
pub fn get_row_id_range( &self, table_id: MVTableId, start: i64, bucket: &mut Vec<RowID>, max_items: u64, ) -> Result<()>
Sourcepub fn query_btree_version_is_valid(
&self,
table_id: MVTableId,
row_id: &RowKey,
tx_id: TxID,
) -> bool
pub fn query_btree_version_is_valid( &self, table_id: MVTableId, row_id: &RowKey, tx_id: TxID, ) -> bool
Check if the B-tree version of a row should be shown to the given transaction.
Returns true if the B-tree version is valid (should be shown). Returns false if the B-tree version is shadowed or deleted by MVCC.
pub fn seek_rowid( &self, start: RowID, inclusive: bool, eq_only: bool, direction: IterationDirection, tx_id: TxID, table_iterator: &mut Option<Box<dyn Iterator<Item = Entry<'static, RowID, RowVersions<A>, BasicComparator, A>> + Send + Sync>>, ) -> Option<RowID>
pub fn seek_index( &self, index_id: MVTableId, start: SortableIndexKey, inclusive: bool, eq_only: bool, direction: IterationDirection, tx_id: TxID, index_iterator: &mut Option<Box<dyn Iterator<Item = Entry<'static, Arc<SortableIndexKey>, RowVersions<A>, BasicComparator, A>> + Send + Sync>>, ) -> Result<Option<RowID>>
Sourcepub fn begin_exclusive_tx(
&self,
pager: Arc<Pager>,
maybe_existing_tx_id: Option<TxID>,
connection: &Connection,
expected_schema_generation: Option<u64>,
) -> Result<TxID>
pub fn begin_exclusive_tx( &self, pager: Arc<Pager>, maybe_existing_tx_id: Option<TxID>, connection: &Connection, expected_schema_generation: Option<u64>, ) -> Result<TxID>
Begins an exclusive write transaction that prevents concurrent writes.
This is used for IMMEDIATE and EXCLUSIVE transaction types where we need to ensure exclusive write access as per SQLite semantics.
Sourcepub fn begin_tx(&self, pager: Arc<Pager>) -> Result<TxID>
pub fn begin_tx(&self, pager: Arc<Pager>) -> Result<TxID>
Begins a new transaction in the database.
This function starts a new transaction in the database and returns a TxID value
that you can use to perform operations within the transaction. All changes made within the
transaction are isolated from other transactions until you commit the transaction.
Sourcepub fn begin_tx_with_schema_generation(
&self,
pager: Arc<Pager>,
expected_schema_generation: Option<u64>,
) -> Result<TxID>
pub fn begin_tx_with_schema_generation( &self, pager: Arc<Pager>, expected_schema_generation: Option<u64>, ) -> Result<TxID>
begin_tx with the connection’s validated schema_generation gate (see
Connection::mvcc_begin_schema_generation). Used by the statement begin path so a passive
checkpoint that republishes physical roots into the begin window forces a reprepare instead
of a transaction beginning against stale roots.
pub fn remove_tx(&self, tx_id: TxID) -> Result<(), TryReserveError>
pub fn register_sequence_allocation( &self, tx_id: TxID, sequence_name: &str, sequence_value: i64, ) -> Result<()>
pub fn set_sequence_watermark(&self, sequence_name: &str, watermark: i64)
Sourcepub fn sequence_watermark(&self, sequence_name: &str) -> Option<i64>
pub fn sequence_watermark(&self, sequence_name: &str) -> Option<i64>
Returns the first sequence value that is not safe for cursor scans to pass.
Readers can safely consume rows with sequence values less than this watermark. The value is the minimum of the current sequence boundary and any lower value already allocated by an active transaction.
Sourcepub fn finish_committed_tx(
&self,
tx_id: TxID,
conn: &Connection,
db_id: usize,
) -> Result<(), TryReserveError>
pub fn finish_committed_tx( &self, tx_id: TxID, conn: &Connection, db_id: usize, ) -> Result<(), TryReserveError>
Atomically retire a committed tx: clear the connection’s mv_tx_id cache
for db_id, then remove the tx from txs. Pairs the two mutations so
no concurrent observer (or in-flight statement) can see the divergent
(cache=Some, txs=None) state — the production-panic shape from
release_named_savepoint and NoSuchTransactionID read-path errors.
Order matches rollback_tx (cache cleared before remove_tx) so the
commit and rollback paths are symmetric.
Use this anywhere the commit state machine would otherwise call
remove_tx directly. Other call sites that don’t have a connection
context (e.g. tests poking internal state) keep using remove_tx.
pub fn get_transaction_database_header(&self, tx_id: &TxID) -> DatabaseHeader
Sourcepub fn set_global_page_size(&self, size: PageSize)
pub fn set_global_page_size(&self, size: PageSize)
Update the cached global header’s page size to match a fresh PRAGMA page_size.
global_header is captured from the pager during MVCC bootstrap, before any PRAGMA
can run, so it always starts at the default 4 KiB. Subsequent transactions copy from
it, which means a PRAGMA page_size = N issued on the connection would otherwise be
invisible to MVCC header lookups even though the pager itself honors N for on-disk
page allocation. Only valid before any data has been written; matches the same
precondition Connection::reset_page_size enforces via db.initialized().
pub fn with_header<T, F>(&self, f: F, tx_id: Option<&TxID>) -> Result<T>where
F: Fn(&DatabaseHeader) -> T,
pub fn with_header_mut<T, F>(&self, f: F, tx_id: Option<&TxID>) -> Result<T>where
F: Fn(&mut DatabaseHeader) -> T,
Sourcepub fn commit_tx(
self: &Arc<Self>,
tx_id: TxID,
connection: &Arc<Connection>,
db_id: usize,
) -> Result<StateMachine<Box<CommitStateMachine<Clock, A>>>>
pub fn commit_tx( self: &Arc<Self>, tx_id: TxID, connection: &Arc<Connection>, db_id: usize, ) -> Result<StateMachine<Box<CommitStateMachine<Clock, A>>>>
Commits a transaction with the specified transaction ID.
This function commits the changes made within the specified transaction and finalizes the transaction. Once a transaction has been committed, all changes made within the transaction are visible to other transactions that access the same data.
§Arguments
tx_id- The ID of the transaction to commit.
Sourcepub fn is_tx_rollbackable(&self, tx_id: TxID) -> bool
pub fn is_tx_rollbackable(&self, tx_id: TxID) -> bool
Returns true if the transaction can be rolled back (Active or Preparing).
Sourcepub fn rollback_tx(
&self,
tx_id: TxID,
_pager: Arc<Pager>,
connection: &Connection,
db: usize,
)
pub fn rollback_tx( &self, tx_id: TxID, _pager: Arc<Pager>, connection: &Connection, db: usize, )
Rolls back a transaction with the specified ID.
This function rolls back a transaction with the specified tx_id by
discarding any changes made by the transaction.
§Arguments
tx_id- The ID of the transaction to abort.db- The database index this transaction belongs to.
Sourcepub fn begin_savepoint(&self, tx_id: TxID)
pub fn begin_savepoint(&self, tx_id: TxID)
Begin a savepoint for the transaction. This should be called at the start of a statement in an interactive transaction.
Sourcepub fn begin_named_savepoint(
&self,
tx_id: TxID,
name: String,
starts_transaction: bool,
deferred_fk_violations: isize,
)
pub fn begin_named_savepoint( &self, tx_id: TxID, name: String, starts_transaction: bool, deferred_fk_violations: isize, )
Begin a user-visible named savepoint inside an existing transaction.
starts_transaction is true when the savepoint was opened in autocommit mode and therefore
releasing the root savepoint should commit the transaction.
Sourcepub fn release_savepoint(&self, tx_id: TxID)
pub fn release_savepoint(&self, tx_id: TxID)
Release the newest savepoint for the transaction. This should be called when a statement completes successfully. Silently returns if the transaction doesn’t exist (e.g., already committed).
Sourcepub fn release_named_savepoint(
&self,
tx_id: TxID,
name: &str,
) -> Result<SavepointResult>
pub fn release_named_savepoint( &self, tx_id: TxID, name: &str, ) -> Result<SavepointResult>
Releases a named savepoint and nested savepoints above it.
Returns [SavepointResult::Commit] when releasing the root savepoint should commit the transaction.
Sourcepub fn rollback_first_savepoint(&self, tx_id: u64) -> Result<bool>
pub fn rollback_first_savepoint(&self, tx_id: u64) -> Result<bool>
Rolls back a savepoint within a transaction. Returns true if a savepoint was rolled back, false if no savepoint was active.
Sourcepub fn rollback_to_named_savepoint(
&self,
tx_id: TxID,
name: &str,
) -> Result<Option<isize>>
pub fn rollback_to_named_savepoint( &self, tx_id: TxID, name: &str, ) -> Result<Option<isize>>
Rolls back to the newest matching named savepoint while keeping that savepoint active.
Returns the deferred FK snapshot stored on the named savepoint, or None if no matching
savepoint exists.
Sourcepub fn is_exclusive_tx(&self, tx_id: &TxID) -> bool
pub fn is_exclusive_tx(&self, tx_id: &TxID) -> bool
Returns true if the given transaction is the exclusive transaction.
Sourcepub fn get_version_id(&self) -> u64
pub fn get_version_id(&self) -> u64
Generates next unique version ID for RowVersion tracking.
Sourcepub fn get_begin_timestamp(&self) -> u64
pub fn get_begin_timestamp(&self) -> u64
Generate a begin timestamp. No side-effect needed alongside generation.
Sourcepub fn get_commit_timestamp<F: FnOnce(u64)>(&self, f: F) -> u64
pub fn get_commit_timestamp<F: FnOnce(u64)>(&self, f: F) -> u64
Generate a commit timestamp and call f with it while the clock
lock is held, atomically publishing the timestamp before release.
See [MvccClock] for the full explanation.
Sourcepub fn checkpoint_snapshot_ts(&self) -> u64
pub fn checkpoint_snapshot_ts(&self) -> u64
Snapshot timestamp for a checkpoint, clamped below any in-flight (Preparing)
commit. The published durable boundary (durable_txid_max_new) derives from this.
last_committed_tx_ts is a fetch_max high-water mark, so a transaction that
already assigned a lower end_ts and is still Preparing can sit below it; the
checkpoint would skip that transaction (not yet Committed) yet publish a boundary
above its end_ts, and a crash after it finalizes would discard its log frame
(commit_ts <= boundary) even though it was never written to the B-tree — silent
data loss. Clamping below the lowest Preparing end_ts prevents the straddle.
Computed while holding the clock lock (via get_timestamp) so a transaction
mid-(end_ts assignment + Preparing publish, which happen together under that
lock) cannot be missed by the scan. Active transactions need no clamp: their future
end_ts is drawn from the monotonic clock and is therefore > snapshot_ts.
Sourcepub fn schema_still_valid_for_tx(&self, tx_id: TxID) -> Result<()>
pub fn schema_still_valid_for_tx(&self, tx_id: TxID) -> Result<()>
Passive checkpoint published new physical roots after this transaction began.
Sourcepub fn compute_lwm(&self) -> u64
pub fn compute_lwm(&self) -> u64
Compute the low-water mark: the minimum begin_ts of all active or preparing transactions. Returns u64::MAX if no transactions are active. Used by GC to determine which row versions are safe to reclaim.
Sourcepub fn live_version_count_approx(&self) -> usize
pub fn live_version_count_approx(&self) -> usize
Current approximate live row-version count. Heuristic only (see the
live_version_count_approx field) — never use for correctness decisions.
Sourcepub fn set_gc_threshold(&self, threshold: i64)
pub fn set_gc_threshold(&self, threshold: i64)
Set the inline-GC trigger threshold (growth in live versions since the
last GC pass). Negative disables inline GC. Mirrors
set_checkpoint_threshold; wired to the mvcc_gc_threshold PRAGMA.
pub fn gc_threshold(&self) -> i64
Sourcepub fn should_gc(&self) -> bool
pub fn should_gc(&self) -> bool
Whether an incremental GC pass should run now: inline GC is enabled
(threshold >= 0) and live_version_count_approx has grown past the threshold
since the last pass. Heuristic — drift only changes GC frequency.
Sourcepub fn drop_unused_row_versions(&self) -> usize
pub fn drop_unused_row_versions(&self) -> usize
Garbage-collects row versions that are invisible to all active transactions.
Uses the low-water mark (LWM) to determine reclaimability in O(1) per version.
Covers both table rows (self.rows) and index rows (self.index_rows).
Returns the number of removed versions.
Sourcepub fn drop_unused_row_versions_and_slots(&self) -> usize
pub fn drop_unused_row_versions_and_slots(&self) -> usize
Like Self::drop_unused_row_versions, but additionally removes chain
slots that end up empty from the skip maps, bounding their entry counts.
The caller must hold the blocking checkpoint lock (or otherwise guarantee
no concurrent writers): slot removal happens after the chain write lock
is dropped, so without that guarantee it races a concurrent
get_or_insert_with on the same key — see the TOCTOU note in
gc_table_row_versions.
Sourcepub fn gc_incremental(&self, max_chains: usize) -> usize
pub fn gc_incremental(&self, max_chains: usize) -> usize
Incremental, non-blocking GC pass — the inline counterpart to
Self::drop_unused_row_versions, driven from the commit path.
Reclaims invisible versions (same rules as gc_version_chain) from up
to max_chains table-row chains, resuming from where the previous pass
stopped (gc_table_cursor) so repeated calls eventually cover the whole
rows map without scanning it all at once.
Safety / design notes:
- Lazy mode only. Empty SkipMap slots are left in place (no
entry.remove()), so the pass needs no blocking checkpoint lock — it races no concurrentget_or_insert_with(see the TOCTOU note ingc_table_row_versions). Physical slot removal stays exclusive to the checkpoint’s_and_slotssweep. finalized_tx_statespruning is intentionally skipped here: it needs the complete referenced-txid set across all chains, which a partial sweep cannot produce. The checkpoint path still prunes it.
Sourcepub fn insert_index_version(
&self,
index_id: MVTableId,
key: Arc<SortableIndexKey>,
row_version: RowVersion,
) -> Result<(Arc<SortableIndexKey>, RowVersions<A>)>
pub fn insert_index_version( &self, index_id: MVTableId, key: Arc<SortableIndexKey>, row_version: RowVersion, ) -> Result<(Arc<SortableIndexKey>, RowVersions<A>)>
Inserts (or appends to) the version chain for an index entry and returns the id and versions of the modified row.
Sourcepub fn insert_version_raw(
&self,
versions: &mut RowVersionChain<A>,
row_version: RowVersion,
) -> Result<(), TryReserveError>
pub fn insert_version_raw( &self, versions: &mut RowVersionChain<A>, row_version: RowVersion, ) -> Result<(), TryReserveError>
Inserts a new row version into the internal data structure for versions, while making sure that the row version is inserted in the correct order.
pub fn write_row_to_pager( &self, row: &Row, cursor: Arc<RwLock<BTreeCursor>>, requires_seek: bool, ) -> Result<StateMachine<WriteRowStateMachine>>
pub fn delete_row_from_pager( &self, rowid: RowID, cursor: Arc<RwLock<BTreeCursor>>, ) -> Result<StateMachine<DeleteRowStateMachine>>
Sourcepub fn purge_row_versions_during_checkpoint(&self, rowid: RowID)
pub fn purge_row_versions_during_checkpoint(&self, rowid: RowID)
Clear every version-chain entry for rowid. Used by checkpoint-time
compaction that deletes the corresponding B-tree row outside the
normal MVCC delete path (e.g. SeqCompactDriver for sequence
backing tables) so the two layers stay in sync — without this the
version chain would keep RowVersion { begin: Timestamp(T), end: None, btree_resident: true } entries pointing at B-tree rows that
no longer exist, until drop_unused_row_versions Rule 3 caught up.
Caller contract — the caller must hold a guarantee that no
concurrent reader can observe mid-purge state. Today the only
caller is SeqCompactDriver, which runs inside the checkpoint
while the pager_commit_lock is held; nextval allocators serialize
through that same lock, so they cannot see the chain in a partially
purged state. Callers without that guarantee must add proper
tombstones via the normal write path instead.
Empty chain slots are left in the SkipMap (lazy removal). The
same TOCTOU rationale as gc_table_row_versions applies: removing
the slot would race a concurrent get_or_insert_with from a future
write to the same key.
Sourcepub fn seqcompact_commit_delete(
&self,
rowid: RowID,
num_cols: usize,
end_ts: u64,
)
pub fn seqcompact_commit_delete( &self, rowid: RowID, num_cols: usize, end_ts: u64, )
Passive sequence compaction: record end-stamped deletes instead of inline B-tree purge.
pub fn get_last_table_rowid( &self, table_id: MVTableId, table_iterator: &mut Option<Box<dyn Iterator<Item = Entry<'static, RowID, RowVersions<A>, BasicComparator, A>> + Send + Sync>>, tx_id: TxID, ) -> Option<RowKey>
pub fn get_last_table_rowid_without_visibility_check( &self, table_id: MVTableId, ) -> Option<RowKey>
pub fn get_last_index_rowid( &self, index_id: MVTableId, tx_id: TxID, index_iterator: &mut Option<Box<dyn Iterator<Item = Entry<'static, Arc<SortableIndexKey>, RowVersions<A>, BasicComparator, A>> + Send + Sync>>, ) -> Result<Option<RowKey>>
pub fn get_logical_log_file(&self) -> Arc<dyn File> ⓘ
pub fn logical_log_offset(&self) -> u64
Sourcepub fn reset_logical_log_after_external_restore(&self) -> Result<Completion>
pub fn reset_logical_log_after_external_restore(&self) -> Result<Completion>
Replace the logical log with a fresh valid header after the database file was restored outside MVCC.
The returned completion must finish before reopening/recovering MVCC state. Otherwise recovery could replay stale local logical-log frames on top of the restored database image.
Sourcepub fn sync_logical_log_after_external_restore(
&self,
connection: &Arc<Connection>,
) -> Result<Option<Completion>>
pub fn sync_logical_log_after_external_restore( &self, connection: &Arc<Connection>, ) -> Result<Option<Completion>>
Return the durable sync completion for the freshly reset logical log.
This is separate from reset_logical_log_after_external_restore so
callers can drive the reset completion cooperatively, then issue the
ordered sync only after the header/truncate group has completed.
Sourcepub fn maybe_recover_logical_log(
&self,
connection: &Arc<Connection>,
st: &mut RecoverLogicalLogState,
) -> Result<IOResult<bool>>
pub fn maybe_recover_logical_log( &self, connection: &Arc<Connection>, st: &mut RecoverLogicalLogState, ) -> Result<IOResult<bool>>
Replays committed logical-log frames into the in-memory MVCC store.
Only frames with commit_ts > persistent_tx_ts_max (the durable replay
boundary from the metadata table) are applied; earlier frames were
already checkpointed. On success, reseeds the MVCC clock and sets the log
writer offset so torn-tail bytes are overwritten. Returns true if any
frames were replayed.
pub fn set_checkpoint_threshold(&self, threshold: i64)
pub fn checkpoint_threshold(&self) -> i64
pub fn get_real_table_id(&self, table_id: i64) -> i64
pub fn get_rowid_allocator(&self, table_id: &MVTableId) -> Arc<RowidAllocator> ⓘ
Sourcepub fn is_btree_allocated(&self, table_id: &MVTableId) -> bool
pub fn is_btree_allocated(&self, table_id: &MVTableId) -> bool
Whether table_id has a currently live checkpointed B-tree. Snapshot-agnostic; for
transaction reads use Self::is_btree_readable_at.
Sourcepub fn is_btree_readable_at(
&self,
table_id: &MVTableId,
begin_ts: u64,
read_mark: WalPos,
) -> bool
pub fn is_btree_readable_at( &self, table_id: &MVTableId, begin_ts: u64, read_mark: WalPos, ) -> bool
Whether a transaction may read table_id’s B-tree, given its logical snapshot
begin_ts and its frozen WAL read_mark. Requires BOTH:
- logical (base validity): the binding
covers(begin_ts)—begin <= begin_ts < end; and - physical reachability:
materialized_at <= read_mark— the btree’s pages are at-or-below this transaction’s read mark (same WAL epoch and frame ≤ mark, or an earlier backfilled epoch). Without this a transaction that opened before an checkpoint materialization would seek a page its read mark cannot reach (a torn/foreign/zeroed-page read).
When this is false the transaction stays version-store-only; the GC floor
(Self::compute_min_reader_mark) guarantees the version-store copy is still present.
Sourcepub fn compute_min_reader_mark(&self) -> WalPos
pub fn compute_min_reader_mark(&self) -> WalPos
Lexicographic minimum WAL read mark over all active/preparing transactions
(WalPos::STAGED if none). A freshly-materialized object’s version-store rows may be GC’d
only once materialized_at <= this, i.e. every live reader can now physically reach it —
otherwise a reader whose read mark predates the materialization would lose the rows.
pub fn tx_should_abort(&self, tx_id: u64) -> bool
Trait Implementations§
Auto Trait Implementations§
impl<Clock, A = TursoAllocator> !Freeze for MvStore<Clock, A>
impl<Clock, A = TursoAllocator> !RefUnwindSafe for MvStore<Clock, A>
impl<Clock, A = TursoAllocator> !UnwindSafe for MvStore<Clock, A>
impl<Clock, A> Send for MvStore<Clock, A>where
SkipMap<RowID, Arc<RwLock<RawRwLock, <A as RowVersionAllocator>::RowVersionChain>>, BasicComparator, A>: Send,
SkipMap<MVTableId, RootEntry, BasicComparator, A>: Send,
SkipMap<MVTableId, SkipMap<Arc<SortableIndexKey>, Arc<RwLock<RawRwLock, <A as RowVersionAllocator>::RowVersionChain>>, BasicComparator, A>, BasicComparator, A>: Send,
SkipMap<u64, Transaction<A>, BasicComparator, A>: Send,
SkipMap<u64, TransactionState, BasicComparator, A>: Send,
impl<Clock, A> Sync for MvStore<Clock, A>where
SkipMap<RowID, Arc<RwLock<RawRwLock, <A as RowVersionAllocator>::RowVersionChain>>, BasicComparator, A>: Sync,
SkipMap<MVTableId, RootEntry, BasicComparator, A>: Sync,
SkipMap<MVTableId, SkipMap<Arc<SortableIndexKey>, Arc<RwLock<RawRwLock, <A as RowVersionAllocator>::RowVersionChain>>, BasicComparator, A>, BasicComparator, A>: Sync,
SkipMap<u64, Transaction<A>, BasicComparator, A>: Sync,
SkipMap<u64, TransactionState, BasicComparator, A>: Sync,
impl<Clock, A> Unpin for MvStore<Clock, A>where
SkipMap<RowID, Arc<RwLock<RawRwLock, <A as RowVersionAllocator>::RowVersionChain>>, BasicComparator, A>: Unpin,
SkipMap<MVTableId, RootEntry, BasicComparator, A>: Unpin,
SkipMap<MVTableId, SkipMap<Arc<SortableIndexKey>, Arc<RwLock<RawRwLock, <A as RowVersionAllocator>::RowVersionChain>>, BasicComparator, A>, BasicComparator, A>: Unpin,
SkipMap<u64, Transaction<A>, BasicComparator, A>: Unpin,
SkipMap<u64, TransactionState, BasicComparator, A>: Unpin,
A: Unpin,
Clock: Unpin,
impl<Clock, A> UnsafeUnpin for MvStore<Clock, A>where
SkipMap<RowID, Arc<RwLock<RawRwLock, <A as RowVersionAllocator>::RowVersionChain>>, BasicComparator, A>: UnsafeUnpin,
SkipMap<MVTableId, RootEntry, BasicComparator, A>: UnsafeUnpin,
SkipMap<MVTableId, SkipMap<Arc<SortableIndexKey>, Arc<RwLock<RawRwLock, <A as RowVersionAllocator>::RowVersionChain>>, BasicComparator, A>, BasicComparator, A>: UnsafeUnpin,
SkipMap<u64, Transaction<A>, BasicComparator, A>: UnsafeUnpin,
SkipMap<u64, TransactionState, BasicComparator, A>: UnsafeUnpin,
A: UnsafeUnpin,
Clock: UnsafeUnpin,
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
impl<T> ErasedDestructor for Twhere
T: 'static,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more