teyyare 0.2.7 → 0.2.9

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/src/index.js CHANGED
@@ -45,32 +45,45 @@ TCPTransport.prototype._attachSocketReader = function(socket) {
45
45
  if (socket._writableState) socket._writableState.highWaterMark = 4 * 1024 * 1024;
46
46
 
47
47
  const chunks = [];
48
+ let chunkHead = 0;
49
+ let chunkOffset = 0;
48
50
  let bufferedLength = 0;
51
+ const headerScratch = Buffer.allocUnsafe(HEADER_SIZE);
49
52
 
50
53
  socket.on('data', (chunk) => {
51
54
  chunks.push(chunk);
52
55
  bufferedLength += chunk.length;
53
56
 
54
57
  while (bufferedLength >= HEADER_SIZE) {
55
- let headerBuf = chunks[0];
56
- if (headerBuf.length < HEADER_SIZE) {
57
- const tempHeader = Buffer.allocUnsafe(HEADER_SIZE);
58
+ const head = chunks[chunkHead];
59
+ const headAvail = head.length - chunkOffset;
60
+ let header;
61
+
62
+ if (headAvail >= HEADER_SIZE) {
63
+ try {
64
+ header = readHeader(head, chunkOffset);
65
+ } catch (err) {
66
+ socket.destroy(err);
67
+ return;
68
+ }
69
+ } else {
58
70
  let off = 0;
59
- for (let i = 0; off < HEADER_SIZE && i < chunks.length; i++) {
60
- const c = chunks[i];
61
- const toCopy = Math.min(c.length, HEADER_SIZE - off);
62
- tempHeader.set(c.subarray(0, toCopy), off);
71
+ let cIdx = chunkHead;
72
+ let cOff = chunkOffset;
73
+ while (off < HEADER_SIZE && cIdx < chunks.length) {
74
+ const c = chunks[cIdx];
75
+ const toCopy = Math.min(c.length - cOff, HEADER_SIZE - off);
76
+ headerScratch.set(c.subarray(cOff, cOff + toCopy), off);
63
77
  off += toCopy;
78
+ cIdx++;
79
+ cOff = 0;
80
+ }
81
+ try {
82
+ header = readHeader(headerScratch, 0);
83
+ } catch (err) {
84
+ socket.destroy(err);
85
+ return;
64
86
  }
65
- headerBuf = tempHeader;
66
- }
67
-
68
- let header;
69
- try {
70
- header = readHeader(headerBuf, 0);
71
- } catch (err) {
72
- socket.destroy(err);
73
- return;
74
87
  }
75
88
 
76
89
  const totalLength = HEADER_SIZE + header.payloadLength;
@@ -79,29 +92,35 @@ TCPTransport.prototype._attachSocketReader = function(socket) {
79
92
  }
80
93
 
81
94
  let rawFrame;
82
- if (chunks[0].length >= totalLength) {
83
- rawFrame = chunks[0].subarray(0, totalLength);
84
- if (chunks[0].length === totalLength) {
85
- chunks.shift();
86
- } else {
87
- chunks[0] = chunks[0].subarray(totalLength);
95
+ if (headAvail >= totalLength) {
96
+ // Fast-path: single chunk zero-copy slice
97
+ rawFrame = head.subarray(chunkOffset, chunkOffset + totalLength);
98
+ chunkOffset += totalLength;
99
+ if (chunkOffset === head.length) {
100
+ chunks[chunkHead] = null;
101
+ chunkHead++;
102
+ chunkOffset = 0;
88
103
  }
89
104
  bufferedLength -= totalLength;
90
105
  } else {
106
+ // Multi-chunk frame assembly
91
107
  rawFrame = Buffer.allocUnsafe(totalLength);
92
108
  let dstOff = 0;
93
109
  let remaining = totalLength;
94
110
  while (remaining > 0) {
95
- const head = chunks[0];
96
- if (head.length <= remaining) {
97
- rawFrame.set(head, dstOff);
98
- dstOff += head.length;
99
- remaining -= head.length;
100
- chunks.shift();
111
+ const curr = chunks[chunkHead];
112
+ const avail = curr.length - chunkOffset;
113
+ if (avail <= remaining) {
114
+ rawFrame.set(curr.subarray(chunkOffset), dstOff);
115
+ dstOff += avail;
116
+ remaining -= avail;
117
+ chunks[chunkHead] = null;
118
+ chunkHead++;
119
+ chunkOffset = 0;
101
120
  } else {
102
- rawFrame.set(head.subarray(0, remaining), dstOff);
121
+ rawFrame.set(curr.subarray(chunkOffset, chunkOffset + remaining), dstOff);
103
122
  dstOff += remaining;
104
- chunks[0] = head.subarray(remaining);
123
+ chunkOffset += remaining;
105
124
  remaining = 0;
106
125
  }
107
126
  }
@@ -126,6 +145,16 @@ TCPTransport.prototype._attachSocketReader = function(socket) {
126
145
  console.error(`[TCPTransport ${this.id}] Error decoding frame:`, err.message);
127
146
  }
128
147
  }
148
+
149
+ // Amortized array compaction to prevent unbounded array growth without O(N) shift()
150
+ if (bufferedLength === 0) {
151
+ chunks.length = 0;
152
+ chunkHead = 0;
153
+ chunkOffset = 0;
154
+ } else if (chunkHead >= 128 && chunkHead > (chunks.length >> 1)) {
155
+ chunks.splice(0, chunkHead);
156
+ chunkHead = 0;
157
+ }
129
158
  });
130
159
 
131
160
  socket.on('close', () => {
@@ -219,18 +248,19 @@ TCPTransport.prototype.sendv = function(peer, buffers) {
219
248
 
220
249
  const pool = this._peerSocketPool ? this._peerSocketPool.get(peer) : null;
221
250
  if (pool && pool.length > 0) {
222
- const active = pool.filter((s) => !s.destroyed && s.writable);
223
- if (active.length > 0) {
224
- let idx = this._channelIndex.get(peer) || 0;
225
- const socket = active[idx % active.length];
226
- this._channelIndex.set(peer, idx + 1);
227
-
228
- socket.cork();
229
- for (let i = 0; i < buffers.length; i++) {
230
- socket.write(buffers[i]);
251
+ let idx = this._channelIndex.get(peer) || 0;
252
+ const poolLen = pool.length;
253
+ for (let attempts = 0; attempts < poolLen; attempts++) {
254
+ const s = pool[(idx + attempts) % poolLen];
255
+ if (!s.destroyed && s.writable) {
256
+ this._channelIndex.set(peer, idx + attempts + 1);
257
+ s.cork();
258
+ for (let i = 0; i < buffers.length; i++) {
259
+ s.write(buffers[i]);
260
+ }
261
+ s.uncork();
262
+ return;
231
263
  }
232
- socket.uncork();
233
- return;
234
264
  }
235
265
  }
236
266
 
@@ -252,13 +282,15 @@ TCPTransport.prototype.send = function(peer, buffer) {
252
282
 
253
283
  const pool = this._peerSocketPool ? this._peerSocketPool.get(peer) : null;
254
284
  if (pool && pool.length > 0) {
255
- const active = pool.filter((s) => !s.destroyed && s.writable);
256
- if (active.length > 0) {
257
- let idx = this._channelIndex.get(peer) || 0;
258
- const socket = active[idx % active.length];
259
- this._channelIndex.set(peer, idx + 1);
260
- socket.write(buffer);
261
- return;
285
+ let idx = this._channelIndex.get(peer) || 0;
286
+ const poolLen = pool.length;
287
+ for (let attempts = 0; attempts < poolLen; attempts++) {
288
+ const s = pool[(idx + attempts) % poolLen];
289
+ if (!s.destroyed && s.writable) {
290
+ this._channelIndex.set(peer, idx + attempts + 1);
291
+ s.write(buffer);
292
+ return;
293
+ }
262
294
  }
263
295
  }
264
296
 
@@ -450,8 +482,8 @@ export async function serve({
450
482
  transport,
451
483
  localNode: 2,
452
484
  remoteNode: 1,
453
- maxInflightBatches: 16,
454
- maxInflightBytes: 64 * 1024 * 1024
485
+ maxInflightBatches: 32,
486
+ maxInflightBytes: 128 * 1024 * 1024
455
487
  });
456
488
 
457
489
  const receiver = new ReceiverPipeline({
@@ -478,7 +510,13 @@ export async function serve({
478
510
  const [metaBuf, payloadBuf] = msg.buffers;
479
511
  const entries = unpackGeneration(metaBuf, payloadBuf);
480
512
  if (entries.length > 0 && entries[0].type === Opcodes.PULL_REQUEST) {
481
- const { remotePath, verifiedChunks = [] } = unpackPullRequest(entries[0].key);
513
+ const {
514
+ remotePath,
515
+ verifiedChunks = [],
516
+ chunkSize,
517
+ maxEntries,
518
+ maxBytes
519
+ } = unpackPullRequest(entries[0].key);
482
520
  let resolved = path.isAbsolute(remotePath) ? remotePath : path.resolve(destinationDir, remotePath);
483
521
  if (!existsSync(resolved)) {
484
522
  const alt = path.resolve(destinationDir, remotePath);
@@ -513,9 +551,18 @@ export async function serve({
513
551
  }));
514
552
  }
515
553
 
554
+ const senderOpts = {
555
+ maxImmutableBuffers: 32,
556
+ maxConcurrentFlushes: 32
557
+ };
558
+ if (chunkSize) senderOpts.chunkSize = chunkSize;
559
+ if (maxEntries) senderOpts.maxEntries = maxEntries;
560
+ if (maxBytes) senderOpts.maxBytes = maxBytes;
561
+
516
562
  const sender = new SenderPipeline({
517
563
  replication,
518
564
  manifest,
565
+ options: senderOpts,
519
566
  knownResumeState,
520
567
  metrics
521
568
  });
@@ -604,8 +651,8 @@ export async function send({
604
651
  transport,
605
652
  localNode: 1,
606
653
  remoteNode: 2,
607
- maxInflightBatches: pipelineOptions.maxInflightBatches || 16,
608
- maxInflightBytes: pipelineOptions.maxInflightBytes || 64 * 1024 * 1024
654
+ maxInflightBatches: pipelineOptions.maxInflightBatches || 32,
655
+ maxInflightBytes: pipelineOptions.maxInflightBytes || 128 * 1024 * 1024
609
656
  });
610
657
 
611
658
  const manifest = await TransferManifest.build(sourcePath);
@@ -651,6 +698,15 @@ export async function pull({
651
698
  pipelineOptions = {},
652
699
  showProgress = true
653
700
  }) {
701
+ const isRemote = host !== '127.0.0.1' && host !== 'localhost';
702
+ const defaultChunkSize = isRemote ? 2 * 1024 * 1024 : 1024 * 1024;
703
+ const defaultMaxEntries = 8;
704
+ const defaultMaxBytes = defaultChunkSize * defaultMaxEntries;
705
+
706
+ const effectiveChunkSize = pipelineOptions.chunkSize || defaultChunkSize;
707
+ const effectiveMaxEntries = pipelineOptions.maxEntries || defaultMaxEntries;
708
+ const effectiveMaxBytes = pipelineOptions.maxBytes || defaultMaxBytes;
709
+
654
710
  const metrics = new MetricsCollector();
655
711
  const progress = showProgress ? new ProgressReporter({ role: 'receiver' }) : null;
656
712
  if (progress) {
@@ -675,8 +731,8 @@ export async function pull({
675
731
  transport,
676
732
  localNode: 1,
677
733
  remoteNode: 2,
678
- maxInflightBatches: pipelineOptions.maxInflightBatches || 16,
679
- maxInflightBytes: pipelineOptions.maxInflightBytes || 64 * 1024 * 1024
734
+ maxInflightBatches: pipelineOptions.maxInflightBatches || 32,
735
+ maxInflightBytes: pipelineOptions.maxInflightBytes || 128 * 1024 * 1024
680
736
  });
681
737
 
682
738
  const receiver = new ReceiverPipeline({
@@ -699,11 +755,17 @@ export async function pull({
699
755
  }
700
756
  } catch {}
701
757
 
702
- // Submit PULL_REQUEST batch with verified chunks for resume
758
+ // Submit PULL_REQUEST batch with verified chunks and WAN tuning
703
759
  await submitSingleMutation(
704
760
  replication,
705
761
  Opcodes.PULL_REQUEST,
706
- packPullRequest({ remotePath: sourceRemotePath, verifiedChunks })
762
+ packPullRequest({
763
+ remotePath: sourceRemotePath,
764
+ verifiedChunks,
765
+ chunkSize: effectiveChunkSize,
766
+ maxEntries: effectiveMaxEntries,
767
+ maxBytes: effectiveMaxBytes
768
+ })
707
769
  );
708
770
 
709
771
  // Wait for receiver pipeline to receive and write all files