Skip to content

Commit 809aebd

Browse files
authored
Move field replay_table_updated to ReplayCommittedState (#4807)
# Description of Changes Shaves another 32 bytes off of `CommittedState` by moving the last replay only map out of the type. # API and ABI breaking changes None # Expected complexity level and risk 2 # Testing Covered by existing tests.
1 parent 21b58ef commit 809aebd

File tree

3 files changed

+91
-43
lines changed

3 files changed

+91
-43
lines changed

crates/datastore/src/locking_tx_datastore/committed_state.rs

Lines changed: 0 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -87,24 +87,6 @@ pub struct CommittedState {
8787
/// - system tables: `st_view_sub`, `st_view_arg`
8888
/// - Tables which back views.
8989
pub(super) ephemeral_tables: EphemeralTables,
90-
91-
/// Set of tables whose `st_table` entries have been updated during the currently-replaying transaction,
92-
/// mapped to the current most-recent `st_table` row.
93-
///
94-
/// When processing an insert to `st_table`, if the table already exists, we'll record it here.
95-
/// Then, when we see a corresponding delete, we know that the table has not been dropped,
96-
/// and so we won't delete the in-memory structure or insert its ID into [`Self::replay_table_dropped`].
97-
///
98-
/// When looking up the `st_table` row for a table, if it has an entry here,
99-
/// that means there are two rows resident in `st_table` at this point in replay.
100-
/// We return the row recorded here rather than inspecting `st_table`.
101-
///
102-
/// We remove from this set when we reach the matching delete,
103-
/// and assert this set is empty at the end of each transaction.
104-
///
105-
/// [`RowPointer`]s from this set are passed to the `unsafe` [`Table::get_row_ref_unchecked`],
106-
/// so it's important to properly maintain only [`RowPointer`]s to valid, extant, non-deleted rows.
107-
pub(super) replay_table_updated: IntMap<TableId, RowPointer>,
10890
}
10991

11092
impl CommittedState {
@@ -138,7 +120,6 @@ impl MemoryUsage for CommittedState {
138120
page_pool: _,
139121
read_sets,
140122
ephemeral_tables,
141-
replay_table_updated,
142123
} = self;
143124
// NOTE(centril): We do not want to include the heap usage of `page_pool` as it's a shared resource.
144125
next_tx_offset.heap_usage()
@@ -147,7 +128,6 @@ impl MemoryUsage for CommittedState {
147128
+ index_id_map.heap_usage()
148129
+ read_sets.heap_usage()
149130
+ ephemeral_tables.heap_usage()
150-
+ replay_table_updated.heap_usage()
151131
}
152132
}
153133

@@ -205,24 +185,6 @@ impl StateView for CommittedState {
205185
None => Ok(ScanOrIndex::scan_eq(cols, val, self.iter(table_id)?)),
206186
}
207187
}
208-
209-
/// Find the `st_table` row for `table_id`, first inspecting [`Self::replay_table_updated`],
210-
/// then falling back to [`Self::iter_by_col_eq`] of `st_table`.
211-
fn find_st_table_row(&self, table_id: TableId) -> Result<StTableRow> {
212-
let row_ref = if let Some(row_ptr) = self.replay_table_updated.get(&table_id) {
213-
let (table, blob_store, _) = self.get_table_and_blob_store(table_id)?;
214-
// Safety: `row_ptr` is stored in `self.replay_table_updated`,
215-
// meaning it was inserted into `st_table` by `replay_insert`
216-
// and has not yet been deleted by `replay_delete_by_rel`.
217-
unsafe { table.get_row_ref_unchecked(blob_store, *row_ptr) }
218-
} else {
219-
self.iter_by_col_eq(ST_TABLE_ID, StTableFields::TableId, &table_id.into())?
220-
.next()
221-
.ok_or_else(|| TableError::IdNotFound(SystemTable::st_table, table_id.into()))?
222-
};
223-
224-
StTableRow::try_from(row_ref)
225-
}
226188
}
227189

228190
impl CommittedState {
@@ -235,7 +197,6 @@ impl CommittedState {
235197
read_sets: <_>::default(),
236198
page_pool,
237199
ephemeral_tables: <_>::default(),
238-
replay_table_updated: <_>::default(),
239200
}
240201
}
241202

crates/datastore/src/locking_tx_datastore/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ pub mod state_view;
99
pub use state_view::{IterByColEqTx, IterByColRangeTx};
1010
pub mod delete_table;
1111
mod replay;
12-
pub use replay::ErrorBehavior;
12+
pub use replay::{ErrorBehavior, Replay};
1313
mod tx;
1414
pub use tx::{NumDistinctValues, TxId};
1515
mod tx_state;

crates/datastore/src/locking_tx_datastore/replay.rs

Lines changed: 90 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@ use crate::locking_tx_datastore::state_view::StateView;
88
use crate::system_tables::{is_built_in_meta_row, StFields as _};
99
use crate::system_tables::{StColumnRow, StTableFields, StTableRow, ST_COLUMN_ID, ST_TABLE_ID};
1010
use anyhow::{anyhow, Context};
11-
use core::ops::{Deref, DerefMut};
12-
use parking_lot::RwLock;
11+
use core::ops::{Deref, DerefMut, RangeBounds};
12+
use parking_lot::{RwLock, RwLockReadGuard};
1313
use spacetimedb_commitlog::payload::txdata;
1414
use spacetimedb_data_structures::map::{HashSet, IntMap, IntSet};
1515
use spacetimedb_lib::Identity;
16-
use spacetimedb_primitives::{ColId, TableId};
16+
use spacetimedb_primitives::{ColId, ColList, TableId};
1717
use spacetimedb_sats::algebraic_value::de::ValueDeserializer;
1818
use spacetimedb_sats::buffer::BufReader;
1919
use spacetimedb_sats::{AlgebraicValue, Deserialize, ProductValue};
@@ -51,6 +51,11 @@ impl<F> Replay<F> {
5151
pub fn next_tx_offset(&self) -> u64 {
5252
self.committed_state.read_arc().next_tx_offset
5353
}
54+
55+
// NOTE: This is not unused.
56+
pub fn committed_state(&self) -> RwLockReadGuard<'_, CommittedState> {
57+
self.committed_state.read()
58+
}
5459
}
5560

