Skip to content

Commit 7dce34c

Browse files
committed
[sql_server] refactor lock and snapshot tables one by one
1 parent 9c04b8d commit 7dce34c

File tree

9 files changed

+326
-318
lines changed

9 files changed

+326
-318
lines changed

src/sql-server-util/Cargo.toml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,6 @@ publish = false
99
[lints]
1010
workspace = true
1111

12-
[[example]]
13-
name = "cdc"
14-
1512
[dependencies]
1613
anyhow = "1.0.98"
1714
async-stream = "0.3.3"

src/sql-server-util/examples/cdc.rs

Lines changed: 0 additions & 120 deletions
This file was deleted.

src/sql-server-util/src/cdc.rs

Lines changed: 28 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@
6161
//! After completing the snapshot we use [`crate::inspect::get_changes_asc`] which will return
6262
//! all changes between a `[lower, upper)` bound of [`Lsn`]s.
6363
64-
use std::collections::{BTreeMap, BTreeSet};
64+
use std::collections::BTreeMap;
6565
use std::fmt;
6666
use std::sync::Arc;
6767
use std::time::Duration;
@@ -73,6 +73,7 @@ use proptest_derive::Arbitrary;
7373
use serde::{Deserialize, Serialize};
7474
use tiberius::numeric::Numeric;
7575

76+
use crate::desc::SqlServerTableRaw;
7677
use crate::{Client, SqlServerError, TransactionIsolationLevel};
7778

7879
/// A stream of changes from a table in SQL Server that has CDC enabled.
@@ -143,39 +144,21 @@ impl<'a> CdcStream<'a> {
143144
self
144145
}
145146

146-
/// Takes a snapshot of the upstream table that the specified `capture_instance` is
147-
/// replicating changes from.
148-
///
149-
/// An optional `instances` parameter can be provided to only snapshot the specified instances.
147+
/// Takes a snapshot of the upstream table.
150148
pub async fn snapshot<'b>(
151149
&'b mut self,
152-
instances: Option<BTreeSet<Arc<str>>>,
150+
table: &SqlServerTableRaw,
153151
worker_id: usize,
154152
source_id: GlobalId,
155153
) -> Result<
156154
(
157155
Lsn,
158-
BTreeMap<Arc<str>, usize>,
159-
impl Stream<Item = (Arc<str>, Result<tiberius::Row, SqlServerError>)> + use<'b, 'a>,
156+
usize,
157+
impl Stream<Item = Result<tiberius::Row, SqlServerError>>,
160158
),
161159
SqlServerError,
162160
> {
163161
static SAVEPOINT_NAME: &str = "_mz_snap_";
164-
165-
// Determine what table we need to snapshot.
166-
let instances = self
167-
.capture_instances
168-
.keys()
169-
.filter(|i| match instances.as_ref() {
170-
// Only snapshot the instance if the filter includes it.
171-
Some(filter) => filter.contains(i.as_ref()),
172-
None => true,
173-
})
174-
.map(|i| i.as_ref());
175-
let tables =
176-
crate::inspect::get_tables_for_capture_instance(self.client, instances).await?;
177-
tracing::info!(%source_id, ?tables, "timely-{worker_id} got table for capture instance");
178-
179162
// Before starting a transaction where the LSN will not advance, ensure
180163
// the upstream DB is ready for CDC.
181164
self.wait_for_ready().await?;
@@ -189,22 +172,15 @@ impl<'a> CdcStream<'a> {
189172
// as it will be just be locking the table(s).
190173
let mut fencing_client = self.client.new_connection().await?;
191174
let mut fence_txn = fencing_client.transaction().await?;
192-
193-
// TODO improve table locking: https://github.com/MaterializeInc/database-issues/issues/9512
194-
for (_capture_instance, schema, table) in &tables {
195-
tracing::trace!(%source_id, %schema, %table, "timely-{worker_id} locking table");
196-
fence_txn.lock_table_shared(&*schema, &*table).await?;
197-
}
198-
199-
// So we know that we locked that tables and roughly how long that took based on the time diff
200-
// from the last message.
201-
tracing::info!(%source_id, "timely-{worker_id} locked tables");
175+
fence_txn
176+
.lock_table_shared(&table.schema_name, &table.name)
177+
.await?;
178+
tracing::info!(%source_id, %table.schema_name, %table.name, "timely-{worker_id} locked table");
202179

203180
self.client
204181
.set_transaction_isolation(TransactionIsolationLevel::Snapshot)
205182
.await?;
206183
let mut txn = self.client.transaction().await?;
207-
208184
// Creating a savepoint forces a write to the transaction log, which will
209185
// assign an LSN, but it does not force a transaction sequence number to be
210186
// assigned as far as I can tell. I have not observed any entries added to
@@ -223,14 +199,14 @@ impl<'a> CdcStream<'a> {
223199
))?
224200
}
225201

