@geodedb/client 1.1.0 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -74,7 +74,17 @@ interface GeodeConfig {
74
74
  keepAliveInterval?: number;
75
75
  /** Maximum idle time in milliseconds (default: 30000) */
76
76
  maxIdleTime?: number;
77
- /** Inline receive timeout in milliseconds (default: 5000) */
77
+ /**
78
+ * How long to wait for the data page the server sends inline after SCHEMA
79
+ * before falling back to an explicit PULL, in milliseconds (default: 5000).
80
+ * DSN key: `inline_timeout`.
81
+ *
82
+ * The inline response is never discarded when this deadline passes: the
83
+ * client keeps track of it and reconciles it with the PULL answer, so a slow
84
+ * first page can never leak into the next query (ENG-3916). Lower it only to
85
+ * make the client fall back to PULL sooner; `requestTimeout` still bounds
86
+ * the whole exchange.
87
+ */
78
88
  inlineTimeout?: number;
79
89
  /** Enable TLS for gRPC (default: true) */
80
90
  tls?: boolean;
@@ -311,6 +321,12 @@ declare const ERR_NO_ROWS_MESSAGE = "geode: no rows in result set";
311
321
  declare function ErrInvalidIdent(): Error;
312
322
  declare function ErrInvalidString(): Error;
313
323
  declare function ErrClosed(): Error;
324
+ /**
325
+ * @deprecated No longer thrown by `Connection`. Concurrent requests on one
326
+ * connection are serialized on the connection's exchange lock instead of
327
+ * failing fast (ENG-3916). Kept exported so existing identity checks still
328
+ * compile.
329
+ */
314
330
  declare function ErrQueryInProgress(): Error;
315
331
  declare function ErrTxInProgress(): Error;
316
332
  declare function ErrNoTx(): Error;
@@ -451,7 +467,7 @@ interface Param {
451
467
  }
452
468
  declare const Param: MessageFns<Param>;
453
469
  interface PullRequest {
454
- requestId: number;
470
+ requestId: bigint;
455
471
  pageSize: number;
456
472
  /** Required for gRPC; ignored for QUIC */
457
473
  sessionId: string;
@@ -530,7 +546,7 @@ interface NullValue {
530
546
  }
531
547
  declare const NullValue: MessageFns<NullValue>;
532
548
  interface IntValue {
533
- value: number;
549
+ value: bigint;
534
550
  kind: IntKind;
535
551
  }
536
552
  declare const IntValue: MessageFns<IntValue>;
@@ -571,15 +587,15 @@ interface MapValue {
571
587
  }
572
588
  declare const MapValue: MessageFns<MapValue>;
573
589
  interface NodeValue {
574
- id: number;
590
+ id: bigint;
575
591
  labels: string[];
576
592
  properties: MapEntry[];
577
593
  }
578
594
  declare const NodeValue: MessageFns<NodeValue>;
579
595
  interface EdgeValue {
580
- id: number;
581
- fromId: number;
582
- toId: number;
596
+ id: bigint;
597
+ fromId: bigint;
598
+ toId: bigint;
583
599
  label: string;
584
600
  properties: MapEntry[];
585
601
  }
@@ -593,7 +609,7 @@ interface ExtendedValue {
593
609
  typeName: string;
594
610
  text?: string | undefined;
595
611
  bytes?: Uint8Array | undefined;
596
- intVal?: number | undefined;
612
+ intVal?: bigint | undefined;
597
613
  doubleVal?: number | undefined;
598
614
  boolVal?: boolean | undefined;
599
615
  }
@@ -607,28 +623,28 @@ interface Error$1 {
607
623
  }
608
624
  declare const Error$1: MessageFns<Error$1>;
609
625
  interface ExecutionMetrics {
610
- parseDurationNs: number;
611
- planDurationNs: number;
612
- executeDurationNs: number;
613
- totalDurationNs: number;
626
+ parseDurationNs: bigint;
627
+ planDurationNs: bigint;
628
+ executeDurationNs: bigint;
629
+ totalDurationNs: bigint;
614
630
  }
615
631
  declare const ExecutionMetrics: MessageFns<ExecutionMetrics>;
616
632
  interface ExplainOp {
617
633
  idx: number;
618
634
  kind: string;
619
- estRows: number;
620
- cost: number;
635
+ estRows: bigint;
636
+ cost: bigint;
621
637
  }
622
638
  declare const ExplainOp: MessageFns<ExplainOp>;
623
639
  interface ExplainTotals {
624
- estRows: number;
625
- cost: number;
640
+ estRows: bigint;
641
+ cost: bigint;
626
642
  }
627
643
  declare const ExplainTotals: MessageFns<ExplainTotals>;
628
644
  interface ExplainProperties {
629
645
  ordered: boolean;
630
- limit: number;
631
- offset: number;
646
+ limit: bigint;
647
+ offset: bigint;
632
648
  distinct: boolean;
633
649
  unionMode: string;
634
650
  unionPartCount: number;
@@ -659,53 +675,53 @@ interface ProfileOp {
659
675
  phase: string;
660
676
  id: number;
661
677
  opIndex: number;
662
- inputRows: number;
663
- rows: number;
664
- timeNs: number;
665
- cpuTimeNs: number;
666
- bytesOut: number;
667
- bytesAlloc: number;
668
- estimateBytes: number;
669
- estimateVsActual: number;
670
- percentPeak: number;
671
- percentNet: number;
672
- cumulativeTimeNs: number;
673
- freedBytes: number;
674
- netAfterOp: number;
675
- errorBytes: number;
676
- errorAbsPct: number;
678
+ inputRows: bigint;
679
+ rows: bigint;
680
+ timeNs: bigint;
681
+ cpuTimeNs: bigint;
682
+ bytesOut: bigint;
683
+ bytesAlloc: bigint;
684
+ estimateBytes: bigint;
685
+ estimateVsActual: bigint;
686
+ percentPeak: bigint;
687
+ percentNet: bigint;
688
+ cumulativeTimeNs: bigint;
689
+ freedBytes: bigint;
690
+ netAfterOp: bigint;
691
+ errorBytes: bigint;
692
+ errorAbsPct: bigint;
677
693
  indexName: string;
678
694
  selectivityEst: number;
679
695
  }
680
696
  declare const ProfileOp: MessageFns<ProfileOp>;
681
697
  interface ProfilePeakContributor {
682
698
  op: string;
683
- bytesAlloc: number;
699
+ bytesAlloc: bigint;
684
700
  }
685
701
  declare const ProfilePeakContributor: MessageFns<ProfilePeakContributor>;
686
702
  interface ProfileTotals {
687
- timeNs: number;
688
- peakBytes: number;
703
+ timeNs: bigint;
704
+ peakBytes: bigint;
689
705
  }
690
706
  declare const ProfileTotals: MessageFns<ProfileTotals>;
691
707
  interface ProfileSpills {
692
- sortSpills: number;
693
- distinctSpills: number;
694
- unionSpills: number;
708
+ sortSpills: bigint;
709
+ distinctSpills: bigint;
710
+ unionSpills: bigint;
695
711
  spillReason: string;
696
- peakBytesAtSpill: number;
712
+ peakBytesAtSpill: bigint;
697
713
  firstSpillOpIndex: number;
698
714
  }
699
715
  declare const ProfileSpills: MessageFns<ProfileSpills>;
700
716
  interface ProfileMemory {
701
- netBytes: number;
702
- peakBytes: number;
703
- totalAllocBytes: number;
717
+ netBytes: bigint;
718
+ peakBytes: bigint;
719
+ totalAllocBytes: bigint;
704
720
  }
705
721
  declare const ProfileMemory: MessageFns<ProfileMemory>;
706
722
  interface ProfilePlannerEstimates {
707
- sumEstimateBytes: number;
708
- sumActualBytes: number;
723
+ sumEstimateBytes: bigint;
724
+ sumActualBytes: bigint;
709
725
  countEstimatedOps: number;
710
726
  minErrorAbsPct: number;
711
727
  maxErrorAbsPct: number;
@@ -717,16 +733,16 @@ interface ProfilePlannerEstimates {
717
733
  declare const ProfilePlannerEstimates: MessageFns<ProfilePlannerEstimates>;
718
734
  interface ProfileMemCurvePoint {
719
735
  opIndex: number;
720
- netAfterOp: number;
736
+ netAfterOp: bigint;
721
737
  }
722
738
  declare const ProfileMemCurvePoint: MessageFns<ProfileMemCurvePoint>;
723
739
  interface ProfileSetOp {
724
740
  type: string;
725
741
  mode: string;
726
- inputRows: number;
727
- outputRows: number;
728
- hashDedupSize: number;
729
- distinctFillPct: number;
742
+ inputRows: bigint;
743
+ outputRows: bigint;
744
+ hashDedupSize: bigint;
745
+ distinctFillPct: bigint;
730
746
  }
731
747
  declare const ProfileSetOp: MessageFns<ProfileSetOp>;
732
748
  interface ProfilePayload {
@@ -734,17 +750,17 @@ interface ProfilePayload {
734
750
  ops: ProfileOp[];
735
751
  peakContributors: ProfilePeakContributor[];
736
752
  totals?: ProfileTotals | undefined;
737
- totalTimeNs: number;
753
+ totalTimeNs: bigint;
738
754
  spills?: ProfileSpills | undefined;
739
755
  memory?: ProfileMemory | undefined;
740
756
  plannerEstimates?: ProfilePlannerEstimates | undefined;
741
757
  memCurve: ProfileMemCurvePoint[];
742
758
  setop?: ProfileSetOp | undefined;
743
- hashaggSpills: number;
759
+ hashaggSpills: bigint;
744
760
  hashaggSpillReason: string;
745
- committedTxns: number;
746
- graphStoreNodes: number;
747
- graphStoreEdges: number;
761
+ committedTxns: bigint;
762
+ graphStoreNodes: bigint;
763
+ graphStoreEdges: bigint;
748
764
  graphStoreDirty: boolean;
749
765
  flaggerFindings: string[];
750
766
  compact: boolean;
@@ -825,67 +841,69 @@ declare const RollbackToResponse: MessageFns<RollbackToResponse>;
825
841
  * ============================================================================
826
842
  */
827
843
  interface CdcDiagnosticsRequest {
844
+ sessionId: string;
828
845
  }
829
846
  declare const CdcDiagnosticsRequest: MessageFns<CdcDiagnosticsRequest>;
830
847
  interface CdcDiagnosticsConfig {
831
848
  enabled: boolean;
832
- malformedSnapshotInterval: number;
849
+ malformedSnapshotInterval: bigint;
833
850
  malformedHashRetain: number;
834
851
  malformedWarnPct: number;
835
852
  malformedAbortPct: number;
836
853
  flushIntervalMs: number;
837
854
  batchSize: number;
838
- pendingBackpressureWatermark: number;
855
+ pendingBackpressureWatermark: bigint;
839
856
  }
840
857
  declare const CdcDiagnosticsConfig: MessageFns<CdcDiagnosticsConfig>;
841
858
  interface CdcDiagnosticsEngine {
842
859
  isRunning: boolean;
843
- lastFlushMs: number;
844
- peakBuffer: number;
845
- currentBuffer: number;
860
+ lastFlushMs: bigint;
861
+ peakBuffer: bigint;
862
+ currentBuffer: bigint;
846
863
  dynamicBatchSize: number;
847
864
  batchAdjustments: number;
848
865
  }
849
866
  declare const CdcDiagnosticsEngine: MessageFns<CdcDiagnosticsEngine>;
850
867
  interface CdcMalformedSnapshot {
851
- count: number;
852
- tsMs: number;
868
+ count: bigint;
869
+ tsMs: bigint;
853
870
  }
854
871
  declare const CdcMalformedSnapshot: MessageFns<CdcMalformedSnapshot>;
855
872
  interface CdcMalformedHash {
856
- count: number;
857
- hash: number;
858
- tsMs: number;
873
+ count: bigint;
874
+ hash: bigint;
875
+ tsMs: bigint;
859
876
  prefixHex: string;
860
877
  }
861
878
  declare const CdcMalformedHash: MessageFns<CdcMalformedHash>;
862
879
  interface CdcDiagnosticsResponse {
863
- malformedChangeRecords: number;
864
- totalChangeAttempts: number;
880
+ malformedChangeRecords: bigint;
881
+ totalChangeAttempts: bigint;
865
882
  malformedRatio: number;
866
883
  guardrailWarnTriggered: boolean;
867
884
  guardrailAbortTriggered: boolean;
868
- firstWarnTsMs: number;
869
- firstAbortTsMs: number;
870
- guardrailEpoch: number;
871
- uptimeMs: number;
885
+ firstWarnTsMs: bigint;
886
+ firstAbortTsMs: bigint;
887
+ guardrailEpoch: bigint;
888
+ uptimeMs: bigint;
872
889
  backpressure: boolean;
873
890
  config?: CdcDiagnosticsConfig | undefined;
874
891
  engine?: CdcDiagnosticsEngine | undefined;
875
892
  malformedSnapshots: CdcMalformedSnapshot[];
876
893
  malformedHashes: CdcMalformedHash[];
877
894
  version: string;
878
- timestampMs: number;
895
+ timestampMs: bigint;
879
896
  }
880
897
  declare const CdcDiagnosticsResponse: MessageFns<CdcDiagnosticsResponse>;
881
898
  interface CdcControlRequest {
882
899
  action: string;
900
+ sessionId: string;
883
901
  }
884
902
  declare const CdcControlRequest: MessageFns<CdcControlRequest>;
885
903
  interface CdcControlResponse {
886
904
  success: boolean;
887
905
  status: string;
888
- guardrailEpoch: number;
906
+ guardrailEpoch: bigint;
889
907
  }
890
908
  declare const CdcControlResponse: MessageFns<CdcControlResponse>;
891
909
  /**
@@ -905,7 +923,7 @@ interface BackupResponse {
905
923
  message: string;
906
924
  backupId: string;
907
925
  backupType: string;
908
- sizeBytes: number;
926
+ sizeBytes: bigint;
909
927
  compression: string;
910
928
  checksum: string;
911
929
  }
@@ -921,11 +939,11 @@ interface RestoreResponse {
921
939
  message: string;
922
940
  restoreDir: string;
923
941
  targetTime: string;
924
- restoreTimestamp: number;
942
+ restoreTimestamp: bigint;
925
943
  }
926
944
  declare const RestoreResponse: MessageFns<RestoreResponse>;
927
945
  interface UploadBackupRequest {
928
- sizeBytes: number;
946
+ sizeBytes: bigint;
929
947
  checksum: string;
930
948
  sessionId: string;
931
949
  /**
@@ -941,7 +959,7 @@ interface UploadBackupResponse {
941
959
  uploadPath: string;
942
960
  }
943
961
  declare const UploadBackupResponse: MessageFns<UploadBackupResponse>;
944
- type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined;
962
+ type Builtin = Date | Function | Uint8Array | string | number | boolean | bigint | undefined;
945
963
  type DeepPartial<T> = T extends Builtin ? T : T extends globalThis.Array<infer U> ? globalThis.Array<DeepPartial<U>> : T extends ReadonlyArray<infer U> ? ReadonlyArray<DeepPartial<U>> : T extends {} ? {
946
964
  [K in keyof T]?: DeepPartial<T[K]>;
947
965
  } : Partial<T>;
@@ -1029,7 +1047,7 @@ declare function buildExecuteRequest(sessionId: string, query: string, params?:
1029
1047
  /**
1030
1048
  * Build a PullRequest message.
1031
1049
  */
1032
- declare function buildPullRequest(requestId: number, pageSize: number, sessionId: string): QuicClientMessage;
1050
+ declare function buildPullRequest(requestId: number | bigint, pageSize: number, sessionId: string): QuicClientMessage;
1033
1051
  /**
1034
1052
  * Build a PingRequest message.
1035
1053
  */
@@ -1120,6 +1138,25 @@ declare class QuicTransport extends BaseTransport {
1120
1138
  private _maxBufferBytes;
1121
1139
  private _pendingProtoReads;
1122
1140
  private _pendingResponses;
1141
+ /**
1142
+ * Decode failure that arrived while no reader was parked. Surfaced by the
1143
+ * next receiveProto() once the already-decoded backlog has been drained.
1144
+ */
1145
+ private _pendingDecodeError;
1146
+ /**
1147
+ * Sticky failure latched when the read pump ends or throws. The pump is the
1148
+ * ONLY producer of responses, so once it is gone nothing can ever settle a
1149
+ * parked reader: report the failure instead of hanging (ENG-3918).
1150
+ */
1151
+ private _readFailure;
1152
+ /** True once close() has released the QUIC client and buffers. */
1153
+ private _disposed;
1154
+ /**
1155
+ * Serializes writes to the single bidirectional stream. `getWriter()` throws
1156
+ * "WritableStream is locked" while another write holds the lock, so queued
1157
+ * exchanges must take turns (ENG-3916).
1158
+ */
1159
+ private _writeChain;
1123
1160
  constructor(address: string, maxMessageSize?: number, maxBufferBytes?: number);
1124
1161
  /**
1125
1162
  * Connect to the Geode server using QUIC.
@@ -1129,6 +1166,15 @@ declare class QuicTransport extends BaseTransport {
1129
1166
  * Start reading from the stream in the background.
1130
1167
  */
1131
1168
  private startReading;
1169
+ /**
1170
+ * Latch the transport dead after the read pump ends or throws.
1171
+ *
1172
+ * Nothing can produce a response once the pump is gone, so leaving
1173
+ * `isClosed()` false makes the connection (and the pool, whose only liveness
1174
+ * predicate is that flag) believe a dead socket is healthy, and parks every
1175
+ * later reader on a producer that will never run again (ENG-3918).
1176
+ */
1177
+ private failFromReadPump;
1132
1178
  /**
1133
1179
  * Reject all pending reads with an error.
1134
1180
  */
@@ -1409,6 +1455,10 @@ declare class QueryResult implements AsyncIterable<Row> {
1409
1455
  private _closed;
1410
1456
  private _bufferIndex;
1411
1457
  private _rowCount;
1458
+ /** Settles once the connection is back in phase after this result closed. */
1459
+ private _drained;
1460
+ /** Owner hook (used by the pool to hold its member for the result's life). */
1461
+ private _onCloseHooks;
1412
1462
  constructor(conn: Connection, columns: ColumnInfo[], initialRows: Record<string, unknown>[], final: boolean, ordered: boolean, orderKeys: string[], pageSize: number, signal?: AbortSignal);
1413
1463
  /**
1414
1464
  * Get column definitions.
@@ -1481,8 +1531,27 @@ declare class QueryResult implements AsyncIterable<Row> {
1481
1531
  reduce<T>(fn: (acc: T, row: Row, index: number) => T | Promise<T>, initial: T): Promise<T>;
1482
1532
  /**
1483
1533
  * Close the result set.
1534
+ *
1535
+ * When the server still owes pages, closing hands the connection the job of
1536
+ * draining them; use {@link QueryResult._settle} (or simply iterate to the
1537
+ * end) to await that. A caller that walks away never observes a connection
1538
+ * that is silently out of phase (ENG-3916).
1484
1539
  */
1485
1540
  close(): void;
1541
+ /**
1542
+ * @internal Whether the result still needs the connection.
1543
+ *
1544
+ * False once every page is buffered client-side: the connection is then free
1545
+ * and its pool member can be released immediately.
1546
+ */
1547
+ get _needsConnection(): boolean;
1548
+ /**
1549
+ * @internal Register a callback fired exactly once when this result closes.
1550
+ * Fires immediately when the result is already closed.
1551
+ */
1552
+ _onClose(hook: () => void): void;
1553
+ /** Close the result and wait for the connection to be back in phase. */
1554
+ private _settle;
1486
1555
  /**
1487
1556
  * Internal close (called by connection).
1488
1557
  */
@@ -1967,11 +2036,11 @@ declare function batchMap(conn: Connection, queryTemplate: string, items: QueryP
1967
2036
  * Unlike the sequential batch(), this executes multiple queries concurrently
1968
2037
  * up to the specified limit.
1969
2038
  *
1970
- * **Important:** A single `Connection` can only execute one query at a time
1971
- * (the connection state machine throws `ErrQueryInProgress` for concurrent
1972
- * queries). When a single connection is provided with concurrency > 1, this
1973
- * function automatically falls back to sequential execution (concurrency=1)
1974
- * to prevent runtime errors.
2039
+ * **Important:** A single `Connection` owns one bidirectional stream and can
2040
+ * only execute one query at a time; concurrent requests queue on its exchange
2041
+ * lock rather than overlapping (ENG-3916). When a single connection is
2042
+ * provided with concurrency > 1, this function falls back to sequential
2043
+ * execution (concurrency=1) because the extra concurrency buys nothing.
1975
2044
  *
1976
2045
  * For true parallel execution, use a connection pool or connection factory
1977
2046
  * that provides separate connections per concurrent query.
@@ -2021,6 +2090,21 @@ declare class Connection {
2021
2090
  private _requestId;
2022
2091
  private _sessionId;
2023
2092
  private _columns;
2093
+ private _delivery;
2094
+ /**
2095
+ * FIFO exchange lock.
2096
+ *
2097
+ * The connection owns ONE bidirectional stream and the wire protocol carries
2098
+ * no request/response correlation id, so two overlapping exchanges would read
2099
+ * each other's answers. Callers therefore queue instead of racing: an
2100
+ * exchange holds the lock for its FULL request -> final-response round trip,
2101
+ * and a streaming QueryResult keeps holding it until it is closed or drained
2102
+ * (ENG-3916).
2103
+ */
2104
+ private _exchangeHeld;
2105
+ private _exchangeWaiters;
2106
+ /** Lock release handed to the active streaming result, if any. */
2107
+ private _activeRelease;
2024
2108
  private constructor();
2025
2109
  /**
2026
2110
  * Create a new connection to the Geode server.
@@ -2040,6 +2124,16 @@ declare class Connection {
2040
2124
  get isClosed(): boolean;
2041
2125
  /** Get session ID. */
2042
2126
  get sessionId(): string;
2127
+ /**
2128
+ * Take the exchange lock, returning its (idempotent) release function.
2129
+ *
2130
+ * Registration is synchronous, so waiters are served strictly in call order.
2131
+ */
2132
+ private acquireExchange;
2133
+ /** Run one complete exchange under the connection's exchange lock. */
2134
+ private withExchange;
2135
+ /** Wake every queued exchange so it can observe the closed state. */
2136
+ private drainExchangeWaiters;
2043
2137
  /** Perform the HELLO handshake. */
2044
2138
  private hello;
2045
2139
  /** Execute a query that returns rows. */
@@ -2058,20 +2152,78 @@ declare class Connection {
2058
2152
  rows: Record<string, unknown>[];
2059
2153
  final: boolean;
2060
2154
  }>;
2061
- /** Try to receive an inline response with short timeout. */
2155
+ /**
2156
+ * Read the next page off the stream.
2157
+ *
2158
+ * Accepts either envelope: the server pushes pages as `execute` responses and
2159
+ * wraps a PULL's answer in `pull`. Anything else means the stream is out of
2160
+ * phase with the request being served, so the connection fails closed instead
2161
+ * of silently swallowing the message.
2162
+ */
2163
+ private _readNextPage;
2164
+ /**
2165
+ * Wait for the response the server sends after SCHEMA.
2166
+ *
2167
+ * Returns `null` when the inline deadline (`inlineTimeout`) expires. That
2168
+ * response is then STILL in flight - the caller must reconcile it before
2169
+ * returning, because the transport is a FIFO stream without request/response
2170
+ * correlation and anything left behind is read by the next request
2171
+ * (ENG-3916).
2172
+ */
2062
2173
  private _tryReceiveInline;
2063
- /** Read inline execute responses until a page, error, heartbeat, or timeout arrives. */
2174
+ /** Read inline execute responses until a page, error, heartbeat, or the inline deadline. */
2064
2175
  private _readInlineExecute;
2065
- /** Drain remaining data pages until final=true to prevent query corruption (QUAL-T7). */
2176
+ /**
2177
+ * Send a PULL and consume responses until every outstanding response has
2178
+ * been accounted for.
2179
+ *
2180
+ * The wire protocol is a FIFO stream with no request/response correlation
2181
+ * id, so the client must reconcile the whole exchange before it returns:
2182
+ * a response left in the transport is read by the NEXT request, which then
2183
+ * sees this query's schema and rows (ENG-3916). Up to two responses can be
2184
+ * outstanding here:
2185
+ *
2186
+ * - the inline page the server sends after SCHEMA, when the inline read
2187
+ * gave up waiting for it (`inlinePending`), and
2188
+ * - the answer to the PULL sent below, which QUIC servers always send as a
2189
+ * `pull`-wrapped message.
2190
+ *
2191
+ * Because the server writes its responses in order, a `pull`-wrapped answer
2192
+ * also proves that nothing is still coming for the EXECUTE, which is what
2193
+ * makes the reconciliation exact.
2194
+ *
2195
+ * When the exchange cannot be reconciled - a timeout, an abort or a
2196
+ * transport failure while a response is still owed - the connection is
2197
+ * closed instead of returned to the caller (and to the pool).
2198
+ *
2199
+ * NOTE (server team, ENG-3916): the durable fix is a request/response
2200
+ * correlation id in the proto. Until the wire protocol carries one, the
2201
+ * client can only reconcile by message type plus stream ordering.
2202
+ */
2203
+ private _pullFirstPage;
2204
+ /**
2205
+ * Drain remaining data pages until final=true to prevent query corruption
2206
+ * (QUAL-T7). Honours the current delivery mode: pushed pages are read,
2207
+ * parked results are pulled one page at a time.
2208
+ */
2066
2209
  private _drainRemainingPages;
2067
- /** @internal Release the active result, returning the connection to idle. */
2068
- _releaseResult(result: QueryResult): void;
2210
+ /**
2211
+ * @internal Release the active result, returning the connection to idle.
2212
+ *
2213
+ * A result abandoned before `final = true` leaves pages the server still owes
2214
+ * on the stream. They are drained here - under the exchange lock the result
2215
+ * still holds - so the next caller cannot read them as its own (ENG-3916).
2216
+ * The returned promise settles once the connection is back in phase.
2217
+ */
2218
+ _releaseResult(result: QueryResult, final: boolean): Promise<void>;
2069
2219
  /** Begin a transaction. */
2070
2220
  begin(signal?: AbortSignal): Promise<Transaction>;
2071
2221
  /** @internal Commit the current transaction. Called by Transaction. */
2072
2222
  _commit(signal?: AbortSignal): Promise<void>;
2073
2223
  /** @internal Rollback the current transaction. Called by Transaction. */
2074
2224
  _rollback(signal?: AbortSignal): Promise<void>;
2225
+ /** Send ROLLBACK and consume its answer. The exchange lock must be held. */
2226
+ private _rollbackExchange;
2075
2227
  /** @internal Create a named savepoint. Called by Transaction. */
2076
2228
  _savepoint(name: string, signal?: AbortSignal): Promise<void>;
2077
2229
  /** @internal Rollback to a previously created savepoint. Called by Transaction. */
@@ -2082,6 +2234,8 @@ declare class Connection {
2082
2234
  reset(signal?: AbortSignal): Promise<void>;
2083
2235
  /** Close the connection. */
2084
2236
  close(): Promise<void>;
2237
+ /** Drop the active result and hand its exchange lock back. */
2238
+ private _abandonActiveResult;
2085
2239
  /** Create a prepared statement. */
2086
2240
  prepare(query: string): Promise<PreparedStatement>;
2087
2241
  /** Get the query execution plan without executing. */
@@ -2090,7 +2244,12 @@ declare class Connection {
2090
2244
  profile(query: string, options?: ExplainOptions): Promise<QueryProfile>;
2091
2245
  /** Execute multiple queries in a batch. */
2092
2246
  batch(queries: BatchQuery[], options?: BatchOptions): Promise<BatchSummary>;
2093
- /** Check connection state before operation. */
2247
+ /**
2248
+ * Check connection state before operation.
2249
+ *
2250
+ * Concurrent requests are NOT rejected any more: they queue on the exchange
2251
+ * lock (ENG-3916). Only genuinely illegal states fail fast here.
2252
+ */
2094
2253
  private checkState;
2095
2254
  /** Send a protobuf message with request timeout enforcement. */
2096
2255
  private _sendWithTimeout;
@@ -2098,7 +2257,38 @@ declare class Connection {
2098
2257
  private _receiveWithTimeout;
2099
2258
  /** Create a combined abort signal from requestTimeout and optional caller signal (CWE-703). */
2100
2259
  private _withRequestTimeout;
2260
+ /**
2261
+ * Tear the connection down after a transport failure.
2262
+ *
2263
+ * `responseOwed` marks the cases where the server may still answer: a request
2264
+ * deadline that elapsed, or any aborted receive. Those leave the stream out
2265
+ * of phase, so the connection must not be reused (or returned to the pool)
2266
+ * even though the underlying socket is still open (ENG-3918).
2267
+ */
2101
2268
  private _closeOnTransportError;
2269
+ /**
2270
+ * Whether the server answers every PULL with its own message.
2271
+ *
2272
+ * QUIC servers reply to a PULL with a `pull`-wrapped response, so that
2273
+ * response has to be consumed before the connection is back in phase. The
2274
+ * gRPC transport has no PULL RPC: it serves PULLs from the Execute stream
2275
+ * that is already open, so no extra message is produced.
2276
+ */
2277
+ private get _pullIsAcknowledged();
2278
+ /**
2279
+ * Tear the connection down after a protocol desync and build the error.
2280
+ *
2281
+ * The stream carries no correlation ids, so a message that cannot belong to
2282
+ * the request being served means client and server are out of phase: every
2283
+ * later response would answer an earlier request, silently handing one
2284
+ * caller another caller's rows. Failing closed makes the connection
2285
+ * unusable, and the pool drops closed connections instead of reusing them.
2286
+ */
2287
+ private _protocolDesync;
2288
+ /** Close the transport and drop any active result, ignoring teardown errors. */
2289
+ private _forceClose;
2290
+ /** Return to the resting state, unless the connection was torn down. */
2291
+ private _restoreIdleState;
2102
2292
  }
2103
2293
 
2104
2294
  /**
@@ -2678,6 +2868,11 @@ declare class ConnectionPool {
2678
2868
  withConnection<T>(fn: (conn: Connection) => Promise<T>, signal?: AbortSignal): Promise<T>;
2679
2869
  /**
2680
2870
  * Execute a query using a pooled connection.
2871
+ *
2872
+ * A streaming result keeps fetching pages from the connection it was created
2873
+ * on, so the pooled member stays checked out until the result is closed
2874
+ * (ENG-3916). A result whose rows are already buffered needs nothing further
2875
+ * from the connection and releases the member right away.
2681
2876
  */
2682
2877
  query(query: string, options?: QueryOptions): Promise<QueryResult>;
2683
2878
  /**
@@ -2709,6 +2904,17 @@ declare class ConnectionPool {
2709
2904
  * Add a new connection to the pool with rate limiting and exponential backoff.
2710
2905
  */
2711
2906
  private addConnection;
2907
+ /**
2908
+ * Drop every idle member whose connection is no longer usable.
2909
+ *
2910
+ * `Connection.isClosed` is the pool's only liveness predicate, and a
2911
+ * connection torn down after a desync reports closed: leaving those members
2912
+ * in place both risks reuse and starves the pool of its maxConnections slots
2913
+ * until the next maintenance tick (ENG-3918).
2914
+ */
2915
+ private evictBrokenConnections;
2916
+ /** Close a discarded connection, ignoring teardown failures. */
2917
+ private closeQuietly;
2712
2918
  /**
2713
2919
  * Remove a connection from the pool.
2714
2920
  */