Skip to content

Commit 22277d7

Browse files
meegooclaude
andcommitted
[BugFix] Don't advance batch-apply rssid offset for zero-segment op_writes
Row-mode partial update on lake PK produced duplicate PK rows: in the batch-apply path, MetaFileBuilder::add_rowset advanced _pending_rowset_data.assigned_segment_idx via get_rowset_id_step() on every op_write, including a leading ZERO-segment op_write (get_rowset_id_step returns 1 for an empty rowset). Because the pending rowset still had segment_metas_size()==0, the next segment-bearing op_write re-entered the first-call branch and stamped its first segment with a non-positional segment_idx (assigned_segment_idx + 0 = 1). At read the segment's effective rssid (rowset.id() + segment_idx) landed one slot above where the base-row delete vector was keyed at apply, so the superseded base rows were never hidden -> duplicate PKs. Only advance assigned_segment_idx for op_writes that actually deposit segments. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 2330827 commit 22277d7

1 file changed

Lines changed: 11 additions & 1 deletion

File tree

be/src/storage/lake/meta_file.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1498,7 +1498,17 @@ void MetaFileBuilder::add_rowset(const RowsetMetadataPB& rowset_pb, const std::m
14981498
_pending_rowset_data.dels.insert(_pending_rowset_data.dels.end(), dels.begin(), dels.end());
14991499

15001500
// Track cumulative rssid slots already assigned when batch applying multiple opwrites.
1501-
_pending_rowset_data.assigned_segment_idx += get_rowset_id_step(rowset_pb);
1501+
// FIX (row-mode partial-update duplicate PK regression): only advance the rssid offset for a
1502+
// contribution that actually deposited segments. get_rowset_id_step() returns 1 even for a
1503+
// 0-segment op_write, so a leading zero-segment op_write would bump assigned_segment_idx while
1504+
// leaving the pending rowset at segment_metas_size()==0; the next segment-bearing op_write then
1505+
// re-enters the first-call branch above and stamps its first segment with
1506+
// segment_idx = assigned_segment_idx(1) + 0 = 1 (non-positional). At read the segment's effective
1507+
// rssid = rowset.id() + segment_idx lands one slot above where the base-row delete vector was keyed
1508+
// at apply (the positional rssid), so the superseded base rows are never hidden -> duplicate PKs.
1509+
if (rowset_pb.segment_metas_size() > 0) {
1510+
_pending_rowset_data.assigned_segment_idx += get_rowset_id_step(rowset_pb);
1511+
}
15021512
}
15031513

15041514
Status MetaFileBuilder::set_final_rowset() {

0 commit comments

Comments
 (0)