Skip to content

Commit 77ffdbb

Browse files
Move RelationalDB to spacetimedb-engine crate. (#5113)
# Description of Changes Moves `RelationalDB` and related database code into a new `spacetimedb-engine` crate. The main motivation is to tighten dependency control around the engine layer and isolate `RelationalDB` behind a crate boundary. - Majority of this PR is code-motion. - Removes direct production dependence on `tokio` from `spacetimedb-engine`. - Keeps `tokio` only as a dev-dependency for test-only code in `spacetimedb-engine`. - This is intended to be a structural refactor only and should not result in any functional change in production. - Adds a CI check to ensure `spacetimedb-engine` continues to compile in simulation mode # API and ABI breaking changes NA # Expected complexity level and risk 1.5. # Testing Existing tests should be enough. --------- Co-authored-by: clockwork-labs-bot <clockwork-labs-bot@users.noreply.github.com>
1 parent 934158a commit 77ffdbb

51 files changed

Lines changed: 928 additions & 688 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,9 @@ jobs:
261261
262262
wasm-bindgen --version
263263
264+
- name: Check engine simulation build
265+
run: cargo check -p spacetimedb-engine --no-default-features --features simulation
266+
264267
# Source emsdk environment to make emcc (Emscripten compiler) available in PATH.
265268
- name: Run tests
266269
run: |

Cargo.lock

Lines changed: 43 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ members = [
1414
"crates/data-structures",
1515
"crates/datastore",
1616
"crates/durability",
17+
"crates/engine",
1718
"crates/execution",
1819
"crates/expr",
1920
"crates/guard",
@@ -133,8 +134,10 @@ spacetimedb-core = { path = "crates/core", version = "=2.6.0" }
133134
spacetimedb-data-structures = { path = "crates/data-structures", version = "=2.6.0" }
134135
spacetimedb-datastore = { path = "crates/datastore", version = "=2.6.0" }
135136
spacetimedb-durability = { path = "crates/durability", version = "=2.6.0" }
137+
spacetimedb-engine = { path = "crates/engine", version = "=2.6.0" }
136138
spacetimedb-execution = { path = "crates/execution", version = "=2.6.0" }
137139
spacetimedb-expr = { path = "crates/expr", version = "=2.6.0" }
140+
spacetimedb-fs-utils = { path = "crates/fs-utils", version = "=2.6.0" }
138141
spacetimedb-guard = { path = "crates/guard", version = "=2.6.0" }
139142
spacetimedb-lib = { path = "crates/lib", default-features = false, version = "=2.6.0" }
140143
spacetimedb-memory-usage = { path = "crates/memory-usage", version = "=2.6.0", default-features = false }
@@ -144,16 +147,15 @@ spacetimedb-pg = { path = "crates/pg", version = "=2.6.0" }
144147
spacetimedb-physical-plan = { path = "crates/physical-plan", version = "=2.6.0" }
145148
spacetimedb-primitives = { path = "crates/primitives", version = "=2.6.0" }
146149
spacetimedb-query = { path = "crates/query", version = "=2.6.0" }
150+
spacetimedb-query-builder = { path = "crates/query-builder", version = "=2.6.0" }
151+
spacetimedb-runtime = { path = "crates/runtime", version = "=2.6.0" }
147152
spacetimedb-sats = { path = "crates/sats", version = "=2.6.0" }
148153
spacetimedb-schema = { path = "crates/schema", version = "=2.6.0" }
154+
spacetimedb-snapshot = { path = "crates/snapshot", version = "=2.6.0" }
149155
spacetimedb-standalone = { path = "crates/standalone", version = "=2.6.0" }
156+
spacetimedb-subscription = { path = "crates/subscription", version = "=2.6.0" }
150157
spacetimedb-sql-parser = { path = "crates/sql-parser", version = "=2.6.0" }
151158
spacetimedb-table = { path = "crates/table", version = "=2.6.0" }
152-
spacetimedb-fs-utils = { path = "crates/fs-utils", version = "=2.6.0" }
153-
spacetimedb-snapshot = { path = "crates/snapshot", version = "=2.6.0" }
154-
spacetimedb-subscription = { path = "crates/subscription", version = "=2.6.0" }
155-
spacetimedb-query-builder = { path = "crates/query-builder", version = "=2.6.0" }
156-
spacetimedb-runtime = { path = "crates/runtime", version = "=2.6.0" }
157159

158160
# Prevent `ahash` from pulling in `getrandom` by disabling default features.
159161
# Modules use `getrandom02` and we need to prevent an incompatible version

crates/bench/benches/subscription.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
use criterion::{black_box, criterion_group, criterion_main, Criterion};
22
use spacetimedb::client::consume_each_list::ConsumeEachBuffer;
33
use spacetimedb::db::relational_db::RelationalDB;
4+
use spacetimedb::db::sql::ast::SchemaViewer;
45
use spacetimedb::error::DBError;
56
use spacetimedb::identity::AuthCtx;
6-
use spacetimedb::sql::ast::SchemaViewer;
77
use spacetimedb::subscription::row_list_builder_pool::BsatnRowListBuilderPool;
88
use spacetimedb::subscription::tx::DeltaTx;
99
use spacetimedb::subscription::{collect_table_update, TableUpdateType};

crates/core/Cargo.toml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,14 @@ spacetimedb-client-api-messages.workspace = true
2222
spacetimedb-commitlog.workspace = true
2323
spacetimedb-datastore.workspace = true
2424
spacetimedb-durability.workspace = true
25+
spacetimedb-engine.workspace = true
2526
spacetimedb-memory-usage.workspace = true
2627
spacetimedb-metrics.workspace = true
2728
spacetimedb-primitives.workspace = true
2829
spacetimedb-paths.workspace = true
2930
spacetimedb-physical-plan.workspace = true
3031
spacetimedb-query.workspace = true
31-
spacetimedb-runtime = { workspace = true, features = ["tokio"] }
32+
spacetimedb-runtime.workspace = true
3233
spacetimedb-sats = { workspace = true, features = ["serde"] }
3334
spacetimedb-schema.workspace = true
3435
spacetimedb-table.workspace = true
@@ -144,7 +145,7 @@ allow_loopback_http_for_tests = []
144145
# Enable timing for wasm ABI calls
145146
spacetimedb-wasm-instance-env-times = []
146147
# Enable test helpers and utils
147-
test = ["spacetimedb-commitlog/test", "spacetimedb-datastore/test"]
148+
test = ["spacetimedb-commitlog/test", "spacetimedb-datastore/test", "spacetimedb-engine/test"]
148149
# Perfmaps for profiling modules
149150
perfmap = []
150151
# Enables core pinning.
@@ -155,6 +156,7 @@ spacetimedb-lib = { path = "../lib", features = ["proptest", "test"] }
155156
spacetimedb-sats = { path = "../sats", features = ["proptest"] }
156157
spacetimedb-commitlog = { path = "../commitlog", features = ["test"] }
157158
spacetimedb-datastore = { path = "../datastore/", features = ["test"] }
159+
spacetimedb-engine = { workspace = true, features = ["test"] }
158160

159161
criterion.workspace = true
160162
# Also as dev-dependencies for use in _this_ crate's tests.

crates/core/src/database_logger.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -674,6 +674,12 @@ impl SystemLogger {
674674
}
675675
}
676676

677+
impl spacetimedb_engine::update::UpdateLogger for SystemLogger {
678+
fn info(&self, msg: &str) {
679+
self.info(msg);
680+
}
681+
}
682+
677683
#[cfg(test)]
678684
mod tests {
679685
use std::{ops::Range, sync::Arc};

crates/core/src/db/mod.rs

Lines changed: 27 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,28 @@
1-
use std::sync::Arc;
1+
pub mod persistence {
2+
pub use spacetimedb_engine::persistence::*;
3+
}
24

3-
use enum_map::EnumMap;
4-
use spacetimedb_schema::reducer_name::ReducerName;
5-
use tokio::sync::mpsc;
6-
use tokio::time::MissedTickBehavior;
5+
pub mod relational_db {
6+
pub use spacetimedb_engine::relational_db::*;
7+
}
78

8-
use crate::subscription::ExecutionCounters;
9-
use spacetimedb_datastore::execution_context::WorkloadType;
10-
use spacetimedb_datastore::{locking_tx_datastore::datastore::TxMetrics, traits::TxData};
9+
pub mod sql {
10+
pub mod ast {
11+
pub use spacetimedb_engine::sql::ast::*;
12+
}
1113

12-
mod durability;
13-
pub mod persistence;
14-
pub mod relational_db;
15-
pub mod snapshot;
16-
pub mod update;
14+
pub mod rls {
15+
pub use spacetimedb_engine::sql::rls::*;
16+
}
17+
}
18+
19+
pub mod snapshot {
20+
pub use spacetimedb_engine::snapshot::*;
21+
}
22+
23+
pub mod update {
24+
pub use spacetimedb_engine::update::*;
25+
}
1726

1827
/// Whether SpacetimeDB is run in memory, or persists objects and
1928
/// a message log to disk.
@@ -35,111 +44,10 @@ pub struct Config {
3544
pub page_pool_max_size: Option<usize>,
3645
}
3746

38-
/// A message that is processed by the [`spawn_metrics_recorder`] actor.
39-
/// We use a separate task to record metrics to avoid blocking transactions.
40-
pub struct MetricsMessage {
41-
/// The reducer the produced these metrics.
42-
reducer: Option<ReducerName>,
43-
/// Metrics from a mutable transaction.
44-
metrics_for_writer: Option<TxMetrics>,
45-
/// Metrics from a read-only transaction.
46-
/// A message may have metrics for both types of transactions,
47-
/// because metrics for a reducer and its subscription updates are recorded together.
48-
metrics_for_reader: Option<TxMetrics>,
49-
/// The row updates for an immutable transaction.
50-
/// Needed for insert and delete counters.
51-
tx_data: Option<Arc<TxData>>,
52-
/// Cached metrics counters for each workload type.
53-
counters: Arc<EnumMap<WorkloadType, ExecutionCounters>>,
54-
}
55-
56-
/// The handle used to send work to the tx metrics recorder.
57-
#[derive(Clone)]
58-
pub struct MetricsRecorderQueue {
59-
tx: mpsc::UnboundedSender<MetricsMessage>,
60-
}
61-
62-
impl MetricsRecorderQueue {
63-
pub fn send_metrics(
64-
&self,
65-
reducer: Option<ReducerName>,
66-
metrics_for_writer: Option<TxMetrics>,
67-
metrics_for_reader: Option<TxMetrics>,
68-
tx_data: Option<Arc<TxData>>,
69-
counters: Arc<EnumMap<WorkloadType, ExecutionCounters>>,
70-
) {
71-
if let Err(err) = self.tx.send(MetricsMessage {
72-
reducer,
73-
metrics_for_writer,
74-
metrics_for_reader,
75-
tx_data,
76-
counters,
77-
}) {
78-
log::warn!("failed to send metrics: {err}");
79-
}
80-
}
81-
}
82-
83-
fn record_metrics(
84-
MetricsMessage {
85-
reducer,
86-
metrics_for_writer,
87-
metrics_for_reader,
88-
tx_data,
89-
counters,
90-
}: MetricsMessage,
91-
) {
92-
if let Some(tx_metrics) = metrics_for_writer {
93-
tx_metrics.report(
94-
// If row updates are present,
95-
// they will always belong to the writer transaction.
96-
tx_data.as_deref(),
97-
reducer.as_ref(),
98-
|wl| &counters[wl],
99-
);
100-
}
101-
if let Some(tx_metrics) = metrics_for_reader {
102-
tx_metrics.report(
103-
// If row updates are present,
104-
// they will never belong to the reader transaction.
105-
// Passing row updates here will most likely panic.
106-
None,
107-
reducer.as_ref(),
108-
|wl| &counters[wl],
109-
);
110-
}
111-
}
112-
113-
/// The metrics recorder is a side channel that the main database thread forwards metrics to.
114-
/// While we want to avoid unnecessary compute on the critical path, communicating with other
115-
/// threads is not free, and for this case in particular waking a parked task is not free.
116-
///
117-
/// Previously, each tx would send its metrics to the recorder task. As soon as the recorder
118-
/// task `recv`d a message, it would update the counters and gauges, and immediately wait for
119-
/// the next tx's message. This meant that the tx would need to be more expensive than the
120-
/// recording of its metrics in order for the recorder task not to be parked on `recv` when
121-
/// the tx would `send` its metrics. This would obviously never be the case, and so each `send`
122-
/// would incur the overhead of waking the task.
123-
///
124-
/// To mitigate this, we now record metrics, for potentially many transactions, periodically
125-
/// every 5ms.
126-
const TX_METRICS_RECORDING_INTERVAL: std::time::Duration = std::time::Duration::from_millis(5);
127-
128-
/// Spawns a task for recording transaction metrics.
129-
/// Returns the handle for pushing metrics to the recorder.
130-
pub fn spawn_tx_metrics_recorder() -> (MetricsRecorderQueue, tokio::task::AbortHandle) {
131-
let (tx, mut rx) = mpsc::unbounded_channel();
132-
let abort_handle = tokio::spawn(async move {
133-
let mut interval = tokio::time::interval(TX_METRICS_RECORDING_INTERVAL);
134-
interval.set_missed_tick_behavior(MissedTickBehavior::Skip);
47+
pub type MetricsRecorderQueue = spacetimedb_engine::MetricsRecorderQueue;
13548

136-
loop {
137-
interval.tick().await;
138-
while let Ok(metrics) = rx.try_recv() {
139-
record_metrics(metrics);
140-
}
141-
}
142-
})
143-
.abort_handle();
144-
(MetricsRecorderQueue { tx }, abort_handle)
49+
pub fn spawn_tx_metrics_recorder(
50+
handle: &spacetimedb_runtime::Handle,
51+
) -> (MetricsRecorderQueue, spacetimedb_runtime::AbortHandle) {
52+
spacetimedb_engine::spawn_tx_metrics_recorder(handle)
14553
}

0 commit comments

Comments
 (0)