Skip to content

[rust] make scanning in order configurable - #528

Merged
changhiskhan merged 1 commit into
mainfrom
changhiskhan/limit-ordered
Feb 6, 2023
Merged

[rust] make scanning in order configurable#528
changhiskhan merged 1 commit into
mainfrom
changhiskhan/limit-ordered

Conversation

@changhiskhan

Copy link
Copy Markdown
Contributor

Regular table scan go in order. KNN scan can be unordered

@changhiskhan
changhiskhan requested a review from eddyxu February 6, 2023 03:06
@changhiskhan
changhiskhan force-pushed the changhiskhan/limit-ordered branch from 00aae52 to 9ce95db Compare February 6, 2023 03:09
Comment thread rust/src/io/exec/scan.rs Outdated
let stream = stream::iter(0..reader.num_batches())
.map(|batch_id| async move { r.read_batch(batch_id as i32, ..).await });

if scan_ordered {

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.

let stream = if scan_order { 
  stream.buffered(...)
} else {
   stream.buffer_unorderred(...)
}

stream.try_for_each(....) 

Simplify a bit?

Comment thread rust/src/dataset/scanner.rs Outdated
manifest.clone(),
PREFECTH_SIZE,
true,
false,

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.

I am not sure that how much difference it is before ordered / unordered at the moment. Should we just implement ordered scan, if later we find that there are meat here to change to unordered, by that time, we add this flag later?

@changhiskhan
changhiskhan force-pushed the changhiskhan/limit-ordered branch from 9ce95db to 207f2b9 Compare February 6, 2023 04:00
@changhiskhan
changhiskhan merged commit 3c70086 into main Feb 6, 2023
@changhiskhan
changhiskhan deleted the changhiskhan/limit-ordered branch February 6, 2023 04:46
Xuanwo pushed a commit that referenced this pull request Aug 10, 2026
…oto (#7432)

Fixes: #7080

## Summary

Follow-up to #6650. The `updated_fragment_offsets` field (proto field 9)
stores per-fragment
matched row offsets as `map<uint64, UInt32List>` -- one uint32 per
matched row. For dense
rewrites this produces multi-GB manifests (e.g. 86k matched rows x 4
bytes x many fragments).

This PR adds proto field 10 (`map<uint64, bytes>`) using portable
RoaringBitmap serialization,
which typically compresses the same data to tens of bytes per fragment.
Writers emit field 10
only; readers prefer field 10, falling back to field 9 for manifests
written before this change.


## Background

PR #6650 added `updated_fragment_offsets` to the `Update` transaction
message so that
`build_manifest` can partially refresh `_row_last_updated_at_version`
for matched rows only.
The encoding choice -- one uint32 per offset in a `UInt32List` -- was
flagged post-merge as a
size regression for dense updates. The offsets are already stored
internally as `RoaringBitmap`;
this PR aligns the proto encoding with that representation.


## Changes

### protos/transaction.proto
- Deprecate field 9 (`map<uint64, UInt32List> updated_fragment_offsets`)
with a comment
  pointing to field 10.
- Add field 10: `map<uint64, bytes> updated_fragment_offset_bitmaps`
with documentation of
  the dual-read strategy.

### rust/lance/src/dataset/transaction.rs

Serialization (`From<&Transaction> for pb::Transaction`):
- Write field 10 only: `RoaringBitmap::serialize_into` produces portable
bytes for each
  fragment's bitmap.
- Set field 9 to an empty `HashMap` (forward compat; old readers ignore
unknown fields).

Deserialization (`TryFrom<pb::Transaction> for Transaction`):
- If field 10 is non-empty: deserialize each entry with
`RoaringBitmap::deserialize_from`.
- Else if field 9 is non-empty: convert each `UInt32List` to
`RoaringBitmap::from_iter`
  (legacy fallback).
- Same `if !new_field.is_empty() { ... } else { ... }` pattern used by
the existing
  `Rewrite.groups` / `Rewrite.old_fragments` migration.

Invalid field 10 bytes fail deserialize with `Error::invalid_input`.

In-memory type unchanged: `UpdatedFragmentOffsets(HashMap<u64,
RoaringBitmap>)`.


## Test plan

- `test_proto_round_trip_field_10` -- write a transaction with field 10,
read back, verify
  offsets match for two fragments.
- `test_proto_legacy_field_9_read` -- construct a proto with only field
9 populated
(simulating an old writer), deserialize, verify offsets are correctly
recovered.
- `test_proto_field_10_takes_precedence_over_field_9` -- when both
fields are present,
  field 10 values are used and field 9 is ignored.


Proto wire format change; team vote may be needed.


## Backward compatibility

- Proto field numbers: field 9 is kept (deprecated, not removed). Field
10 is new. No field
  number reuse.
- Old readers: ignore unknown field 10; they only read field 9, which is
now empty on new
commits. Old Lance versions deserializing commits written by this PR
will not recover
offsets from the txn; that only affects audit/`readTransaction()` on
historical
  commits, not table data or OCC.
- New readers: prefer field 10; fall back to field 9 for manifests
written by older Lance
  versions that predate this change.
- No JNI or Java changes. The in-memory type (`UpdatedFragmentOffsets`)
is unchanged; only
  the proto wire encoding changes.

Independent of #6748 and lance-spark #528 (JNI wiring). No mutual merge
dependencies.

Co-authored-by: Jing chen He <jingh@adobe.com>
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