@pylonsync/sync 0.3.180 → 0.3.181

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/index.ts +17 -13
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.3.180",
6
+ "version": "0.3.181",
7
7
  "type": "module",
8
8
  "main": "src/index.ts",
9
9
  "types": "src/index.ts",
package/src/index.ts CHANGED
@@ -1160,18 +1160,22 @@ export class SyncEngine {
1160
1160
  this.reconnectAttempts = 0;
1161
1161
  this.wsStableTimer = null;
1162
1162
  }, 5_000);
1163
- // Client-side keepalive ping. The server's per-client reader
1164
- // thread blocks on a synchronous WS read for as long as no client
1165
- // message arrives. On the HTTP-multiplexed `/api/sync/ws` path
1166
- // tiny_http doesn't expose stream-level read timeouts, so the
1167
- // reader's mutex hold is bounded only by client activity. Without
1168
- // these pings the broadcaster contends for the same mutex and
1169
- // wedges Insert events never reach the tab. A 1s cadence makes
1170
- // worst-case broadcast latency ~1s even when the user is idle.
1171
- // Browsers don't expose WebSocket-level PING frames; a JSON
1172
- // payload with type:"ping" achieves the same effect (the server
1173
- // reads, looks up an unknown "ping" type, loops back releasing
1174
- // the mutex; broadcaster grabs it during the gap).
1163
+ // Client-side keepalive ping at 200ms. The server's per-client
1164
+ // reader thread blocks synchronously on `ws.read()` and holds the
1165
+ // per-client mutex for the duration of the call. On the dedicated
1166
+ // `:port+1` listener that block is bounded by a 200ms TCP read
1167
+ // timeout (`stream.set_read_timeout`), so the broadcaster gets a
1168
+ // window every 200ms. The HTTP-multiplexed `/api/sync/ws` path
1169
+ // goes through tiny_http's `CustomStream`, which doesn't expose
1170
+ // the underlying TcpStream, so we can't set a timeout there.
1171
+ // These periodic JSON pings give the broadcaster the same 200ms
1172
+ // window by causing the read to return (the server treats
1173
+ // unknown `type` values as no-ops; the side effect is releasing
1174
+ // the mutex between iterations). Browsers don't expose WS-level
1175
+ // PING frames at the application layer, so we send a Text frame.
1176
+ // Tradeoff: ~5 inbound frames/sec/client of background traffic
1177
+ // for sub-second broadcast latency — worth it for the demo
1178
+ // experience and idle tabs alike.
1175
1179
  if (this.pingTimer) clearInterval(this.pingTimer);
1176
1180
  this.pingTimer = setInterval(() => {
1177
1181
  if (this.ws?.readyState !== WebSocket.OPEN) return;
@@ -1180,7 +1184,7 @@ export class SyncEngine {
1180
1184
  } catch {
1181
1185
  // ignore — onclose will trigger reconnect
1182
1186
  }
1183
- }, 1_000);
1187
+ }, 200);
1184
1188
  // Re-send any active CRDT subscriptions across the new socket.
1185
1189
  // The server purged them on disconnect (`unsubscribe_all`), so
1186
1190
  // without this resync a tab that was subscribed before a network