5661
impl<F: FnMut(u64)> spacetimedb_commitlog::Decoder for &mut Replay<F> {
@@ -339,6 +344,24 @@ struct ReplayCommittedState<'cs> {
339344
/// and delete from it during [`Self::replay_delete`] of `st_column` rows.
340345
/// We assert this is empty at the end of each transaction.
341346
replay_columns_to_ignore: HashSet<RowPointer>,
347+
348+
/// Set of tables whose `st_table` entries have been updated during the currently-replaying transaction,
349+
/// mapped to the current most-recent `st_table` row.
350+
///
351+
/// When processing an insert to `st_table`, if the table already exists, we'll record it here.
352+
/// Then, when we see a corresponding delete, we know that the table has not been dropped,
353+
/// and so we won't delete the in-memory structure or insert its ID into [`Self::replay_table_dropped`].
354+
///
355+
/// When looking up the `st_table` row for a table, if it has an entry here,
356+
/// that means there are two rows resident in `st_table` at this point in replay.
357+
/// We return the row recorded here rather than inspecting `st_table`.
358+
///
359+
/// We remove from this set when we reach the matching delete,
360+
/// and assert this set is empty at the end of each transaction.
361+
///
362+
/// [`RowPointer`]s from this set are passed to the `unsafe` [`Table::get_row_ref_unchecked`],
363+
/// so it's important to properly maintain only [`RowPointer`]s to valid, extant, non-deleted rows.
364+
pub(super) replay_table_updated: IntMap<TableId, RowPointer>,
342365
}
343366

344367
impl Deref for ReplayCommittedState<'_> {
@@ -361,6 +384,7 @@ impl<'cs> ReplayCommittedState<'cs> {
361384
state,
362385
replay_table_dropped: <_>::default(),
363386
replay_columns_to_ignore: <_>::default(),
387+
replay_table_updated: <_>::default(),
364388
}
365389
}
366390

@@ -655,6 +679,69 @@ impl<'cs> ReplayCommittedState<'cs> {
655679
}
656680
}
657681

682+
impl StateView for ReplayCommittedState<'_> {
683+
/// Find the `st_table` row for `table_id`,
684+
/// first inspecting [`Self::replay_table_updated`],
685+
/// then falling back to [`CommittedState::iter_by_col_eq`].
686+
fn find_st_table_row(&self, table_id: TableId) -> Result<StTableRow> {
687+
if let Some(row_ptr) = self.replay_table_updated.get(&table_id) {
688+
let (table, blob_store, _) = self.state.get_table_and_blob_store(table_id)?;
689+
// SAFETY: `row_ptr` is stored in `self.replay_table_updated`,
690+
// meaning it was inserted into `st_table` by `replay_insert`
691+
// and has not yet been deleted by `replay_delete_by_rel`.
692+
let row_ref = unsafe { table.get_row_ref_unchecked(blob_store, *row_ptr) };
693+
StTableRow::try_from(row_ref)
694+
} else {
695+
self.state.find_st_table_row(table_id)
696+
}
697+
}
698+
699+
type Iter<'a>
700+
= <CommittedState as StateView>::Iter<'a>
701+
where
702+
Self: 'a;
703+
704+
type IterByColRange<'a, R: RangeBounds<AlgebraicValue>>
705+
= <CommittedState as StateView>::IterByColRange<'a, R>
706+
where
707+
Self: 'a;
708+
709+
type IterByColEq<'a, 'r>
710+
= <CommittedState as StateView>::IterByColEq<'a, 'r>
711+
where
712+
Self: 'a;
713+
714+
fn get_schema(&self, table_id: TableId) -> Option<&Arc<TableSchema>> {
715+
self.state.get_schema(table_id)
716+
}
717+
718+
fn table_row_count(&self, table_id: TableId) -> Option<u64> {
719+
self.state.table_row_count(table_id)
720+
}
721+
722+
fn iter(&self, table_id: TableId) -> Result<Self::Iter<'_>> {
723+
self.state.iter(table_id)
724+
}
725+
726+
fn iter_by_col_range<R: RangeBounds<AlgebraicValue>>(
727+
&self,
728+
table_id: TableId,
729+
cols: ColList,
730+
range: R,
731+
) -> Result<Self::IterByColRange<'_, R>> {
732+
self.state.iter_by_col_range(table_id, cols, range)
733+
}
734+
735+
fn iter_by_col_eq<'a, 'r>(
736+
&'a self,
737+
table_id: TableId,
738+
cols: impl Into<ColList>,
739+
value: &'r AlgebraicValue,
740+
) -> Result<Self::IterByColEq<'a, 'r>> {
741+
self.state.iter_by_col_eq(table_id, cols, value)
742+
}
743+
}
744+
658745
#[cfg(test)]
659746
mod tests {
660747
use crate::{

0 commit comments

Comments
 (0)