Skip to content

perf(scan): lazily decode manifest bucket entries - #212

Merged
SteNicholas merged 2 commits into
apache:mainfrom
gripleaf:feat-lazy-manifest-bucket-decode
Aug 20, 2026
Merged

perf(scan): lazily decode manifest bucket entries#212
SteNicholas merged 2 commits into
apache:mainfrom
gripleaf:feat-lazy-manifest-bucket-decode

Conversation

@gripleaf

@gripleaf gripleaf commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Purpose

Optimize snapshot live manifest cache rebuilding for bucket-filtered scans.

Previously, rebuilding a bucket-scoped cache entry deserialized every manifest entry before filtering by bucket. This change reads the serialization version and bucket first, and performs full ManifestEntry deserialization only for entries belonging to the target bucket.

The optimization is enabled by default and can be disabled with
scan.manifest-entry.lazy-decode.enabled.

This change also:

  • Preserves serialization-version validation before accessing version-dependent fields.
  • Reuses cached raw manifest bytes when reading bucket entries.
  • Ensures manifest readers are closed when batch processing exits early.
  • Retains the original full-deserialization path as a configurable fallback.

Tests

Added or updated unit tests covering:

  • Default and explicitly configured values of scan.manifest-entry.lazy-decode.enabled.
  • Manifest serialization-version validation.
  • Reading entries for individual buckets.
  • Reading a nonexistent bucket and returning an empty result.
  • Verifying that entries from non-target buckets are not fully deserialized.
  • Raw manifest byte-cache reuse across bucket reads.
  • Snapshot live manifest cache hits and cache-miss rebuilding.
  • Result equivalence when lazy decoding is disabled.
  • Compatibility with Java Paimon 0.9 and 1.1 Avro manifests.

All 8 selected unit tests passed.

API and Format

This change adds the following public configuration API:

  • Options::SCAN_MANIFEST_ENTRY_LAZY_DECODE_ENABLED
  • CoreOptions::ScanManifestEntryLazyDecodeEnabled()

The corresponding option key is
scan.manifest-entry.lazy-decode.enabled, and its default value is true.

This change does not modify the manifest storage format, serialization version, or external protocol.

Documentation

The new configuration option and affected internal APIs include English inline documentation.

No standalone user documentation is added because this option controls an internal scan optimization and does not change scan results.

Generative AI tooling

Generated-by: OpenAI Codex (GPT-5)

@lxy-9602 lxy-9602 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the contribution! I reviewed the code, and the overall design looks fine to me. While I’d like to better understand the practical benefit of this PR in production. For example, in a scenario where we scan 1k manifests and query a specific bucket, roughly how much scan latency reduction can we expect?

for (int64_t i = 0; i < batch->length(); i++) {
ColumnarRow row(batch->fields(), pool_, i);
PAIMON_RETURN_NOT_OK(ManifestEntrySerializer::ValidateVersion(row.GetInt(0)));
if (row.GetInt(3) != bucket) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a bucketGetter in ManifestEntrySerializer, similar to the Java implementation, to avoid using the raw index 3 directly.

Comment thread src/paimon/core/utils/objects_file.h Outdated
reader_builder_->Build(file_input_stream));
auto reader = std::make_unique<ManifestMetaReader>(std::move(batch_reader),
serializer_->GetDataType(), pool_);
ScopeGuard close_reader([&reader]() { reader->Close(); });

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We probably don’t need ScopeGuard here, since cleanup is already handled by ManifestMetaReader’s destructor via DoClose.

@gripleaf

Copy link
Copy Markdown
Contributor Author

Thank you for the contribution! I reviewed the code, and the overall design looks fine to me. While I’d like to better understand the practical benefit of this PR in production. For example, in a scenario where we scan 1k manifests and query a specific bucket, roughly how much scan latency reduction can we expect?

image This optimization mainly improves cold-start performance. In our case, we have around 16k buckets, but only need the manifest for a single bucket. The latency improvement is roughly comparable to the difference between P99 Phase 1 and Current Phase 2 in the figure, which represents about a 60% reduction in latency.

@lxy-9602

Copy link
Copy Markdown
Member

Got it! It does look like manifest deserialization takes a significant amount of time. Please address the review comments so the PR can move forward and be merged.

@lxy-9602 lxy-9602 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

@gripleaf
gripleaf force-pushed the feat-lazy-manifest-bucket-decode branch from a2fa79b to 59e2dd3 Compare August 20, 2026 07:34
@zjw1111

zjw1111 commented Aug 20, 2026

Copy link
Copy Markdown
Collaborator

Please link issue id and fix the wrong format of PR desc (lines after Validation commands:)

@gripleaf

Copy link
Copy Markdown
Contributor Author

Please link issue id and fix the wrong format of PR desc (lines after Validation commands:)

done

@SteNicholas SteNicholas left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

@SteNicholas
SteNicholas merged commit aa60634 into apache:main Aug 20, 2026
16 checks passed
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.

4 participants