[adapter] Batch-allocate user IDs to reduce persist writes during DDL#35310
[adapter] Batch-allocate user IDs to reduce persist writes during DDL#35310mtabebe merged 1 commit intoMaterializeInc:mainfrom
Conversation
|
Thanks for opening this PR! Here are a few tips to help make the review process smooth for everyone. PR title guidelines
Pre-merge checklist
|
a0cba20 to
563d8af
Compare
| let (connection_id, connection_gid) = match self.allocate_user_id().await { | ||
| Ok(item_id) => item_id, | ||
| Err(err) => return ctx.retire(Err(err.into())), | ||
| Err(err) => return ctx.retire(Err(err)), |
There was a problem hiding this comment.
I got hit with a lint issue about this, so tidying it up
c3dab3b to
092af75
Compare
092af75 to
d3ee143
Compare
|
Nightly triggered: https://buildkite.com/materialize/nightly/builds/15452 |
e1bdcd0 to
6556509
Compare
|
bugbot run |
PR SummaryMedium Risk Overview Wires the pool through DDL call sites and updates tests. Replaces direct Written by Cursor Bugbot for commit 6556509. This will update automatically on new commits. Configure here. |
|
The only remaining CI failure is Zero downtime 3 (upsert-sources) where we fail because we can't reach ReadyToPromote due to consensus contention under what appears to be load (50 concurrent upsert sources). I think this is unrelated to the batch ID allocation changes. So I'd like to get this reviewed |
aljoscha
left a comment
There was a problem hiding this comment.
Very nice! Could we add some perf numbers to the PR / an Experiments section. Could be as simple as doing a DDL on an empty catalog before and after, a couple times, and we record that diff. Say before it takes 50ms, now it takes 40ms, or something like that.
| @@ -0,0 +1,125 @@ | |||
| # Copyright Materialize, Inc. and contributors. All rights reserved. | |||
There was a problem hiding this comment.
This test is fine, but I don't know if it adds much beyond the test coverage we already have. In case of doubt, leave it in, though!
Sure, it was roughly a 10% saving: from 160ms to 145 ms |
6556509 to
2a7fd52
Compare
Problem: Every DDL statement (CREATE TABLE, CREATE VIEW, etc.) performs a persist write + timestamp oracle call to allocate a single user ID. This represents one extra round trip to the timestamp oracle, in addition to the necessary catalog operation for DDL. Solution: - Add `IdPool` struct with `allocate()`, `allocate_many()`, and `refill()` methods to pre-allocate user IDs in a contiguous range - Add `USER_ID_POOL_BATCH_SIZE` dyncfg (default 512) to control batch size - Add `user_id_pool` field to `Coordinator` initialized as empty - Add Coordinator wrapper methods `allocate_user_id()` and `allocate_user_ids(count)` that draw from the pool and auto-refill from catalog when exhausted - Replace all call sites that used the two-line pattern of `get_catalog_write_ts()` + `catalog().allocate_user_id(ts)` with the new pooled methods - This eliminates the timestamp oracle call, and catalog operation Testing: - New `IdPool` unit tests covering empty pool, single/batch allocation, refill, mixed allocation, and invalid range panic - New SLT regression test (`test/sqllogictest/id_pool.slt`) exercising tables, views, materialized views, indexes, types, secrets, and drop/recreate cycles - New MZCompose restart test to ensure IDs remain unique across the restart - update all_checks to handle larger IDs (as restart tests may burn IDs) - update secret id check to use actual allocated id (as restart burns IDs) Perf: On local machine: 10% saving: from 160ms to 145 ms on a empty catalog
2a7fd52 to
82fff70
Compare
…ring DDL (MaterializeInc#35310)" This reverts commit 7cdb382.
…ring DDL (MaterializeInc#35310)" This reverts commit 7cdb382. Due to failures in: MaterializeInc/database-issues#11232
…MaterializeInc#35310) # Problem: Every DDL statement (CREATE TABLE, CREATE VIEW, etc.) performs a persist write + timestamp oracle call to allocate a single user ID. This represents one extra round trip to the timestamp oracle, in addition to the necessary catalog operation for DDL. # Solution: - Add `IdPool` struct with `allocate()`, `allocate_many()`, and `refill()` methods to pre-allocate user IDs in a contiguous range - Add `USER_ID_POOL_BATCH_SIZE` dyncfg (default 512) to control batch size - Add `user_id_pool` field to `Coordinator` initialized as empty - Add Coordinator wrapper methods `allocate_user_id()` and `allocate_user_ids(count)` that draw from the pool and auto-refill from catalog when exhausted - Replace all call sites that used the two-line pattern of `get_catalog_write_ts()` + `catalog().allocate_user_id(ts)` with the new pooled methods - This eliminates the timestamp oracle call, and catalog operation # Testing: - New `IdPool` unit tests covering empty pool, single/batch allocation, refill, mixed allocation, and invalid range panic - New SLT regression test (`test/sqllogictest/id_pool.slt`) exercising tables, views, materialized views, indexes, types, secrets, and drop/recreate cycles - New MZCompose restart test to ensure IDs remain unique across the restart # Perf On local machine: 10% saving: from 160ms to 145 ms on a empty catalog Closes SQL-115
…#35460) [adapter] Batch-allocate user IDs to reduce persist writes during DDL (#35310) # Problem: Every DDL statement (CREATE TABLE, CREATE VIEW, etc.) performs a persist write + timestamp oracle call to allocate a single user ID. This represents one extra round trip to the timestamp oracle, in addition to the necessary catalog operation for DDL. # Solution: - Add `IdPool` struct with `allocate()`, `allocate_many()`, and `refill()` methods to pre-allocate user IDs in a contiguous range - Add `USER_ID_POOL_BATCH_SIZE` dyncfg (default 512) to control batch size - Add `user_id_pool` field to `Coordinator` initialized as empty - Add Coordinator wrapper methods `allocate_user_id()` and `allocate_user_ids(count)` that draw from the pool and auto-refill from catalog when exhausted - Replace all call sites that used the two-line pattern of `get_catalog_write_ts()` + `catalog().allocate_user_id(ts)` with the new pooled methods - This eliminates the timestamp oracle call, and catalog operation #Sub Problem/Solution In the preflight phase of a zero down time upgrade, get_next_ids() is used to determine if there has been ongoing DDL. Since the IdPool batch-allocates IDs, the counter can advance far ahead of actually-created items. New objects created from the pool then had IDs below the captured baseline and were missed by DDL detection, so the standby could fail to see new tables/views. So IDs in the catalog (transaction get_items / get_cluster_replicas) instead of the allocator counter. And compute next user item ID and next user replica ID from the max existing # Testing: - New `IdPool` unit tests covering empty pool, single/batch allocation, refill, mixed allocation, and invalid range panic - New SLT regression test (`test/sqllogictest/id_pool.slt`) exercising tables, views, materialized views, indexes, types, secrets, and drop/recreate cycles - New MZCompose restart test to ensure IDs remain unique across the restart - New cargo test: mz-catalog test_persist_ddl_detection_with_batch_allocated_ids - New MZ Compose test: workflow_ddl_detection_with_id_pool # Perf On local machine: 10% saving: from 160ms to 145 ms on a empty catalog
Problem:
Every DDL statement (CREATE TABLE, CREATE VIEW, etc.) performs a persist write + timestamp oracle call to allocate a single user ID. This represents one extra round trip to the timestamp oracle, in addition to the necessary catalog operation for DDL.
Solution:
IdPoolstruct withallocate(),allocate_many(), andrefill()methods to pre-allocate user IDs in a contiguous rangeUSER_ID_POOL_BATCH_SIZEdyncfg (default 512) to control batch sizeuser_id_poolfield toCoordinatorinitialized as emptyallocate_user_id()andallocate_user_ids(count)that draw from the pool and auto-refill from catalog when exhaustedget_catalog_write_ts()+catalog().allocate_user_id(ts)with the new pooled methodsTesting:
IdPoolunit tests covering empty pool, single/batch allocation, refill, mixed allocation, and invalid range panictest/sqllogictest/id_pool.slt) exercising tables, views, materialized views, indexes, types, secrets, and drop/recreate cyclesCloses SQL-115