Skip to content

[improve][metadata] Add streaming scanChildren to MetadataStore#25701

Merged
merlimat merged 1 commit intoapache:masterfrom
merlimat:mmerli/metadata-range-scan
May 7, 2026
Merged

[improve][metadata] Add streaming scanChildren to MetadataStore#25701
merlimat merged 1 commit intoapache:masterfrom
merlimat:mmerli/metadata-range-scan

Conversation

@merlimat
Copy link
Copy Markdown
Contributor

@merlimat merlimat commented May 6, 2026

Summary

Adds MetadataStore.scanChildren(parentPath, ScanConsumer consumer) — the value-bearing counterpart to getChildren. Same hierarchy semantics (direct children only, no descendants), but each record carries the value and Stat alongside the path.

Results are delivered to a ScanConsumer via onNext / onError / onCompleted so a large result set is never materialized as a single in-memory List.

Default implementation

getChildrenFromStore(parent) + sequential storeGet per child. Used by ZooKeeper / MockZooKeeper. One round trip per child.

Native overrides

  • LocalMemoryMetadataStoreNavigableMap.subMap with a direct-child filter.
  • RocksdbMetadataStoreRocksIterator.seek over the parent's prefix.
  • OxiaMetadataStoreclient.rangeScan over [parent + "/", parent + "//"), the same convention getChildrenFromStore uses with client.list.

Wrappers

  • DualMetadataStore routes by migration phase.
  • FaultInjectionMetadataStore adds OperationType.SCAN_CHILDREN for fault injection.

Motivation

Sub-PIP groundwork for PIP-471: the metadata-driven transaction components need to stream the children of a parent path together with their values (to enumerate ops for a transaction, ack records for a subscription, etc.) without paying for repeated round trips or for accumulating large result lists in broker memory.

Cache integration is a separate follow-up — sits more naturally on MetadataCache<T> (typed) than on MetadataStore (raw bytes).

Test plan

  • ./gradlew :pulsar-metadata:test --tests "org.apache.pulsar.metadata.MetadataStoreScanChildrenTest" — 4 cases × 5 backends (ZooKeeper, Memory, RocksDB, Oxia, MockZooKeeper) = 20 invocations, all passing.
  • Existing pulsar-metadata tests still pass.

Adds `MetadataStore.scanChildren(parentPath, ScanConsumer consumer)` —
the value-bearing counterpart to `getChildren`. Same hierarchy semantics
(direct children only, no descendants), but each record carries the value
and `Stat` alongside the path. Results are delivered via a `ScanConsumer`
callback (`onNext` / `onError` / `onCompleted`) so a large result set is
never materialized as a single in-memory `List`.

Default implementation (used by ZooKeeper / MockZooKeeper) lists children
with `getChildrenFromStore` and fetches each value sequentially — one extra
round trip per child. Native overrides:
- `LocalMemoryMetadataStore` — `NavigableMap.subMap` with a direct-child
  filter.
- `RocksdbMetadataStore` — `RocksIterator.seek` over the parent's prefix.
- `OxiaMetadataStore` — `client.rangeScan` over `[parent + "/", parent + "//")`,
  the same convention `getChildrenFromStore` uses with `client.list`.

Wrappers:
- `DualMetadataStore` routes by migration phase.
- `FaultInjectionMetadataStore` adds `OperationType.SCAN_CHILDREN`.

Tests run against all five backends via the existing `impl` data provider.
@merlimat merlimat force-pushed the mmerli/metadata-range-scan branch from 65ffd9a to b9f8cdf Compare May 6, 2026 22:37
@merlimat merlimat merged commit cd0ab9d into apache:master May 7, 2026
43 checks passed
@merlimat merlimat deleted the mmerli/metadata-range-scan branch May 7, 2026 04:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants