teyyare 0.2.0 → 0.2.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/package.json +1 -1
- package/src/core/generation.js +107 -0
- package/src/index.js +6 -19
- package/src/receiver/pipeline.js +5 -7
- package/src/sender/pipeline.js +12 -7
package/package.json
CHANGED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zero-copy generation framing and buffer extraction utilities.
|
|
3
|
+
* Ensures complete compatibility across all versions of Muttafa and Raptiye.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Extract scatter/gather buffers [metaBuf, payloadBuf] from any Muttafa generation.
|
|
8
|
+
* @param {object} batch
|
|
9
|
+
* @returns {Uint8Array[]}
|
|
10
|
+
*/
|
|
11
|
+
export function extractGenerationBuffers(batch) {
|
|
12
|
+
if (typeof batch.buffers === 'function') {
|
|
13
|
+
return batch.buffers();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const store = batch.store || batch;
|
|
17
|
+
if (typeof store.buffers === 'function') {
|
|
18
|
+
return store.buffers();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Direct extraction from ArenaStore
|
|
22
|
+
const rawBuf = store._allocator?.rawBuffer;
|
|
23
|
+
const payload = rawBuf ? rawBuf.subarray(0, store._allocator.usedBytes) : new Uint8Array(0);
|
|
24
|
+
const metaBuf = new Uint8Array(store.length * 25);
|
|
25
|
+
const view = new DataView(metaBuf.buffer, metaBuf.byteOffset, metaBuf.byteLength);
|
|
26
|
+
|
|
27
|
+
let off = 0;
|
|
28
|
+
const chunks = store._chunks || [];
|
|
29
|
+
for (let c = 0; c < chunks.length; c++) {
|
|
30
|
+
const chunk = chunks[c];
|
|
31
|
+
for (let i = 0; i < chunk.count; i++) {
|
|
32
|
+
view.setFloat64(off, chunk.seqs[i], true);
|
|
33
|
+
view.setUint8(off + 8, chunk.types[i]);
|
|
34
|
+
view.setUint32(off + 9, chunk.keyOffsets[i], true);
|
|
35
|
+
view.setUint32(off + 13, chunk.keyLengths[i], true);
|
|
36
|
+
view.setUint32(off + 17, chunk.valOffsets[i], true);
|
|
37
|
+
view.setUint32(off + 21, chunk.valLengths[i], true);
|
|
38
|
+
off += 25;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return [metaBuf, payload];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Fast zero-copy generator / unpacker for received generation buffers.
|
|
47
|
+
* @param {Uint8Array} metaBuf
|
|
48
|
+
* @param {Uint8Array} payloadBuf
|
|
49
|
+
* @returns {Array<{ seq: number, type: number, key: Uint8Array, val: Uint8Array }>}
|
|
50
|
+
*/
|
|
51
|
+
export function unpackGeneration(metaBuf, payloadBuf) {
|
|
52
|
+
const entryCount = (metaBuf.byteLength / 25) | 0;
|
|
53
|
+
const view = new DataView(metaBuf.buffer, metaBuf.byteOffset, metaBuf.byteLength);
|
|
54
|
+
const entries = new Array(entryCount);
|
|
55
|
+
|
|
56
|
+
let off = 0;
|
|
57
|
+
for (let i = 0; i < entryCount; i++) {
|
|
58
|
+
const seq = view.getFloat64(off, true);
|
|
59
|
+
const type = view.getUint8(off + 8);
|
|
60
|
+
const keyOffset = view.getUint32(off + 9, true);
|
|
61
|
+
const keyLength = view.getUint32(off + 13, true);
|
|
62
|
+
const valOffset = view.getUint32(off + 17, true);
|
|
63
|
+
const valLength = view.getUint32(off + 21, true);
|
|
64
|
+
off += 25;
|
|
65
|
+
|
|
66
|
+
entries[i] = {
|
|
67
|
+
seq,
|
|
68
|
+
type,
|
|
69
|
+
key: payloadBuf.subarray(keyOffset, keyOffset + keyLength),
|
|
70
|
+
val: payloadBuf.subarray(valOffset, valOffset + valLength)
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return entries;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Pack a single-mutation batch directly into scatter/gather wire buffers.
|
|
79
|
+
* @param {number} generationId
|
|
80
|
+
* @param {number} opcode
|
|
81
|
+
* @param {Uint8Array} key
|
|
82
|
+
* @param {Uint8Array} [val]
|
|
83
|
+
* @returns {object}
|
|
84
|
+
*/
|
|
85
|
+
export function packSingleMutationBatch(generationId, opcode, key, val = new Uint8Array(0)) {
|
|
86
|
+
const metaBuf = new Uint8Array(25);
|
|
87
|
+
const view = new DataView(metaBuf.buffer, metaBuf.byteOffset, 25);
|
|
88
|
+
view.setFloat64(0, 1, true); // seq
|
|
89
|
+
view.setUint8(8, opcode);
|
|
90
|
+
view.setUint32(9, 0, true); // keyOffset
|
|
91
|
+
view.setUint32(13, key.byteLength, true); // keyLength
|
|
92
|
+
view.setUint32(17, key.byteLength, true); // valOffset
|
|
93
|
+
view.setUint32(21, val.byteLength, true); // valLength
|
|
94
|
+
|
|
95
|
+
const payloadBuf = new Uint8Array(key.byteLength + val.byteLength);
|
|
96
|
+
payloadBuf.set(key, 0);
|
|
97
|
+
payloadBuf.set(val, key.byteLength);
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
generationId,
|
|
101
|
+
firstSequence: 1,
|
|
102
|
+
lastSequence: 1,
|
|
103
|
+
entryCount: 1,
|
|
104
|
+
byteLength: metaBuf.byteLength + payloadBuf.byteLength,
|
|
105
|
+
buffers: [metaBuf, payloadBuf]
|
|
106
|
+
};
|
|
107
|
+
}
|
package/src/index.js
CHANGED
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
|
|
5
5
|
import path from 'node:path';
|
|
6
6
|
import { existsSync } from 'node:fs';
|
|
7
|
-
import { MutableBuffer, ArenaStore, BumpAllocator, FrozenArenaStore } from 'muttafa';
|
|
8
7
|
import { TCPTransport, ReplicationPipeline } from 'raptiye';
|
|
9
8
|
import { SenderPipeline } from './sender/pipeline.js';
|
|
10
9
|
import { ReceiverPipeline } from './receiver/pipeline.js';
|
|
@@ -12,6 +11,7 @@ import { TransferManifest } from './manifest/manifest.js';
|
|
|
12
11
|
import { FileResumeState } from './resume/state.js';
|
|
13
12
|
import { MetricsCollector } from './metrics/collector.js';
|
|
14
13
|
import { ProgressReporter } from './cli/progress.js';
|
|
14
|
+
import { packSingleMutationBatch, unpackGeneration } from './core/generation.js';
|
|
15
15
|
import {
|
|
16
16
|
Opcodes,
|
|
17
17
|
packChunkMeta,
|
|
@@ -39,22 +39,9 @@ export {
|
|
|
39
39
|
};
|
|
40
40
|
|
|
41
41
|
async function submitSingleMutation(replication, opcode, key, value = new Uint8Array(0)) {
|
|
42
|
-
const
|
|
43
|
-
const
|
|
44
|
-
mutable.append(1, opcode, key, value);
|
|
45
|
-
const frozen = mutable.freeze();
|
|
46
|
-
frozen.markFlushing();
|
|
47
|
-
const receipt = await replication.submitBatch({
|
|
48
|
-
generationId: frozen.generation,
|
|
49
|
-
firstSequence: frozen.firstSequence,
|
|
50
|
-
lastSequence: frozen.lastSequence,
|
|
51
|
-
entryCount: frozen.entryCount,
|
|
52
|
-
byteLength: frozen.byteLength,
|
|
53
|
-
buffers: frozen.buffers()
|
|
54
|
-
});
|
|
42
|
+
const batch = packSingleMutationBatch(Date.now() & 0x7fffffff, opcode, key, value);
|
|
43
|
+
const receipt = await replication.submitBatch(batch);
|
|
55
44
|
await receipt.delivered();
|
|
56
|
-
frozen.markFlushed();
|
|
57
|
-
frozen.release();
|
|
58
45
|
}
|
|
59
46
|
|
|
60
47
|
/**
|
|
@@ -102,9 +89,9 @@ export async function serve({
|
|
|
102
89
|
replication.onBatch(async (msg) => {
|
|
103
90
|
if (!msg.buffers || msg.buffers.length < 2) return;
|
|
104
91
|
const [metaBuf, payloadBuf] = msg.buffers;
|
|
105
|
-
const
|
|
106
|
-
if (
|
|
107
|
-
const { remotePath } = unpackPullRequest(
|
|
92
|
+
const entries = unpackGeneration(metaBuf, payloadBuf);
|
|
93
|
+
if (entries.length > 0 && entries[0].type === Opcodes.PULL_REQUEST) {
|
|
94
|
+
const { remotePath } = unpackPullRequest(entries[0].key);
|
|
108
95
|
let resolved = path.isAbsolute(remotePath) ? remotePath : path.resolve(destinationDir, remotePath);
|
|
109
96
|
if (!existsSync(resolved)) {
|
|
110
97
|
const alt = path.resolve(destinationDir, remotePath);
|
package/src/receiver/pipeline.js
CHANGED
|
@@ -8,9 +8,9 @@ import fs from 'node:fs/promises';
|
|
|
8
8
|
import { existsSync, createReadStream } from 'node:fs';
|
|
9
9
|
import path from 'node:path';
|
|
10
10
|
import crypto from 'node:crypto';
|
|
11
|
-
import { FrozenArenaStore } from 'muttafa';
|
|
12
11
|
import { crc32 } from 'raptiye';
|
|
13
12
|
import { Opcodes, unpackChunkMeta, unpackFileBegin, unpackTransferBegin, unpackFileEnd, unpackPullError } from '../core/protocol.js';
|
|
13
|
+
import { unpackGeneration } from '../core/generation.js';
|
|
14
14
|
import { FileResumeState } from '../resume/state.js';
|
|
15
15
|
|
|
16
16
|
export class ReceiverPipeline {
|
|
@@ -70,14 +70,12 @@ export class ReceiverPipeline {
|
|
|
70
70
|
this.metrics.generationBytes += (metaBuf.byteLength + payloadBuf.byteLength);
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
// Zero-copy
|
|
74
|
-
const
|
|
75
|
-
const count =
|
|
73
|
+
// Zero-copy unpacking of generation entries
|
|
74
|
+
const entries = unpackGeneration(metaBuf, payloadBuf);
|
|
75
|
+
const count = entries.length;
|
|
76
76
|
|
|
77
77
|
for (let i = 0; i < count; i++) {
|
|
78
|
-
const type =
|
|
79
|
-
const key = store.getKey(i);
|
|
80
|
-
const val = store.getValue(i);
|
|
78
|
+
const { type, key, val } = entries[i];
|
|
81
79
|
|
|
82
80
|
if (this.metrics) {
|
|
83
81
|
this.metrics.mutations++;
|
package/src/sender/pipeline.js
CHANGED
|
@@ -17,6 +17,8 @@ import {
|
|
|
17
17
|
packTransferBegin,
|
|
18
18
|
packTransferEnd
|
|
19
19
|
} from '../core/protocol.js';
|
|
20
|
+
import { extractGenerationBuffers } from '../core/generation.js';
|
|
21
|
+
import { FileResumeState } from '../resume/state.js';
|
|
20
22
|
|
|
21
23
|
class RaptiyeSink extends Sink {
|
|
22
24
|
/**
|
|
@@ -36,20 +38,23 @@ class RaptiyeSink extends Sink {
|
|
|
36
38
|
* @param {import('muttafa').ImmutableBuffer} batch
|
|
37
39
|
*/
|
|
38
40
|
async flush(batch) {
|
|
39
|
-
const buffers = batch
|
|
41
|
+
const buffers = extractGenerationBuffers(batch);
|
|
40
42
|
const t0 = Date.now();
|
|
43
|
+
const generationId = batch.generationId || batch.generation || 1;
|
|
44
|
+
const entryCount = batch.entryCount || batch.store?.length || 1;
|
|
45
|
+
const byteLength = batch.byteLength || (buffers[0].byteLength + buffers[1].byteLength);
|
|
41
46
|
|
|
42
47
|
if (this.metrics) {
|
|
43
48
|
this.metrics.generations++;
|
|
44
|
-
this.metrics.generationBytes +=
|
|
49
|
+
this.metrics.generationBytes += byteLength;
|
|
45
50
|
}
|
|
46
51
|
|
|
47
52
|
const receipt = await this.replication.submitBatch({
|
|
48
|
-
generationId
|
|
49
|
-
firstSequence: batch.firstSequence,
|
|
50
|
-
lastSequence: batch.lastSequence,
|
|
51
|
-
entryCount
|
|
52
|
-
byteLength
|
|
53
|
+
generationId,
|
|
54
|
+
firstSequence: batch.firstSequence || 1,
|
|
55
|
+
lastSequence: batch.lastSequence || 1,
|
|
56
|
+
entryCount,
|
|
57
|
+
byteLength,
|
|
53
58
|
buffers
|
|
54
59
|
});
|
|
55
60
|
|