You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Sub-issue of #247. These surfaced while prototyping the performance work but are independent of it — none require the perf changes to land first, and each is small. Grouped into one issue because individually they are too small to track separately.
Problem statement
Six pieces of duplication and near-duplication in the row decode path. Several of them actively increased the cost or risk of the perf spikes in #247.
A sync PlpChunkValidator { declared, total_read, chunks_seen } with accept_chunk(&mut self, len: usize) -> TdsResult<bool> would serve the async path, the streaming path, and any future sans-I/O path from one implementation.
packet_reader.rs defines TdsPacketReadertwice with identical bodies (pub(crate) vs pub, cfg-gated). Same pattern for ColumnPolicy, RowReadResult, and RowPauseState in token_stream.rs.
Consolidate PLP sentinels, currently declared in three places: decoder.rs:490, sqltypes.rs:146, tds_value_serializer.rs:26.
Affected crate
mssql-tds
Alternatives considered
Doing these opportunistically inside the perf PRs. Rejected — it would inflate diffs on changes that are already wide, and make the perf review harder. They should land separately, ideally before the perf work where they reduce its cost (the trait duplication in particular).
Additional context
Also worth noting for anyone in this area: SqlTypeDecode::decode and decode_into are two complete ~40-arm type switches that must be kept in agreement by hand. #254 risks adding a third. That is called out in #254 rather than here because the resolution is a design decision for that issue, not a mechanical cleanup.
Sub-issue of #247. These surfaced while prototyping the performance work but are independent of it — none require the perf changes to land first, and each is small. Grouped into one issue because individually they are too small to track separately.
Problem statement
Six pieces of duplication and near-duplication in the row decode path. Several of them actively increased the cost or risk of the perf spikes in #247.
Proposed solution
Extract a shared
PlpChunkValidator.ensure_active_chunk(mssql-tds/src/datatypes/decoder.rs:242) performs 6 validation checks: chunk count, chunk size, overflow,MAX_PLP_SIZE, exceeds-declared-length, and ended-before-declared-length.PR POC: reduce per-row cost in the TDS row decode path #238's
validate_plp_chunkreimplements only 2 of those 6 and re-declares the constants locally. The AddRowWriterreserve/commit accumulator API to remove double/triple buffering (row decode perf, axis: value handoff) #253 spike'sstream_plp_intonow duplicates all 6 a third time.A sync
PlpChunkValidator { declared, total_read, chunks_seen }withaccept_chunk(&mut self, len: usize) -> TdsResult<bool>would serve the async path, the streaming path, and any future sans-I/O path from one implementation.Checks "exceeds declared" and "ended before declared" are load-bearing given that
LenHint::Exactis trusted downstream by AddRowWriterreserve/commit accumulator API to remove double/triple buffering (row decode perf, axis: value handoff) #253.Constants for reference:
MAX_PLP_SIZE= 64 KiB fuzzing /i32::MAX;MAX_PLP_CHUNKS= 1000 / 100_000;MAX_PLP_CHUNK_SIZE= 8 KiB / 16 MiB.Intern strings as
Rc<str>/Arc<str>.mssql-js/src/binary_row_writer.rs:82doesstring_map.insert(s.clone(), idx)— a fullStringclone used only as a dedup-map key. This is the last remaining string allocation after AddRowWriterreserve/commit accumulator API to remove double/triple buffering (row decode perf, axis: value handoff) #253 lands.Avoid
columns.to_vec()per paused row.read_row_headerintoken_stream.rsclones the entireVec<ColumnMetadata>, including everycolumn_name: String, on each pause. Substantially more expensive than the 6-byte bitmap Keep the NBCROW null bitmap on the stack (row decode perf, axis: precomputation) #255 removes — though it is on the cursor/pause path rather than the bulk-scan path, so it does not show up in the POC: reduce per-row cost in the TDS row decode path #238 benchmark at all.Collapse the cfg-gated trait/struct duplication.
packet_reader.rsdefinesTdsPacketReadertwice with identical bodies (pub(crate)vspub, cfg-gated). Same pattern forColumnPolicy,RowReadResult, andRowPauseStateintoken_stream.rs.This doubled the edit cost of both Convert
TdsPacketReaderto RPITIT to remove per-read boxing (row decode perf, axis: dispatch) #252 and Precompute a per-column decode plan from COLMETADATA (row decode perf, axis: precomputation) #254 and is a standing correctness hazard — the two copies can silently drift. Open question: whethermacro_rules!can collapse them without breaking the fuzz targets.Replace raw
0xFFFFliterals withLENGTH_NULL.packet_reader.rs:9already definespub const LENGTH_NULL: u16 = 0xffff, yetdecoder.rscontained 22 raw literals. The Remove#[async_trait]fromSqlTypeDecode(row decode perf, axis: dispatch) #251/AddRowWriterreserve/commit accumulator API to remove double/triple buffering (row decode perf, axis: value handoff) #253 spikes converted two; the rest remain.Consolidate PLP sentinels, currently declared in three places:
decoder.rs:490,sqltypes.rs:146,tds_value_serializer.rs:26.Affected crate
mssql-tds
Alternatives considered
Doing these opportunistically inside the perf PRs. Rejected — it would inflate diffs on changes that are already wide, and make the perf review harder. They should land separately, ideally before the perf work where they reduce its cost (the trait duplication in particular).
Additional context
Also worth noting for anyone in this area:
SqlTypeDecode::decodeanddecode_intoare two complete ~40-arm type switches that must be kept in agreement by hand. #254 risks adding a third. That is called out in #254 rather than here because the resolution is a design decision for that issue, not a mechanical cleanup.