Skip to content

Commit de8b694

Browse files
storage: partition MySQL string PK snapshots by key prefix
Replace the OFFSET-walking boundary discovery with the prefix-based partitioner in mz-mysql-util, so discovery costs EXPLAIN index dives instead of an O(rows) index pass. Only string primary keys are supported. Integer keys, which the OFFSET walk used to sample, now fall back to a single-worker whole-table read. Prefixes of a numeric key do not order consistently with its values, so they would need a separate numeric range splitter. Boundaries are rendered as SQL literals via the server QUOTE() and still pass the existing strict-monotonicity verification in each read transaction. The new mysql_source_snapshot_partition_min_rows dyncfg (default 50000) stops splitting below a minimum range size. Test configs set it low so the tiny tables in mysql-cdc testdrive and parallel-workload still exercise range reads.
1 parent ecefb45 commit de8b694

5 files changed

Lines changed: 185 additions & 136 deletions

File tree

misc/python/materialize/mzcompose/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,11 @@ def get_variable_system_parameters(
298298
VariableSystemParameter(
299299
"mysql_source_snapshot_parallelism", "true", ["true", "false"]
300300
),
301+
# Low default so the tiny tables in tests still exercise PK-prefix
302+
# range splitting; the production default only splits large tables.
303+
VariableSystemParameter(
304+
"mysql_source_snapshot_partition_min_rows", "2", ["2", "50000"]
305+
),
301306
VariableSystemParameter(
302307
"persist_batch_columnar_format",
303308
"structured" if version > MzVersion.parse_mz("v0.135.0-dev") else "both_v2",
@@ -701,6 +706,9 @@ def get_default_system_parameters(
701706
# The estimated path is covered explicitly in mysql-cdc/statistics.td and
702707
# by parallel-workload.
703708
"mysql_source_snapshot_exact_count_max_rows",
709+
# Not varied here because the 256-prefix floor dominates for test-sized
710+
# tables. parallel-workload flips it.
711+
"mysql_source_snapshot_partition_probed_prefixes_per_billion_rows",
704712
"postgres_fetch_slot_resume_lsn_interval",
705713
"pg_schema_validation_interval",
706714
"pg_source_validate_timeline",

misc/python/materialize/parallel_workload/action.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3046,6 +3046,20 @@ def __init__(
30463046
self.flags_with_values["mysql_source_snapshot_parallelism"] = (
30473047
BOOLEAN_FLAG_VALUES
30483048
)
3049+
# 2 exercises PK-prefix splitting on workload-sized tables, the
3050+
# default leaves them in a single bucket.
3051+
self.flags_with_values["mysql_source_snapshot_partition_min_rows"] = [
3052+
"2",
3053+
"50000",
3054+
]
3055+
# 0 leaves only the 256-prefix floor, the default scales with table
3056+
# size.
3057+
self.flags_with_values[
3058+
"mysql_source_snapshot_partition_probed_prefixes_per_billion_rows"
3059+
] = [
3060+
"0",
3061+
"1000",
3062+
]
30493063

30503064
# If you are adding a new config flag in Materialize, consider using it
30513065
# here instead of just marking it as uninteresting to silence the

src/storage-types/src/dyncfgs.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,26 @@ pub static MYSQL_SOURCE_SNAPSHOT_PARALLELISM: Config<bool> = Config::new(
215215
"Whether to split MySQL snapshot reads across workers by primary-key ranges.",
216216
);
217217

218+
/// The smallest estimated row count for which the MySQL snapshot prefix
219+
/// partitioner keeps splitting a range; also the smallest table worth splitting.
220+
pub static MYSQL_SOURCE_SNAPSHOT_PARTITION_MIN_ROWS: Config<usize> = Config::new(
221+
"mysql_source_snapshot_partition_min_rows",
222+
50_000,
223+
"Minimum estimated rows per range before MySQL snapshot PK-prefix partitioning \
224+
stops splitting; also the smallest table considered worth splitting.",
225+
);
226+
227+
/// Probed-prefix budget for the MySQL snapshot prefix partitioner, scaled to
228+
/// the table's estimated size, with a small floor. When a table's budget runs
229+
/// out, splitting stops early and buckets come out coarser, never incorrect.
230+
pub static MYSQL_SOURCE_SNAPSHOT_PARTITION_PROBED_PREFIXES_PER_BILLION_ROWS: Config<usize> =
231+
Config::new(
232+
"mysql_source_snapshot_partition_probed_prefixes_per_billion_rows",
233+
1_000,
234+
"Cap on MySQL snapshot PK-prefix partitioning probed prefixes per table, per billion \
235+
estimated rows; when exhausted, splitting stops early with coarser buckets.",
236+
);
237+
218238
/// If the optimizer estimates the table has fewer rows than this, compute the exact row count
219239
/// with `COUNT(*)`. Otherwise, report the `information_schema` estimate directly.
220240
pub static MYSQL_SOURCE_SNAPSHOT_EXACT_COUNT_MAX_ROWS: Config<usize> = Config::new(
@@ -438,6 +458,8 @@ pub fn all_dyncfgs(configs: ConfigSet) -> ConfigSet {
438458
.add(&MYSQL_REPLICATION_HEARTBEAT_INTERVAL)
439459
.add(&MYSQL_SOURCE_SNAPSHOT_EXACT_COUNT_MAX_ROWS)
440460
.add(&MYSQL_SOURCE_SNAPSHOT_PARALLELISM)
461+
.add(&MYSQL_SOURCE_SNAPSHOT_PARTITION_MIN_ROWS)
462+
.add(&MYSQL_SOURCE_SNAPSHOT_PARTITION_PROBED_PREFIXES_PER_BILLION_ROWS)
441463
.add(&ORE_OVERFLOWING_BEHAVIOR)
442464
.add(&PG_FETCH_SLOT_RESUME_LSN_INTERVAL)
443465
.add(&PG_SCHEMA_VALIDATION_INTERVAL)

0 commit comments

Comments
 (0)