@actuallab/rpc 13.0.163 → 13.0.167

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.cjs CHANGED
@@ -1077,6 +1077,7 @@ var import_core5 = require("@actuallab/core");
1077
1077
  var { warnLog: warnLog3 } = getLogs("RpcSharedStream");
1078
1078
  var DEFAULT_ACK_PERIOD = 256;
1079
1079
  var DEFAULT_ACK_ADVANCE = 128;
1080
+ var RTT_WINDOW_MS = 2e4;
1080
1081
  var RpcStreamSender = class _RpcStreamSender {
1081
1082
  static _emptyGuid = "00000000-0000-0000-0000-000000000000";
1082
1083
  id;
@@ -1118,6 +1119,9 @@ var RpcStreamSender = class _RpcStreamSender {
1118
1119
  _lastAckedIndex = 0;
1119
1120
  _abortController = new AbortController();
1120
1121
  _iterator = null;
1122
+ // -- RTT sampling (send → matching ack round trips, windowed minimum) --
1123
+ _pendingSendTimes = [];
1124
+ _rttSamples = [];
1121
1125
  // -- Recorder-controller metrics (Step 9.1) --
1122
1126
  /** Total items skipped via real-time `canSkipTo` compaction. */
1123
1127
  _skipCount = 0;
@@ -1142,6 +1146,21 @@ var RpcStreamSender = class _RpcStreamSender {
1142
1146
  get skipCount() {
1143
1147
  return this._skipCount;
1144
1148
  }
1149
+ // Windowed-MIN ack round trip (ms, -1 until sampled): converges on propagation
1150
+ // RTT — unlike a mean/EMA it is not inflated by self-induced queuing.
1151
+ get minRttMs() {
1152
+ const cutoffMs = Date.now() - RTT_WINDOW_MS;
1153
+ while (this._rttSamples.length > 0 && this._rttSamples[0].atMs < cutoffMs)
1154
+ this._rttSamples.shift();
1155
+ if (this._rttSamples.length === 0)
1156
+ return -1;
1157
+ let min = this._rttSamples[0].rttMs;
1158
+ for (const s of this._rttSamples) {
1159
+ if (s.rttMs < min)
1160
+ min = s.rttMs;
1161
+ }
1162
+ return min;
1163
+ }
1145
1164
  // -- ACK queue (analog of .NET's Channel<(long, bool)>) --
1146
1165
  _acks = [];
1147
1166
  _whenAckReady = null;
@@ -1195,6 +1214,7 @@ var RpcStreamSender = class _RpcStreamSender {
1195
1214
  this._sendDisconnect();
1196
1215
  return;
1197
1216
  }
1217
+ this._sampleRtt(nextIndex, mustReset);
1198
1218
  this._acks.push({ nextIndex, mustReset });
1199
1219
  if (this._whenAckReady) {
1200
1220
  this._whenAckReady.resolve();
@@ -1233,6 +1253,7 @@ var RpcStreamSender = class _RpcStreamSender {
1233
1253
  this._nextIndex,
1234
1254
  item
1235
1255
  );
1256
+ this._pendingSendTimes.push({ index: this._nextIndex, sentAtMs: Date.now() });
1236
1257
  }
1237
1258
  this._nextIndex++;
1238
1259
  }
@@ -1475,6 +1496,26 @@ var RpcStreamSender = class _RpcStreamSender {
1475
1496
  await this._whenAckReady;
1476
1497
  this._whenAckReady = null;
1477
1498
  }
1499
+ // One RTT sample per ack: time from sending the newest acked item to the
1500
+ // ack's arrival (a slight upper bound — includes the server's ack cadence).
1501
+ _sampleRtt(ackNextIndex, mustReset) {
1502
+ if (mustReset) {
1503
+ this._pendingSendTimes.length = 0;
1504
+ return;
1505
+ }
1506
+ let sentAtMs = -1;
1507
+ while (this._pendingSendTimes.length > 0 && this._pendingSendTimes[0].index < ackNextIndex) {
1508
+ sentAtMs = this._pendingSendTimes[0].sentAtMs;
1509
+ this._pendingSendTimes.shift();
1510
+ }
1511
+ if (sentAtMs < 0)
1512
+ return;
1513
+ const nowMs = Date.now();
1514
+ const cutoffMs = nowMs - RTT_WINDOW_MS;
1515
+ while (this._rttSamples.length > 0 && this._rttSamples[0].atMs < cutoffMs)
1516
+ this._rttSamples.shift();
1517
+ this._rttSamples.push({ atMs: nowMs, rttMs: nowMs - sentAtMs });
1518
+ }
1478
1519
  // -- IRpcObject --
1479
1520
  reconnect() {
1480
1521
  }