Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
de86197
Delta-encode the QWP symbol dictionary
glasstiger Jul 9, 2026
3092230
Contain QWP dict catch-up send failures in the reconnect loop
glasstiger Jul 9, 2026
9f3f7bd
Resume the QWP symbol-dict write-ahead from the durable size
glasstiger Jul 9, 2026
09cd706
Guard the oversized catch-up entry terminal
glasstiger Jul 9, 2026
18dd545
Persist the QWP symbol-dict batch in a single write
glasstiger Jul 9, 2026
2a1a2d6
Free the recovery-seeded dict mirror in close()
glasstiger Jul 9, 2026
e130da9
Pin the recovery test on the catch-up frame
glasstiger Jul 9, 2026
2b78b0e
Cover the disk-mode full-dict fallback
glasstiger Jul 9, 2026
282ac09
Truncate the torn tail when reopening the symbol dict
glasstiger Jul 9, 2026
754eb3d
Guard the sent-dict mirror against int overflow
glasstiger Jul 9, 2026
01f251d
Merge branch 'main' into qwp-delta-symbol-dict
bluestreak01 Jul 9, 2026
4f44fb3
Parse the delta header once per frame on send
glasstiger Jul 9, 2026
0f9d88f
Fix stale self-sufficient-frame comments
glasstiger Jul 9, 2026
d89f14f
Make the delta-dict tests deterministic and leak-checked
glasstiger Jul 9, 2026
27aad9e
Cover the split-batch delta contract
glasstiger Jul 9, 2026
034d8f3
Accumulate the tail of a partial-overlap delta into the mirror
glasstiger Jul 9, 2026
41fddb1
Guard against persisted-dict duplication on a failed publish
glasstiger Jul 9, 2026
f9430bc
Merge branch 'main' into qwp-delta-symbol-dict
glasstiger Jul 9, 2026
9602440
Merge branch 'qwp-delta-symbol-dict' of https://github.com/questdb/ja…
glasstiger Jul 9, 2026
0ef96af
Cover the reconnect catch-up ACK alignment
glasstiger Jul 9, 2026
ff79b72
Cover the retriable catch-up send containment
glasstiger Jul 9, 2026
8fd9ad1
Tidy catch-up comments and harden two edges
glasstiger Jul 9, 2026
8fcd835
Fix delta symbol-dict recovery and reconnect bugs
glasstiger Jul 10, 2026
fc8b4ba
Adopt recovered dictionary into the send mirror
glasstiger Jul 10, 2026
8dd7723
Harden delta symbol-dict recovery and NACK gating
glasstiger Jul 10, 2026
8853d8f
Persist symbol delta from frame, skip re-encode
glasstiger Jul 10, 2026
10a125c
Guard catch-up frame overflow, add biting tests
glasstiger Jul 10, 2026
fa50e81
Fix orphan-drain losing symbol-dict mirror seed
glasstiger Jul 10, 2026
444199e
Fix symbol-dict durability doc; add recovery test
glasstiger Jul 10, 2026
7a95653
Discard a surviving symbol dict on fresh start
glasstiger Jul 10, 2026
6d14727
Harden delta symbol-dict send and persist paths
glasstiger Jul 10, 2026
fdd7141
Tidy delta symbol-dict dead code and edges
glasstiger Jul 10, 2026
2fdbbf9
Fix catch-up terminal on a homogeneous batch cap
glasstiger Jul 10, 2026
8cfd7bd
Fix recovery id desync on UTF-8-colliding symbols
glasstiger Jul 10, 2026
0b4ab3f
Harden symbol-dict resource teardown paths
glasstiger Jul 10, 2026
186ae10
Defer catch-up commit; fix stale delta comments
glasstiger Jul 10, 2026
fef7203
Plug ensureConnected leak; consolidate varint helpers
glasstiger Jul 10, 2026
cbaf474
Add a per-entry CRC to the SF symbol dictionary
glasstiger Jul 10, 2026
6cc1307
Document split-flush and dict-getter contracts
glasstiger Jul 10, 2026
c065ad4
Stop split-flush stranding a deferred prefix
glasstiger Jul 10, 2026
40c6a28
Harden persisted symbol-dict edge paths
glasstiger Jul 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,37 @@ public GlobalSymbolDictionary(int initialCapacity) {
this.idToSymbol = new ObjList<>(initialCapacity);
}

/**
* Appends {@code symbol} at the next sequential id, matching a recovered /
* persisted dictionary's dense id order, WITHOUT de-duplicating.
* <p>
* Recovery ({@code QwpWebSocketSender.seedGlobalDictionaryFromPersisted})
* replays the persisted entries in id order to rebuild this dictionary. It must
* NOT collapse two source strings that decode to the same characters, because
* the persisted {@code .symbol-dict}, the on-wire delta and the I/O-thread
* catch-up mirror all key on the entry POSITION (id), not on the string. The
* only strings that collide this way are malformed lone UTF-16 surrogates,
* which the UTF-8 encoder maps to {@code '?'}: {@link #getOrAddSymbol} would
* de-dup them and leave this dictionary SHORTER than the persisted entry count,
* desyncing the producer's delta baseline from the catch-up mirror (which uses
* {@code pd.size()}) and silently misattributing later symbols. Appending
* unconditionally keeps {@link #size()} equal to that count. The reverse lookup
* keeps the highest id for a colliding string, which is harmless: both ids
* encode to the same bytes, so resolving either is equivalent.
*
* @param symbol the recovered symbol string (must not be null)
* @return the id assigned (the previous {@link #size()})
*/
public int addRecoveredSymbol(String symbol) {
if (symbol == null) {
throw new IllegalArgumentException("symbol cannot be null");
}
int newId = idToSymbol.size();
symbolToId.put(symbol, newId);
idToSymbol.add(symbol);
return newId;
}

/**
* Clears all symbols from the dictionary.
* <p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,21 @@ public static int varintSize(long value) {
return (64 - Long.numberOfLeadingZeros(value) + 6) / 7;
}

/**
* Writes {@code value} as an unsigned LEB128 varint directly at native address
* {@code addr} and returns the address just past the last byte. The canonical
* raw-address varint writer shared by the SF cursor's persisted dictionary and
* catch-up frame builder.
*/
public static long writeVarint(long addr, long value) {
while (value > 0x7F) {
Unsafe.getUnsafe().putByte(addr++, (byte) ((value & 0x7F) | 0x80));
value >>>= 7;
}
Unsafe.getUnsafe().putByte(addr++, (byte) value);
return addr;
}

@Override
public void close() {
if (bufferPtr != 0) {
Expand Down Expand Up @@ -336,11 +351,7 @@ public void skip(int bytes) {
}

private static void writeVarintDirect(long addr, long value) {
while (value > 0x7F) {
Unsafe.getUnsafe().putByte(addr++, (byte) ((value & 0x7F) | 0x80));
value >>>= 7;
}
Unsafe.getUnsafe().putByte(addr, (byte) value);
writeVarint(addr, value);
}

private void encodeUtf8(CharSequence value, int utf8Len) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,14 @@ public class QwpWebSocketEncoder implements QuietCloseable {

private final QwpColumnWriter columnWriter = new QwpColumnWriter();
private NativeBufferWriter buffer;
// Byte offsets, within the buffer, of the symbol-dict delta ENTRY region
// ([len][utf8]... only, without the two section varints) that beginMessage
// last wrote. Let the producer persist those bytes straight to the slot's
// .symbol-dict instead of re-encoding the same symbols (see
// QwpWebSocketSender.persistNewSymbolsBeforePublish). Valid until the next
// beginMessage; stored as offsets so they survive a buffer realloc.
private int deltaEntriesEnd;
private int deltaEntriesStart;
// QWP ingress always advertises Gorilla timestamp encoding. The column
// writer still emits a per-column encoding byte and falls back to raw
// values when delta-of-delta overflows int32.
Expand Down Expand Up @@ -75,10 +83,12 @@ public void beginMessage(
payloadStart = buffer.getPosition();
buffer.putVarint(deltaStart);
buffer.putVarint(deltaCount);
deltaEntriesStart = buffer.getPosition();
for (int id = deltaStart; id < deltaStart + deltaCount; id++) {
String symbol = globalDict.getSymbol(id);
buffer.putString(symbol);
}
deltaEntriesEnd = buffer.getPosition();
columnWriter.setBuffer(buffer);
}

Expand Down Expand Up @@ -122,6 +132,22 @@ public QwpBufferWriter getBuffer() {
return buffer;
}

/**
* Byte length of the symbol-dict delta ENTRY region ({@code [len][utf8]...},
* excluding the two section varints) that {@link #beginMessage} last wrote.
*/
public int getDeltaEntriesLen() {
return deltaEntriesEnd - deltaEntriesStart;
}

/**
* Byte offset, within {@link #getBuffer()}, of the symbol-dict delta ENTRY
* region {@link #beginMessage} last wrote.
*/
public int getDeltaEntriesStart() {
return deltaEntriesStart;
}

public void setDeferCommit(boolean defer) {
if (defer) {
flags |= FLAG_DEFER_COMMIT;
Expand Down
Loading
Loading