Skip to content

Use FragmentSlice for zone-level zonemap pruning #806

Description

@majin1102

Background

lance-spark currently loads zonemap statistics on the driver, but pruning only retains matching fragment IDs. The zoneStart and zoneLength information is discarded, so executors still scan the entire surviving fragment.

Lance draft PR lance-format/lance#8951 adds Java/JNI FragmentSlice support:

new FragmentSlice(fragmentId, rowOffset, rowCount)

A slice represents the physical row range [rowOffset, rowOffset + rowCount) inside a fragment. Native Lance converts it to the dataset _rowid domain and executes it through the existing row-address prefilter path.

This issue tracks the lance-spark integration after that Lance PR is merged and released.

Related umbrella issue: #34.

Proposed changes

  • Preserve matching zonemap ranges instead of reducing them to fragment IDs.
  • Convert candidate zones to FragmentSlice values and merge adjacent or overlapping slices in the same fragment.
  • Extend LanceSplit / LanceInputPartition to carry optional fragment slices.
  • Keep one fragment per Spark input partition in the first implementation.
  • Pass slices to both:
    • the ordinary columnar fragment reader;
    • the pushed-down COUNT(*) reader.
  • Continue applying the complete pushed filter as a residual predicate.
  • Keep using the dataset version pinned during driver planning.

Predicate semantics

Slice pruning must remain conservative:

  • predicates pushed separately by Spark are combined with AND;
  • AND intersects candidate physical ranges;
  • OR unions ranges only when both branches can be analyzed;
  • NOT, unsupported expressions, type mismatches, or missing zonemap coverage fall back to scanning the complete affected fragment;
  • deleted rows do not compact physical offsets and are removed by the normal Lance scan path.

Initial non-goals

  • Generic Scanner::plan_splits.
  • Distributed BTree/Bitmap/FTS segment execution.
  • Packing slices from multiple fragments into one Spark partition.
  • Replacing the residual filter with zonemap evaluation.
  • Exposing row addresses to Java or Spark.

Acceptance criteria

  • A predicate matching only part of a fragment scans the corresponding physical slices.
  • Multiple disjoint, adjacent, and overlapping zones are handled correctly.
  • Deleted rows do not shift slice offsets.
  • Multi-column AND and supported OR predicates produce correct ranges.
  • Unsupported predicates and incomplete index coverage conservatively retain data.
  • Ordinary reads and pushed-down COUNT(*) return identical results.
  • Projection, filter, limit, _rowaddr, and blob-column paths remain correct.
  • Executors use the same pinned dataset snapshot used for driver planning.
  • Metrics or tests demonstrate that fewer physical rows/ranges are planned, not only that the final result is correct.

Dependency

Blocked by the merge and Java artifact release of:

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions