Skip to content

Commit 4f44fb3

Browse files
glasstigerclaude
andcommitted
Parse the delta header once per frame on send
trySendOne decoded a frame's delta header twice: the pre-send torn-dictionary guard called frameDeltaStart (magic/flags check + start-id varint), then post-send accumulateSentDict re-ran isDeltaFrame and re-read the start id before reading deltaCount. Both run on every delta frame on the I/O send path. Decode the start id once in the guard, hoist the frame address into a local, and pass the start id into accumulateSentDict, which now locates deltaCount just past the canonical start-id encoding (via NativeBufferWriter.varintSize) instead of re-parsing the header. The non-delta-frame case is carried by the same start id (-1), so the post- send mirror update runs exactly when it did before. Also move the accumulateSentDict javadoc onto accumulateSentDict: it had drifted above frameDeltaStart (which kept its own doc), leaving accumulateSentDict undocumented. The per-entry region walk (to size the mirror copy) remains; eliminating it needs a wire-level deltaBytes field, a server-side change out of scope for this client fix. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 754eb3d commit 4f44fb3

1 file changed

Lines changed: 29 additions & 22 deletions

File tree

core/src/main/java/io/questdb/client/cutlass/qwp/client/sf/cursor/CursorWebSocketSendLoop.java

Lines changed: 29 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1978,17 +1978,6 @@ private void setWireBaselineWithCatchUp(long replayStart) {
19781978
}
19791979
}
19801980

1981-
/**
1982-
* Copies the symbol-dictionary delta a just-sent frame carries into the
1983-
* loop-local mirror ({@link #sentDictBytesAddr}) so a future reconnect can
1984-
* re-register it. Frames are sent in FSN order carrying monotonically
1985-
* extending deltas, so a frame whose delta starts exactly at
1986-
* {@link #sentDictCount} extends the mirror; a replayed or empty-delta frame
1987-
* (nothing new) is skipped. Only ever called in delta mode.
1988-
*
1989-
* @param payloadAddr address of the QWP message (12-byte header first)
1990-
* @param payloadLen message length in bytes
1991-
*/
19921981
/**
19931982
* Returns the symbol-dictionary delta start id of a frame, or -1 when the
19941983
* frame carries no delta section. Used by the pre-send torn-dictionary guard.
@@ -2013,14 +2002,27 @@ private static boolean isDeltaFrame(long payloadAddr, int payloadLen) {
20132002
return (flags & QwpConstants.FLAG_DELTA_SYMBOL_DICT) != 0;
20142003
}
20152004

2016-
private void accumulateSentDict(long payloadAddr, int payloadLen) {
2005+
/**
2006+
* Copies the symbol-dictionary delta a just-sent frame carries into the
2007+
* loop-local mirror ({@link #sentDictBytesAddr}) so a future reconnect can
2008+
* re-register it. Frames are sent in FSN order carrying monotonically
2009+
* extending deltas, so a frame whose delta starts exactly at
2010+
* {@link #sentDictCount} extends the mirror; a replayed or empty-delta frame
2011+
* (nothing new) is skipped. Only ever called in delta mode, for a frame the
2012+
* pre-send guard already classified as a delta frame.
2013+
*
2014+
* @param payloadAddr address of the QWP message (12-byte header first)
2015+
* @param payloadLen message length in bytes
2016+
* @param deltaStart the frame's delta start id, already decoded by the
2017+
* pre-send guard ({@link #frameDeltaStart}) -- passed in so
2018+
* the magic/flags and start-id varint are not re-parsed
2019+
*/
2020+
private void accumulateSentDict(long payloadAddr, int payloadLen, int deltaStart) {
20172021
long limit = payloadAddr + payloadLen;
2018-
if (!isDeltaFrame(payloadAddr, payloadLen)) {
2019-
return;
2020-
}
2021-
long p = payloadAddr + QwpConstants.HEADER_SIZE;
2022-
long deltaStart = readVarintAt(p, limit);
2023-
p = varintEnd;
2022+
// deltaStart is known (the guard decoded it); locate deltaCount just past
2023+
// its canonical LEB128 encoding rather than re-reading the header and the
2024+
// start-id varint.
2025+
long p = payloadAddr + QwpConstants.HEADER_SIZE + NativeBufferWriter.varintSize(deltaStart);
20242026
long deltaCount = readVarintAt(p, limit);
20252027
p = varintEnd;
20262028
// Only a delta that extends exactly from our current tip introduces new
@@ -2291,6 +2293,11 @@ private boolean trySendOne() {
22912293
if (frameEnd > pub) {
22922294
return false; // payload not fully published yet
22932295
}
2296+
long frameAddr = base + sendOffset + MmapSegment.FRAME_HEADER_SIZE;
2297+
// -1 unless this is a delta frame; the guard decodes it once here and
2298+
// accumulateSentDict reuses it post-send, so the delta header is parsed
2299+
// once per frame rather than twice.
2300+
int deltaStart = -1;
22942301
if (deltaDictEnabled) {
22952302
// Torn-dictionary guard. In normal operation a delta frame's start id
22962303
// never exceeds the dictionary coverage established so far (replayed
@@ -2302,7 +2309,7 @@ private boolean trySendOne() {
23022309
// Sending the frame would corrupt the table (the server would null-pad
23032310
// the missing ids), so fail terminally instead; the unreplayable data
23042311
// must be resent.
2305-
int deltaStart = frameDeltaStart(base + sendOffset + MmapSegment.FRAME_HEADER_SIZE, payloadLen);
2312+
deltaStart = frameDeltaStart(frameAddr, payloadLen);
23062313
if (deltaStart > sentDictCount) {
23072314
recordFatal(new LineSenderException(
23082315
"recovered store-and-forward symbol dictionary is incomplete (likely a host crash): "
@@ -2312,16 +2319,16 @@ private boolean trySendOne() {
23122319
}
23132320
}
23142321
try {
2315-
client.sendBinary(base + sendOffset + MmapSegment.FRAME_HEADER_SIZE, payloadLen);
2322+
client.sendBinary(frameAddr, payloadLen);
23162323
} catch (Throwable t) {
23172324
fail(t);
23182325
return false;
23192326
}
2320-
if (deltaDictEnabled) {
2327+
if (deltaStart >= 0) {
23212328
// Mirror the symbols this frame introduced so a later reconnect can
23222329
// rebuild the whole dictionary. Idempotent on replay: a frame whose
23232330
// delta we already hold advances nothing.
2324-
accumulateSentDict(base + sendOffset + MmapSegment.FRAME_HEADER_SIZE, payloadLen);
2331+
accumulateSentDict(frameAddr, payloadLen, deltaStart);
23252332
}
23262333
lastFrameOrPingNanos = System.nanoTime();
23272334
sendOffset = frameEnd;

0 commit comments

Comments
 (0)