graphql-alt: scanning api bloom filter pipelines#24788
Merged
henryachen merged 21 commits intomainfrom Jan 30, 2026
Merged
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
amnn
reviewed
Jan 7, 2026
8 tasks
emmazzz
reviewed
Jan 13, 2026
amnn
reviewed
Jan 13, 2026
b1ce241 to
7a0f2d4
Compare
85986bb to
bcfadfe
Compare
Merged
8 tasks
henryachen
added a commit
that referenced
this pull request
Feb 6, 2026
## Description Adds two new indexer pipelines to support transaction scanning queries via bloom filters: cp_blooms concurrent pipeline: Per-checkpoint bloom filters - Indexes function calls, affected objects, senders, and recipients for each checkpoint - Folds bloom filter if it is sparse up to a certain min size or density - Stored in cp_blooms table with one row per checkpoint cp_bloom_blocks sequential pipeline: Blocked bloom filters spanning 1000 checkpoints - Splits each 256KB bloom into 128 separate smaller 2KB blocks - Stored in cp_bloom_blocks table with ~128 rows per cp_block (sparse, only non-zero blocks) - ORs bloom blocks on conflicts Schema changes: - cp_blooms: Per-checkpoint bloom filters - cp_bloom_blocks: Blocked bloom filters with (cp_block_id, bloom_block_index) primary key ## Test plan ``` cargo nextest run -p sui-indexer-alt cargo nextest run -p sui-indexer-alt-schema ``` ## Stack: #24788 👈 #24900 #25097 --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] gRPC: - [ ] JSON-RPC: - [x] GraphQL: add bloom filter pipelines for scanning APIs - [ ] CLI: - [ ] Rust SDK: - [ ] Indexing Framework:
henryachen
added a commit
that referenced
this pull request
Feb 6, 2026
## Description Adds two new indexer pipelines to support transaction scanning queries via bloom filters: cp_blooms concurrent pipeline: Per-checkpoint bloom filters - Indexes function calls, affected objects, senders, and recipients for each checkpoint - Folds bloom filter if it is sparse up to a certain min size or density - Stored in cp_blooms table with one row per checkpoint cp_bloom_blocks sequential pipeline: Blocked bloom filters spanning 1000 checkpoints - Splits each 256KB bloom into 128 separate smaller 2KB blocks - Stored in cp_bloom_blocks table with ~128 rows per cp_block (sparse, only non-zero blocks) - ORs bloom blocks on conflicts Schema changes: - cp_blooms: Per-checkpoint bloom filters - cp_bloom_blocks: Blocked bloom filters with (cp_block_id, bloom_block_index) primary key ## Test plan ``` cargo nextest run -p sui-indexer-alt cargo nextest run -p sui-indexer-alt-schema ``` ## Stack: #24788 👈 #24900 #25097 --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] gRPC: - [ ] JSON-RPC: - [x] GraphQL: add bloom filter pipelines for scanning APIs - [ ] CLI: - [ ] Rust SDK: - [ ] Indexing Framework:
henryachen
added a commit
that referenced
this pull request
Mar 4, 2026
## Description - Added scanEvent GraphQL query endpoint for scanning events using bloom filters - Reuse existing scan infrastructure (bloom querying, pagination) from Transaction Scanning - Added e2e tests covering filtering by sender, module, type, and pagination ## Test plan ``` cargo nextest run -p sui-indexer-alt-graphql cargo nextest run -p sui-indexer-alt-graphql -- schema_sdl cargo nextest run -p sui-indexer-alt-graphql --features staging -- schema_sdl cargo nextest run -p sui-indexer-alt-e2e-tests -- graphql/ ``` ## Stack: #24788 #24900 #25097 👈 --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] gRPC: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] Indexing Framework:
henryachen
added a commit
that referenced
this pull request
Mar 5, 2026
## Description - Added scanEvent GraphQL query endpoint for scanning events using bloom filters - Reuse existing scan infrastructure (bloom querying, pagination) from Transaction Scanning - Added e2e tests covering filtering by sender, module, type, and pagination ## Test plan ``` cargo nextest run -p sui-indexer-alt-graphql cargo nextest run -p sui-indexer-alt-graphql -- schema_sdl cargo nextest run -p sui-indexer-alt-graphql --features staging -- schema_sdl cargo nextest run -p sui-indexer-alt-e2e-tests -- graphql/ ``` ## Stack: #24788 #24900 #25097 👈 --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] gRPC: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] Indexing Framework:
henryachen
added a commit
that referenced
this pull request
Mar 5, 2026
## Description
Adds a new transactionsScan query that scans checkpoints using bloom
filters to find transactions matching filter criteria.
Changes
GraphQL (sui-indexer-alt-graphql)
- New transactionsScan query endpoint
- maxScanLimit service config to limit checkpoints scanned per query
- Transaction filter and scan logic in scan.rs
Schema (sui-indexer-alt-schema)
- `bloom_contains` SQL function for checking probe membership in bloom
filters
Reader (sui-indexer-alt-reader)
- `cp_blooms` loader for batch-loading bloom filter data
## Test plan
```
cargo nextest run -p sui-indexer-alt-graphql graphql_scan_limit_tests
cargo nextest run -p sui-indexer-alt-graphql
cargo nextest run -p sui-indexer-alt
cargo nextest run -p sui-indexer-alt-schema
```
Query Plan:
```
Limit (cost=1911.69..1358704.33 rows=62 width=8) (actual time=14.454..14.836 rows=62 loops=1)
Buffers: shared hit=7287
CTE block_bit_probes
-> ProjectSet (cost=0.00..8.20 rows=1635 width=18) (actual time=0.004..0.431 rows=1635 loops=1)
-> Result (cost=0.00..0.01 rows=1 width=0) (actual time=0.001..0.001 rows=1 loops=1)
-> Nested Loop (cost=1903.49..1358696.13 rows=62 width=8) (actual time=14.453..14.830 rows=62 loops=1)
Buffers: shared hit=7287
-> Limit (cost=1903.06..1903.22 rows=62 width=24) (actual time=14.411..14.414 rows=1 loops=1)
Buffers: shared hit=6976
CTE block_lookup
-> Nested Loop Left Join (cost=41.30..1723.38 rows=200 width=131) (actual time=0.654..2.899 rows=327 loops=1)
Buffers: shared hit=1348
-> HashAggregate (cost=40.88..42.88 rows=200 width=10) (actual time=0.620..0.682 rows=327 loops=1)
Group Key: block_bit_probes_1.cp_block_index, block_bit_probes_1.bloom_idx
Batches: 1 Memory Usage: 61kB
-> CTE Scan on block_bit_probes block_bit_probes_1 (cost=0.00..32.70 rows=1635 width=10) (actual time=0.000..0.159 rows=1635 loops=1)
-> Index Scan using cp_bloom_blocks_pkey on cp_bloom_blocks bb (cost=0.42..8.40 rows=1 width=131) (actual time=0.006..0.006 rows=1 loops=327)
Index Cond: ((cp_block_index = block_bit_probes_1.cp_block_index) AND (bloom_block_index = block_bit_probes_1.bloom_idx))
Buffers: shared hit=1343
-> Sort (cost=179.69..180.17 rows=192 width=24) (actual time=14.410..14.412 rows=1 loops=1)
Sort Key: ((block_bit_probes.cp_block_index * '1000'::bigint))
Sort Method: quicksort Memory: 25kB
Buffers: shared hit=6976
-> Hash Right Anti Join (cost=48.29..173.01 rows=192 width=24) (actual time=14.392..14.400 rows=5 loops=1)
Hash Cond: (p.cp_block_index = block_bit_probes.cp_block_index)
Buffers: shared hit=6976
-> Hash Join (cost=7.00..129.78 rows=8 width=8) (actual time=3.224..12.355 rows=1011 loops=1)
Hash Cond: ((p.cp_block_index = bl.cp_block_index) AND (p.bloom_idx = bl.bloom_idx))
Join Filter: ((bl.bloom_filter IS NULL) OR (p.bit_mask <> get_byte(bl.bloom_filter, (p.byte_pos % length(bl.bloom_filter)))))
Rows Removed by Join Filter: 624
Buffers: shared hit=6976
-> CTE Scan on block_bit_probes p (cost=0.00..32.70 rows=1635 width=18) (actual time=0.000..0.211 rows=1635 loops=1)
-> Hash (cost=4.00..4.00 rows=200 width=42) (actual time=3.142..3.142 rows=327 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 93kB
Buffers: shared hit=1348
-> CTE Scan on block_lookup bl (cost=0.00..4.00 rows=200 width=42) (actual time=0.656..3.060 rows=327 loops=1)
Buffers: shared hit=1348
-> Hash (cost=38.79..38.79 rows=200 width=8) (actual time=1.773..1.774 rows=327 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 21kB
-> HashAggregate (cost=36.79..38.79 rows=200 width=8) (actual time=1.668..1.717 rows=327 loops=1)
Group Key: block_bit_probes.cp_block_index
Batches: 1 Memory Usage: 61kB
-> CTE Scan on block_bit_probes (cost=0.00..32.70 rows=1635 width=8) (actual time=0.007..1.162 rows=1635 loops=1)
-> Index Scan using cp_blooms_pkey on cp_blooms cb (cost=0.43..21883.74 rows=1 width=8) (actual time=0.040..0.408 rows=62 loops=1)
Index Cond: ((cp_sequence_number >= ((block_bit_probes.cp_block_index * '1000'::bigint))) AND (cp_sequence_number <= ((((block_bit_probes.cp_block_index * '1000'::bigint) + '1000'::bigint) - 1))))
Filter: (((get_byte(bloom_filter, (208 % length(bloom_filter))) & 8) = 8) AND ((get_byte(bloom_filter, (5988 % length(bloom_filter))) & 32) = 32) AND ((get_byte(bloom_filter, (8084 % length(bloom_filter))) & 32) = 32) AND ((get_byte(bloom_filter, (15059 % length(bloom_filter))) & 32) = 32) AND ((get_byte(bloom_filter, (8330 % length(bloom_filter))) & 64) = 64) AND ((get_byte(bloom_filter, (10313 % length(bloom_filter))) & 4) = 4))
Rows Removed by Filter: 318
Buffers: shared hit=311
Planning:
Buffers: shared hit=78
Planning Time: 2.181 ms
Execution Time: 15.060 ms
```
## Stack:
#24788
#24900 👈
#25097
---
## Release notes
Check each box that your changes affect. If none of the boxes relate to
your changes, release notes aren't required.
For each box you select, include information after the relevant heading
that describes the impact of your changes that a user might notice and
any actions they must take to implement updates.
- [ ] Protocol:
- [ ] Nodes (Validators and Full nodes):
- [ ] gRPC:
- [ ] JSON-RPC:
- [ ] GraphQL:
- [ ] CLI:
- [ ] Rust SDK:
- [ ] Indexing Framework:
henryachen
added a commit
that referenced
this pull request
Mar 5, 2026
## Description
Adds a new transactionsScan query that scans checkpoints using bloom
filters to find transactions matching filter criteria.
Changes
GraphQL (sui-indexer-alt-graphql)
- New transactionsScan query endpoint
- maxScanLimit service config to limit checkpoints scanned per query
- Transaction filter and scan logic in scan.rs
Schema (sui-indexer-alt-schema)
- `bloom_contains` SQL function for checking probe membership in bloom
filters
Reader (sui-indexer-alt-reader)
- `cp_blooms` loader for batch-loading bloom filter data
## Test plan
```
cargo nextest run -p sui-indexer-alt-graphql graphql_scan_limit_tests
cargo nextest run -p sui-indexer-alt-graphql
cargo nextest run -p sui-indexer-alt
cargo nextest run -p sui-indexer-alt-schema
```
Query Plan:
```
Limit (cost=1911.69..1358704.33 rows=62 width=8) (actual time=14.454..14.836 rows=62 loops=1)
Buffers: shared hit=7287
CTE block_bit_probes
-> ProjectSet (cost=0.00..8.20 rows=1635 width=18) (actual time=0.004..0.431 rows=1635 loops=1)
-> Result (cost=0.00..0.01 rows=1 width=0) (actual time=0.001..0.001 rows=1 loops=1)
-> Nested Loop (cost=1903.49..1358696.13 rows=62 width=8) (actual time=14.453..14.830 rows=62 loops=1)
Buffers: shared hit=7287
-> Limit (cost=1903.06..1903.22 rows=62 width=24) (actual time=14.411..14.414 rows=1 loops=1)
Buffers: shared hit=6976
CTE block_lookup
-> Nested Loop Left Join (cost=41.30..1723.38 rows=200 width=131) (actual time=0.654..2.899 rows=327 loops=1)
Buffers: shared hit=1348
-> HashAggregate (cost=40.88..42.88 rows=200 width=10) (actual time=0.620..0.682 rows=327 loops=1)
Group Key: block_bit_probes_1.cp_block_index, block_bit_probes_1.bloom_idx
Batches: 1 Memory Usage: 61kB
-> CTE Scan on block_bit_probes block_bit_probes_1 (cost=0.00..32.70 rows=1635 width=10) (actual time=0.000..0.159 rows=1635 loops=1)
-> Index Scan using cp_bloom_blocks_pkey on cp_bloom_blocks bb (cost=0.42..8.40 rows=1 width=131) (actual time=0.006..0.006 rows=1 loops=327)
Index Cond: ((cp_block_index = block_bit_probes_1.cp_block_index) AND (bloom_block_index = block_bit_probes_1.bloom_idx))
Buffers: shared hit=1343
-> Sort (cost=179.69..180.17 rows=192 width=24) (actual time=14.410..14.412 rows=1 loops=1)
Sort Key: ((block_bit_probes.cp_block_index * '1000'::bigint))
Sort Method: quicksort Memory: 25kB
Buffers: shared hit=6976
-> Hash Right Anti Join (cost=48.29..173.01 rows=192 width=24) (actual time=14.392..14.400 rows=5 loops=1)
Hash Cond: (p.cp_block_index = block_bit_probes.cp_block_index)
Buffers: shared hit=6976
-> Hash Join (cost=7.00..129.78 rows=8 width=8) (actual time=3.224..12.355 rows=1011 loops=1)
Hash Cond: ((p.cp_block_index = bl.cp_block_index) AND (p.bloom_idx = bl.bloom_idx))
Join Filter: ((bl.bloom_filter IS NULL) OR (p.bit_mask <> get_byte(bl.bloom_filter, (p.byte_pos % length(bl.bloom_filter)))))
Rows Removed by Join Filter: 624
Buffers: shared hit=6976
-> CTE Scan on block_bit_probes p (cost=0.00..32.70 rows=1635 width=18) (actual time=0.000..0.211 rows=1635 loops=1)
-> Hash (cost=4.00..4.00 rows=200 width=42) (actual time=3.142..3.142 rows=327 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 93kB
Buffers: shared hit=1348
-> CTE Scan on block_lookup bl (cost=0.00..4.00 rows=200 width=42) (actual time=0.656..3.060 rows=327 loops=1)
Buffers: shared hit=1348
-> Hash (cost=38.79..38.79 rows=200 width=8) (actual time=1.773..1.774 rows=327 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 21kB
-> HashAggregate (cost=36.79..38.79 rows=200 width=8) (actual time=1.668..1.717 rows=327 loops=1)
Group Key: block_bit_probes.cp_block_index
Batches: 1 Memory Usage: 61kB
-> CTE Scan on block_bit_probes (cost=0.00..32.70 rows=1635 width=8) (actual time=0.007..1.162 rows=1635 loops=1)
-> Index Scan using cp_blooms_pkey on cp_blooms cb (cost=0.43..21883.74 rows=1 width=8) (actual time=0.040..0.408 rows=62 loops=1)
Index Cond: ((cp_sequence_number >= ((block_bit_probes.cp_block_index * '1000'::bigint))) AND (cp_sequence_number <= ((((block_bit_probes.cp_block_index * '1000'::bigint) + '1000'::bigint) - 1))))
Filter: (((get_byte(bloom_filter, (208 % length(bloom_filter))) & 8) = 8) AND ((get_byte(bloom_filter, (5988 % length(bloom_filter))) & 32) = 32) AND ((get_byte(bloom_filter, (8084 % length(bloom_filter))) & 32) = 32) AND ((get_byte(bloom_filter, (15059 % length(bloom_filter))) & 32) = 32) AND ((get_byte(bloom_filter, (8330 % length(bloom_filter))) & 64) = 64) AND ((get_byte(bloom_filter, (10313 % length(bloom_filter))) & 4) = 4))
Rows Removed by Filter: 318
Buffers: shared hit=311
Planning:
Buffers: shared hit=78
Planning Time: 2.181 ms
Execution Time: 15.060 ms
```
## Stack:
#24788
#24900 👈
#25097
---
## Release notes
Check each box that your changes affect. If none of the boxes relate to
your changes, release notes aren't required.
For each box you select, include information after the relevant heading
that describes the impact of your changes that a user might notice and
any actions they must take to implement updates.
- [ ] Protocol:
- [ ] Nodes (Validators and Full nodes):
- [ ] gRPC:
- [ ] JSON-RPC:
- [ ] GraphQL:
- [ ] CLI:
- [ ] Rust SDK:
- [ ] Indexing Framework:
mystenmark
pushed a commit
that referenced
this pull request
Mar 5, 2026
## Description
Adds a new transactionsScan query that scans checkpoints using bloom
filters to find transactions matching filter criteria.
Changes
GraphQL (sui-indexer-alt-graphql)
- New transactionsScan query endpoint
- maxScanLimit service config to limit checkpoints scanned per query
- Transaction filter and scan logic in scan.rs
Schema (sui-indexer-alt-schema)
- `bloom_contains` SQL function for checking probe membership in bloom
filters
Reader (sui-indexer-alt-reader)
- `cp_blooms` loader for batch-loading bloom filter data
## Test plan
```
cargo nextest run -p sui-indexer-alt-graphql graphql_scan_limit_tests
cargo nextest run -p sui-indexer-alt-graphql
cargo nextest run -p sui-indexer-alt
cargo nextest run -p sui-indexer-alt-schema
```
Query Plan:
```
Limit (cost=1911.69..1358704.33 rows=62 width=8) (actual time=14.454..14.836 rows=62 loops=1)
Buffers: shared hit=7287
CTE block_bit_probes
-> ProjectSet (cost=0.00..8.20 rows=1635 width=18) (actual time=0.004..0.431 rows=1635 loops=1)
-> Result (cost=0.00..0.01 rows=1 width=0) (actual time=0.001..0.001 rows=1 loops=1)
-> Nested Loop (cost=1903.49..1358696.13 rows=62 width=8) (actual time=14.453..14.830 rows=62 loops=1)
Buffers: shared hit=7287
-> Limit (cost=1903.06..1903.22 rows=62 width=24) (actual time=14.411..14.414 rows=1 loops=1)
Buffers: shared hit=6976
CTE block_lookup
-> Nested Loop Left Join (cost=41.30..1723.38 rows=200 width=131) (actual time=0.654..2.899 rows=327 loops=1)
Buffers: shared hit=1348
-> HashAggregate (cost=40.88..42.88 rows=200 width=10) (actual time=0.620..0.682 rows=327 loops=1)
Group Key: block_bit_probes_1.cp_block_index, block_bit_probes_1.bloom_idx
Batches: 1 Memory Usage: 61kB
-> CTE Scan on block_bit_probes block_bit_probes_1 (cost=0.00..32.70 rows=1635 width=10) (actual time=0.000..0.159 rows=1635 loops=1)
-> Index Scan using cp_bloom_blocks_pkey on cp_bloom_blocks bb (cost=0.42..8.40 rows=1 width=131) (actual time=0.006..0.006 rows=1 loops=327)
Index Cond: ((cp_block_index = block_bit_probes_1.cp_block_index) AND (bloom_block_index = block_bit_probes_1.bloom_idx))
Buffers: shared hit=1343
-> Sort (cost=179.69..180.17 rows=192 width=24) (actual time=14.410..14.412 rows=1 loops=1)
Sort Key: ((block_bit_probes.cp_block_index * '1000'::bigint))
Sort Method: quicksort Memory: 25kB
Buffers: shared hit=6976
-> Hash Right Anti Join (cost=48.29..173.01 rows=192 width=24) (actual time=14.392..14.400 rows=5 loops=1)
Hash Cond: (p.cp_block_index = block_bit_probes.cp_block_index)
Buffers: shared hit=6976
-> Hash Join (cost=7.00..129.78 rows=8 width=8) (actual time=3.224..12.355 rows=1011 loops=1)
Hash Cond: ((p.cp_block_index = bl.cp_block_index) AND (p.bloom_idx = bl.bloom_idx))
Join Filter: ((bl.bloom_filter IS NULL) OR (p.bit_mask <> get_byte(bl.bloom_filter, (p.byte_pos % length(bl.bloom_filter)))))
Rows Removed by Join Filter: 624
Buffers: shared hit=6976
-> CTE Scan on block_bit_probes p (cost=0.00..32.70 rows=1635 width=18) (actual time=0.000..0.211 rows=1635 loops=1)
-> Hash (cost=4.00..4.00 rows=200 width=42) (actual time=3.142..3.142 rows=327 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 93kB
Buffers: shared hit=1348
-> CTE Scan on block_lookup bl (cost=0.00..4.00 rows=200 width=42) (actual time=0.656..3.060 rows=327 loops=1)
Buffers: shared hit=1348
-> Hash (cost=38.79..38.79 rows=200 width=8) (actual time=1.773..1.774 rows=327 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 21kB
-> HashAggregate (cost=36.79..38.79 rows=200 width=8) (actual time=1.668..1.717 rows=327 loops=1)
Group Key: block_bit_probes.cp_block_index
Batches: 1 Memory Usage: 61kB
-> CTE Scan on block_bit_probes (cost=0.00..32.70 rows=1635 width=8) (actual time=0.007..1.162 rows=1635 loops=1)
-> Index Scan using cp_blooms_pkey on cp_blooms cb (cost=0.43..21883.74 rows=1 width=8) (actual time=0.040..0.408 rows=62 loops=1)
Index Cond: ((cp_sequence_number >= ((block_bit_probes.cp_block_index * '1000'::bigint))) AND (cp_sequence_number <= ((((block_bit_probes.cp_block_index * '1000'::bigint) + '1000'::bigint) - 1))))
Filter: (((get_byte(bloom_filter, (208 % length(bloom_filter))) & 8) = 8) AND ((get_byte(bloom_filter, (5988 % length(bloom_filter))) & 32) = 32) AND ((get_byte(bloom_filter, (8084 % length(bloom_filter))) & 32) = 32) AND ((get_byte(bloom_filter, (15059 % length(bloom_filter))) & 32) = 32) AND ((get_byte(bloom_filter, (8330 % length(bloom_filter))) & 64) = 64) AND ((get_byte(bloom_filter, (10313 % length(bloom_filter))) & 4) = 4))
Rows Removed by Filter: 318
Buffers: shared hit=311
Planning:
Buffers: shared hit=78
Planning Time: 2.181 ms
Execution Time: 15.060 ms
```
## Stack:
#24788
#24900 👈
#25097
---
## Release notes
Check each box that your changes affect. If none of the boxes relate to
your changes, release notes aren't required.
For each box you select, include information after the relevant heading
that describes the impact of your changes that a user might notice and
any actions they must take to implement updates.
- [ ] Protocol:
- [ ] Nodes (Validators and Full nodes):
- [ ] gRPC:
- [ ] JSON-RPC:
- [ ] GraphQL:
- [ ] CLI:
- [ ] Rust SDK:
- [ ] Indexing Framework:
jessiemongeon1
pushed a commit
to jessiemongeon1/sui
that referenced
this pull request
Mar 5, 2026
## Description
Adds a new transactionsScan query that scans checkpoints using bloom
filters to find transactions matching filter criteria.
Changes
GraphQL (sui-indexer-alt-graphql)
- New transactionsScan query endpoint
- maxScanLimit service config to limit checkpoints scanned per query
- Transaction filter and scan logic in scan.rs
Schema (sui-indexer-alt-schema)
- `bloom_contains` SQL function for checking probe membership in bloom
filters
Reader (sui-indexer-alt-reader)
- `cp_blooms` loader for batch-loading bloom filter data
## Test plan
```
cargo nextest run -p sui-indexer-alt-graphql graphql_scan_limit_tests
cargo nextest run -p sui-indexer-alt-graphql
cargo nextest run -p sui-indexer-alt
cargo nextest run -p sui-indexer-alt-schema
```
Query Plan:
```
Limit (cost=1911.69..1358704.33 rows=62 width=8) (actual time=14.454..14.836 rows=62 loops=1)
Buffers: shared hit=7287
CTE block_bit_probes
-> ProjectSet (cost=0.00..8.20 rows=1635 width=18) (actual time=0.004..0.431 rows=1635 loops=1)
-> Result (cost=0.00..0.01 rows=1 width=0) (actual time=0.001..0.001 rows=1 loops=1)
-> Nested Loop (cost=1903.49..1358696.13 rows=62 width=8) (actual time=14.453..14.830 rows=62 loops=1)
Buffers: shared hit=7287
-> Limit (cost=1903.06..1903.22 rows=62 width=24) (actual time=14.411..14.414 rows=1 loops=1)
Buffers: shared hit=6976
CTE block_lookup
-> Nested Loop Left Join (cost=41.30..1723.38 rows=200 width=131) (actual time=0.654..2.899 rows=327 loops=1)
Buffers: shared hit=1348
-> HashAggregate (cost=40.88..42.88 rows=200 width=10) (actual time=0.620..0.682 rows=327 loops=1)
Group Key: block_bit_probes_1.cp_block_index, block_bit_probes_1.bloom_idx
Batches: 1 Memory Usage: 61kB
-> CTE Scan on block_bit_probes block_bit_probes_1 (cost=0.00..32.70 rows=1635 width=10) (actual time=0.000..0.159 rows=1635 loops=1)
-> Index Scan using cp_bloom_blocks_pkey on cp_bloom_blocks bb (cost=0.42..8.40 rows=1 width=131) (actual time=0.006..0.006 rows=1 loops=327)
Index Cond: ((cp_block_index = block_bit_probes_1.cp_block_index) AND (bloom_block_index = block_bit_probes_1.bloom_idx))
Buffers: shared hit=1343
-> Sort (cost=179.69..180.17 rows=192 width=24) (actual time=14.410..14.412 rows=1 loops=1)
Sort Key: ((block_bit_probes.cp_block_index * '1000'::bigint))
Sort Method: quicksort Memory: 25kB
Buffers: shared hit=6976
-> Hash Right Anti Join (cost=48.29..173.01 rows=192 width=24) (actual time=14.392..14.400 rows=5 loops=1)
Hash Cond: (p.cp_block_index = block_bit_probes.cp_block_index)
Buffers: shared hit=6976
-> Hash Join (cost=7.00..129.78 rows=8 width=8) (actual time=3.224..12.355 rows=1011 loops=1)
Hash Cond: ((p.cp_block_index = bl.cp_block_index) AND (p.bloom_idx = bl.bloom_idx))
Join Filter: ((bl.bloom_filter IS NULL) OR (p.bit_mask <> get_byte(bl.bloom_filter, (p.byte_pos % length(bl.bloom_filter)))))
Rows Removed by Join Filter: 624
Buffers: shared hit=6976
-> CTE Scan on block_bit_probes p (cost=0.00..32.70 rows=1635 width=18) (actual time=0.000..0.211 rows=1635 loops=1)
-> Hash (cost=4.00..4.00 rows=200 width=42) (actual time=3.142..3.142 rows=327 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 93kB
Buffers: shared hit=1348
-> CTE Scan on block_lookup bl (cost=0.00..4.00 rows=200 width=42) (actual time=0.656..3.060 rows=327 loops=1)
Buffers: shared hit=1348
-> Hash (cost=38.79..38.79 rows=200 width=8) (actual time=1.773..1.774 rows=327 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 21kB
-> HashAggregate (cost=36.79..38.79 rows=200 width=8) (actual time=1.668..1.717 rows=327 loops=1)
Group Key: block_bit_probes.cp_block_index
Batches: 1 Memory Usage: 61kB
-> CTE Scan on block_bit_probes (cost=0.00..32.70 rows=1635 width=8) (actual time=0.007..1.162 rows=1635 loops=1)
-> Index Scan using cp_blooms_pkey on cp_blooms cb (cost=0.43..21883.74 rows=1 width=8) (actual time=0.040..0.408 rows=62 loops=1)
Index Cond: ((cp_sequence_number >= ((block_bit_probes.cp_block_index * '1000'::bigint))) AND (cp_sequence_number <= ((((block_bit_probes.cp_block_index * '1000'::bigint) + '1000'::bigint) - 1))))
Filter: (((get_byte(bloom_filter, (208 % length(bloom_filter))) & 8) = 8) AND ((get_byte(bloom_filter, (5988 % length(bloom_filter))) & 32) = 32) AND ((get_byte(bloom_filter, (8084 % length(bloom_filter))) & 32) = 32) AND ((get_byte(bloom_filter, (15059 % length(bloom_filter))) & 32) = 32) AND ((get_byte(bloom_filter, (8330 % length(bloom_filter))) & 64) = 64) AND ((get_byte(bloom_filter, (10313 % length(bloom_filter))) & 4) = 4))
Rows Removed by Filter: 318
Buffers: shared hit=311
Planning:
Buffers: shared hit=78
Planning Time: 2.181 ms
Execution Time: 15.060 ms
```
## Stack:
MystenLabs#24788
MystenLabs#24900 👈
MystenLabs#25097
---
## Release notes
Check each box that your changes affect. If none of the boxes relate to
your changes, release notes aren't required.
For each box you select, include information after the relevant heading
that describes the impact of your changes that a user might notice and
any actions they must take to implement updates.
- [ ] Protocol:
- [ ] Nodes (Validators and Full nodes):
- [ ] gRPC:
- [ ] JSON-RPC:
- [ ] GraphQL:
- [ ] CLI:
- [ ] Rust SDK:
- [ ] Indexing Framework:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Adds two new indexer pipelines to support transaction scanning queries via bloom filters:
cp_blooms concurrent pipeline: Per-checkpoint bloom filters
cp_bloom_blocks sequential pipeline: Blocked bloom filters spanning 1000 checkpoints
Schema changes:
Test plan
Stack:
#24788 👈
#24900
#25097
Release notes
Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required.
For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates.