Skip to content

Keep a partial protocol v5 segment header across reads - #1308

Open
fruch wants to merge 1 commit into
apache:trunkfrom
fruch:apache-v5-partial-segment-header
Open

fruch wants to merge 1 commit into
apache:trunkfrom
fruch:apache-v5-partial-segment-header

Conversation

@fruch

@fruch fruch commented Oct 22, 2026

Copy link
Copy Markdown

With protocol v5, a read that ends 1-5 bytes into a segment header loses those bytes, and the connection is then defuncted with CRC mismatch on header.

Connection._process_segment_buffer() has three cases:

  1. A whole segment is buffered: decode it.
  2. A full header but an incomplete payload is buffered: rewind with seek(0) and wait for more.
  3. Less than one header (6 bytes) is buffered: return without rewinding.

process_io_buffer() then calls reset_io_buffer(), which keeps only the bytes after the current position. In case 3 the position is at the end, so the partial header is discarded. The next read is parsed from the wrong offset and its header fails the CRC24 check:

cassandra.connection.ConnectionShutdown: CRC mismatch on header 85184f. Received 1f0000", computed 9e9992.

This PR adds the missing seek(0) to case 3.

It needs a socket read to end inside a header, so it's rare. It's more likely with the asyncio reactor, which processes the buffer after every 4 KB read. We hit it in CI against Cassandra 5.0 with asyncio, during a large schema refresh, while running the integration tests of the ScyllaDB fork of this driver, which has the same code (scylladb#1141).

Test: the new unit test test_v5_segment_header_split_across_reads feeds two segments split at every offset from 0 to 7 bytes into the second header. On trunk, offsets 1-5 fail. With the fix, all pass, and tests/unit/test_connection.py and tests/unit/test_segment.py pass.

🤖 Generated with Claude Code

With protocol v5 framing, _process_segment_buffer() returned without
rewinding the io buffer when fewer than 6 bytes (one segment header plus
its CRC24) had arrived. process_io_buffer() then called
reset_io_buffer(), which keeps only the bytes after the current
position, and the position was at the end, so those 1-5 header bytes
were dropped. The next read was parsed from the wrong offset and the
connection was defuncted with "CRC mismatch on header".

Rewind in that branch too, like the "header but incomplete payload"
branch already does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
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.

1 participant