refactor(bootstrap): cap refresh concurrency #534
Conversation
Cap the Settings-change refresh at 8 concurrent in-flight probes via `buffer_unordered`. Unbounded parallelism worked fine against local katana but would trip rate limits on hosted Starknet RPC providers for manifests above ~20 items. 8 is high enough that the common case (2–10 items) finishes in one batch with no measurable overhead, low enough to stay under typical free-tier 20 req/s caps. A compile-time assertion guards against a future refactor setting the constant to 0 (which would deadlock the refresh task, since `buffer_unordered(0)` never advances). Also retire both entries in TODOS.md: - Bounded concurrency: addressed here. - Account-change refresh trigger: the premise was wrong. The dirty flag fires on any Settings field edit, not just RPC URL, and `compute_deploy_address` already uses the current account — so unique=true deploys naturally re-probe at the new address when the user changes signer. Locked down with tests. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
There was a problem hiding this comment.
Benchmark
Details
| Benchmark suite | Current: a1b558d | Previous: 3db9b65 | Ratio |
|---|---|---|---|
CompiledClass(fixture)/compress |
2759042 ns/iter (± 15607) |
2574151 ns/iter (± 8127) |
1.07 |
CompiledClass(fixture)/decompress |
2873840 ns/iter (± 13908) |
2840607 ns/iter (± 12378) |
1.01 |
ExecutionCheckpoint/compress |
32 ns/iter (± 7) |
35 ns/iter (± 4) |
0.91 |
ExecutionCheckpoint/decompress |
25 ns/iter (± 7) |
27 ns/iter (± 9) |
0.93 |
PruningCheckpoint/compress |
32 ns/iter (± 8) |
35 ns/iter (± 2) |
0.91 |
PruningCheckpoint/decompress |
25 ns/iter (± 5) |
27 ns/iter (± 5) |
0.93 |
VersionedHeader/compress |
646 ns/iter (± 5) |
658 ns/iter (± 10) |
0.98 |
VersionedHeader/decompress |
815 ns/iter (± 6) |
866 ns/iter (± 17) |
0.94 |
StoredBlockBodyIndices/compress |
78 ns/iter (± 5) |
81 ns/iter (± 1) |
0.96 |
StoredBlockBodyIndices/decompress |
36 ns/iter (± 9) |
40 ns/iter (± 8) |
0.90 |
StorageEntry/compress |
145 ns/iter (± 2) |
183 ns/iter (± 2) |
0.79 |
StorageEntry/decompress |
145 ns/iter (± 5) |
155 ns/iter (± 6) |
0.94 |
ContractNonceChange/compress |
146 ns/iter (± 3) |
184 ns/iter (± 7) |
0.79 |
ContractNonceChange/decompress |
241 ns/iter (± 4) |
261 ns/iter (± 5) |
0.92 |
ContractClassChange/compress |
216 ns/iter (± 3) |
217 ns/iter (± 4) |
1.00 |
ContractClassChange/decompress |
250 ns/iter (± 4) |
281 ns/iter (± 5) |
0.89 |
ContractStorageEntry/compress |
162 ns/iter (± 3) |
170 ns/iter (± 2) |
0.95 |
ContractStorageEntry/decompress |
311 ns/iter (± 8) |
347 ns/iter (± 6) |
0.90 |
GenericContractInfo/compress |
136 ns/iter (± 5) |
139 ns/iter (± 3) |
0.98 |
GenericContractInfo/decompress |
106 ns/iter (± 9) |
111 ns/iter (± 4) |
0.95 |
Felt/compress |
82 ns/iter (± 3) |
91 ns/iter (± 12) |
0.90 |
Felt/decompress |
69 ns/iter (± 7) |
64 ns/iter (± 10) |
1.08 |
BlockHash/compress |
81 ns/iter (± 3) |
91 ns/iter (± 18) |
0.89 |
BlockHash/decompress |
58 ns/iter (± 5) |
61 ns/iter (± 13) |
0.95 |
TxHash/compress |
82 ns/iter (± 3) |
91 ns/iter (± 3) |
0.90 |
TxHash/decompress |
58 ns/iter (± 7) |
61 ns/iter (± 6) |
0.95 |
ClassHash/compress |
82 ns/iter (± 7) |
90 ns/iter (± 11) |
0.91 |
ClassHash/decompress |
61 ns/iter (± 5) |
61 ns/iter (± 7) |
1 |
CompiledClassHash/compress |
81 ns/iter (± 5) |
90 ns/iter (± 4) |
0.90 |
CompiledClassHash/decompress |
58 ns/iter (± 7) |
61 ns/iter (± 4) |
0.95 |
BlockNumber/compress |
47 ns/iter (± 3) |
51 ns/iter (± 3) |
0.92 |
BlockNumber/decompress |
25 ns/iter (± 4) |
26 ns/iter (± 1) |
0.96 |
TxNumber/compress |
47 ns/iter (± 2) |
51 ns/iter (± 2) |
0.92 |
TxNumber/decompress |
26 ns/iter (± 0) |
26 ns/iter (± 1) |
1 |
FinalityStatus/compress |
1 ns/iter (± 0) |
0 ns/iter (± 0) |
+∞ |
FinalityStatus/decompress |
12 ns/iter (± 2) |
13 ns/iter (± 0) |
0.92 |
TypedTransactionExecutionInfo/compress |
16247 ns/iter (± 90) |
14845 ns/iter (± 45) |
1.09 |
TypedTransactionExecutionInfo/decompress |
3612 ns/iter (± 107) |
3709 ns/iter (± 70) |
0.97 |
VersionedContractClass/compress |
367 ns/iter (± 5) |
362 ns/iter (± 6) |
1.01 |
VersionedContractClass/decompress |
766 ns/iter (± 20) |
814 ns/iter (± 3) |
0.94 |
MigratedCompiledClassHash/compress |
159 ns/iter (± 5) |
179 ns/iter (± 4) |
0.89 |
MigratedCompiledClassHash/decompress |
147 ns/iter (± 4) |
162 ns/iter (± 9) |
0.91 |
ContractInfoChangeList/compress |
1515 ns/iter (± 45) |
1524 ns/iter (± 78) |
0.99 |
ContractInfoChangeList/decompress |
2254 ns/iter (± 381) |
2395 ns/iter (± 422) |
0.94 |
BlockChangeList/compress |
667 ns/iter (± 36) |
653 ns/iter (± 101) |
1.02 |
BlockChangeList/decompress |
902 ns/iter (± 155) |
980 ns/iter (± 164) |
0.92 |
ReceiptEnvelope/compress |
30908 ns/iter (± 1922) |
28030 ns/iter (± 1145) |
1.10 |
ReceiptEnvelope/decompress |
6143 ns/iter (± 240) |
6431 ns/iter (± 268) |
0.96 |
TrieDatabaseValue/compress |
165 ns/iter (± 2) |
159 ns/iter (± 2) |
1.04 |
TrieDatabaseValue/decompress |
226 ns/iter (± 4) |
242 ns/iter (± 4) |
0.93 |
TrieHistoryEntry/compress |
289 ns/iter (± 2) |
292 ns/iter (± 2) |
0.99 |
TrieHistoryEntry/decompress |
262 ns/iter (± 11) |
280 ns/iter (± 12) |
0.94 |
This comment was automatically generated by workflow using github-action-benchmark.
There was a problem hiding this comment.
⚠️ Performance Alert ⚠️
Possible performance regression was detected for benchmark.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold 1.30.
| Benchmark suite | Current: a1b558d | Previous: 3db9b65 | Ratio |
|---|---|---|---|
FinalityStatus/compress |
1 ns/iter (± 0) |
0 ns/iter (± 0) |
+∞ |
This comment was automatically generated by workflow using github-action-benchmark.
CC: @kariy
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #534 +/- ##
==========================================
- Coverage 73.32% 66.68% -6.64%
==========================================
Files 209 308 +99
Lines 23132 42486 +19354
==========================================
+ Hits 16961 28333 +11372
- Misses 6171 14153 +7982 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Summary
futures::StreamExt::buffer_unordered. Unbounded parallelism worked against local katana but trips rate limits on hosted Starknet RPC providers for larger manifests. 8 is high enough that the common case (2–10 items) finishes in one batch with no measurable overhead, low enough to stay under typical free-tier 20 req/s caps.TODOS.md. Both entries are resolved:compute_deploy_addressalready uses the current account, sounique=truedeploys naturally re-probe at the new address when the signer changes. Locked down with tests.Details
REFRESH_CONCURRENCY: usize = 8constant intui.rs, with aconst _: () = assert!(REFRESH_CONCURRENCY >= 1)compile-time guard (becausebuffer_unordered(0)would deadlock).Vec<Pin<Box<dyn Future<Output = ()> + Send>>>so declare and deploy probes share one stream. The async blocks have distinct anonymous types otherwise.settings_dirty_fires_on_account_edit_not_just_rpc— locks down that editing the Account field sets the dirty flag.compute_deploy_address_uses_current_account_for_unique_deploys— confirms unique=true yields different addresses per caller, unique=false is caller-independent. This is the coupling that makes account-change refresh correct-by-construction.needs_refresh_includes_done_and_unknown_but_not_pending_or_failed(pre-existing, unchanged).Test plan
cargo nextest run -p katana-bootstrap— 98 passed, 0 failedcargo clippy -p katana-bootstrap --all-targets --no-deps— only pre-existing warnings remaincargo +nightly-2025-02-20 fmt --all🤖 Generated with Claude Code