226-
// Because the tables are locked, any write operation has either
202+
// Because the table is locked, any write operation has either
227203
// completed, or is blocked. The LSN and XSN acquired now will represent a
228204
// consistent point-in-time view, such that any committed write will be
229205
// visible to this snapshot and the LSN of such a write will be less than
230206
// or equal to the LSN captured here. Creating the savepoint sets the LSN,
231207
// we can read it after rolling back the locks.
232208
txn.create_savepoint(SAVEPOINT_NAME).await?;
233-
tracing::info!(%source_id, %SAVEPOINT_NAME, "timely-{worker_id} created savepoint");
209+
tracing::info!(%source_id, %table.schema_name, %table.name, %SAVEPOINT_NAME, "timely-{worker_id} created savepoint");
234210

235211
// Once the XSN is esablished and the LSN captured, the tables no longer
236212
// need to be locked. Any writes that happen to the upstream tables
@@ -241,42 +217,27 @@ impl<'a> CdcStream<'a> {
241217

242218
tracing::info!(%source_id, ?lsn, "timely-{worker_id} starting snapshot");
243219

244-
// Get the size of each table we're about to snapshot.
245-
//
246-
// TODO(sql_server3): To expose a more "generic" interface it would be nice to
247-
// make it configurable about whether or not we take a count first.
248-
let mut snapshot_stats = BTreeMap::default();
249-
for (capture_instance, schema, table) in &tables {
250-
tracing::trace!(%capture_instance, %schema, %table, "snapshot stats start");
251-
let size = crate::inspect::snapshot_size(txn.client, &*schema, &*table).await?;
252-
snapshot_stats.insert(Arc::clone(capture_instance), size);
253-
tracing::trace!(%source_id, %capture_instance, %schema, %table, "timely-{worker_id} snapshot stats end");
254-
}
255-
256-
// Run a `SELECT` query to snapshot the entire table.
257-
let stream = async_stream::stream! {
258-
// TODO(sql_server3): A stream of streams would be better here than
259-
// returning the name with each result, but the lifetimes are tricky.
260-
for (capture_instance, schema_name, table_name) in tables {
261-
tracing::trace!(%source_id, %capture_instance, %schema_name, %table_name, "timely-{worker_id} snapshot start");
262-
263-
let snapshot = crate::inspect::snapshot(txn.client, &*schema_name, &*table_name);
264-
let mut snapshot = std::pin::pin!(snapshot);
265-
while let Some(result) = snapshot.next().await {
266-
yield (Arc::clone(&capture_instance), result);
220+
tracing::trace!(%source_id, %table.capture_instance, %table.schema_name, %table.name, "timely-{worker_id} snapshot stats start");
221+
// Establish the consistent point-in-time and release the lock.
222+
let size =
223+
crate::inspect::snapshot_size(txn.client, &table.schema_name, &table.name).await?;
224+
tracing::trace!(%source_id, %table.capture_instance, %table.schema_name, %table.name, "timely-{worker_id} snapshot stats end");
225+
let schema_name = Arc::clone(&table.schema_name);
226+
let table_name = Arc::clone(&table.name);
227+
let rows = async_stream::try_stream! {
228+
{
229+
let snapshot_stream = crate::inspect::snapshot(txn.client, &*schema_name, &*table_name);
230+
tokio::pin!(snapshot_stream);
231+
232+
while let Some(row) = snapshot_stream.next().await {
233+
yield row?;
267234
}
268-
269-
tracing::trace!(%source_id, %capture_instance, %schema_name, %table_name, "timely-{worker_id} snapshot end");
270235
}
271236

272-
// Slightly awkward, but if the rollback fails we need to conform to
273-
// type of the stream.
274-
if let Err(e) = txn.rollback().await {
275-
yield ("rollback".into(), Err(e));
276-
}
237+
txn.rollback().await?
277238
};
278239

279-
Ok((lsn, snapshot_stats, stream))
240+
Ok((lsn, size, rows))
280241
}
281242

282243
/// Consume `self` returning a [`Stream`] of [`CdcEvent`]s.

0 commit comments

Comments
 (0)