Repository navigation
Conversation
…aligns with first record boundary
|
/gemini review |
There was a problem hiding this comment.
📋 Review Summary
This PR fixes a data-loss bug in _TruncatingFileHandle when a post-header restriction [start, stop) has its stop offset aligned with the first record boundary after start. The fix accurately distinguishes between a restriction ending within the header (stop <= self._header_len) and a post-header restriction whose initial skipped record lands directly on stop (offset == 0), ensuring the record starting at stop is read rather than skipped by both adjacent restrictions.
🔍 General Feedback
- The boundary condition analysis and fix in
sdks/python/apache_beam/dataframe/io.pyare sound and apply cleanly to both header-bearing (_TextFileSplitter) and headerless (_DelimSplitter) splittable reads. - The new unit test in
sdks/python/apache_beam/dataframe/io_test.pydeterministically exercises the[0, split),[split, first_record_end),[first_record_end, len(contents))split alignment that previously dropped a record.
💡 Tip: Comment /gemini review on this pull request to trigger a new Gemini code review.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #41163 +/- ##
============================================
+ Coverage 59.13% 59.14% +0.01%
Complexity 4894 4894
============================================
Files 1468 1461 -7
Lines 186188 186010 -178
Branches 3645 3645
============================================
- Hits 110096 110018 -78
+ Misses 72743 72643 -100
Partials 3349 3349
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
/gemini review |
|
To the reviewer: this one is subtle/took me a while to understand after AI discovered it - I tried to leave explanatory comments, but let me know if you need more context |
|
Assigning reviewers: R: @shunping for label python. Note: If you would like to opt out of this review, comment Available commands:
The PR bot will only process comments in the main thread (not review comments). |
Summary
Fixes a splittable CSV/text read bug in
_TruncatingFileHandle(sdks/python/apache_beam/dataframe/io.py) where a record is silently dropped when a restriction'sstopoffset lands on the exact record boundary immediately following itsstartoffset. This was exposed byCsvTest.test_non_utf8_csv_read_writeinPreCommit Python (3.14)(run 42817903097).Example Reproducing the Problem
By design,
_TruncatingFileHandlebreaks files at record delimiters (\n): any restriction withstart > len(header)skips fromstartto the first delimiter at or afterstart(because the previous restriction reads through that delimiter), and reads through the first delimiter at or afterstop.Consider a 30-byte CSV file with three 10-byte lines:
Suppose the file range
[0, 30)is split into three contiguous restrictions[0, 15),[15, 20), and[20, 30)(where15is mid-way through Record 1 and20is the exact boundary between Record 1 and Record 2):[0, 15):_buffer_start_pos = 10).offset = stop - _buffer_start_pos = 15 - 10 = 5 > 0, so it reads bytes[10, 15)(b"row1,") plusread_to_record_boundarythrough byte20(b"val1\n").b"row1,val1\n").[15, 20):__init__, becausestart = 15 > len(header) (10), it seeks to byte15and callsread_to_record_boundaryto skip the tail of Record 1 (b"val1\n"), advancing_buffer_start_posto20(the start of Record 2,b"row2,val2\n")._read,offset = stop - _buffer_start_pos = 20 - 20 = 0.if offset <= 0:treatedoffset == 0as empty and returnedself._empty.[20, 30):__init__, becausestart = 20 > len(header) (10), it seeks to byte20and callsread_to_record_boundaryto skip to the first delimiter after20— skipping all of Record 2 (b"row2,val2\n", bytes[20, 30)) and advancing_buffer_start_posto30.As a result, concatenating the three contiguous restrictions
[0, 15),[15, 20), and[20, 30)silently drops Record 2 (b"row2,val2\n"):Fix
In
_TruncatingFileHandle._read, only returnself._emptywhenoffset < 0orstop <= self._header_len. Whenstop > self._header_lenandoffset == 0(_buffer_start_pos == stop),_TruncatingFileHandlereads through the next record boundary from_buffer[0:], emitting the record starting atstopthat the subsequent restriction[stop, ...)skips.Verification
TruncatingFileHandleTest(sdks/python/apache_beam/dataframe/io_test.py) testing splits[0, split),[split, first_record_end),[first_record_end, len(contents)).beam_PreCommit_Python.ymlworkflow run on branch:42832034988