@vuer-ai/vuer-rtc-server 0.2.0 → 0.2.2
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/.env +1 -0
- package/S3_COMPRESSION_GUIDE.md +233 -0
- package/dist/archive/ArchivalService.d.ts +117 -0
- package/dist/archive/ArchivalService.d.ts.map +1 -0
- package/dist/archive/ArchivalService.js +181 -0
- package/dist/archive/ArchivalService.js.map +1 -0
- package/dist/broker/InMemoryBroker.d.ts +2 -0
- package/dist/broker/InMemoryBroker.d.ts.map +1 -1
- package/dist/broker/InMemoryBroker.js +4 -0
- package/dist/broker/InMemoryBroker.js.map +1 -1
- package/dist/compression/CompressionUtils.d.ts +57 -0
- package/dist/compression/CompressionUtils.d.ts.map +1 -0
- package/dist/compression/CompressionUtils.js +90 -0
- package/dist/compression/CompressionUtils.js.map +1 -0
- package/dist/compression/index.d.ts +7 -0
- package/dist/compression/index.d.ts.map +1 -0
- package/dist/compression/index.js +7 -0
- package/dist/compression/index.js.map +1 -0
- package/dist/journal/CoalescingService.d.ts +63 -0
- package/dist/journal/CoalescingService.d.ts.map +1 -0
- package/dist/journal/CoalescingService.js +507 -0
- package/dist/journal/CoalescingService.js.map +1 -0
- package/dist/journal/JournalRLE.d.ts +81 -0
- package/dist/journal/JournalRLE.d.ts.map +1 -0
- package/dist/journal/JournalRLE.js +199 -0
- package/dist/journal/JournalRLE.js.map +1 -0
- package/dist/journal/JournalService.d.ts +7 -3
- package/dist/journal/JournalService.d.ts.map +1 -1
- package/dist/journal/JournalService.js +152 -12
- package/dist/journal/JournalService.js.map +1 -1
- package/dist/journal/RLECompression.d.ts +73 -0
- package/dist/journal/RLECompression.d.ts.map +1 -0
- package/dist/journal/RLECompression.js +152 -0
- package/dist/journal/RLECompression.js.map +1 -0
- package/dist/journal/rle-demo.d.ts +8 -0
- package/dist/journal/rle-demo.d.ts.map +1 -0
- package/dist/journal/rle-demo.js +159 -0
- package/dist/journal/rle-demo.js.map +1 -0
- package/dist/persistence/S3ColdStorage.d.ts +62 -0
- package/dist/persistence/S3ColdStorage.d.ts.map +1 -0
- package/dist/persistence/S3ColdStorage.js +88 -0
- package/dist/persistence/S3ColdStorage.js.map +1 -0
- package/dist/persistence/S3ColdStorageIntegration.d.ts +78 -0
- package/dist/persistence/S3ColdStorageIntegration.d.ts.map +1 -0
- package/dist/persistence/S3ColdStorageIntegration.js +93 -0
- package/dist/persistence/S3ColdStorageIntegration.js.map +1 -0
- package/dist/serve.d.ts +2 -0
- package/dist/serve.d.ts.map +1 -1
- package/dist/serve.js +623 -15
- package/dist/serve.js.map +1 -1
- package/docs/RLE_COMPRESSION.md +397 -0
- package/examples/compression-example.ts +259 -0
- package/package.json +14 -14
- package/src/archive/ArchivalService.ts +250 -0
- package/src/broker/InMemoryBroker.ts +5 -0
- package/src/compression/CompressionUtils.ts +113 -0
- package/src/compression/index.ts +14 -0
- package/src/journal/COALESCING.md +267 -0
- package/src/journal/CoalescingService.ts +626 -0
- package/src/journal/JournalRLE.ts +265 -0
- package/src/journal/JournalService.ts +163 -11
- package/src/journal/RLECompression.ts +210 -0
- package/src/journal/rle-demo.ts +193 -0
- package/src/serve.ts +702 -15
- package/tests/benchmark/journal-optimization-benchmark.test.ts +482 -0
- package/tests/compression/compression.test.ts +343 -0
- package/tests/integration/repositories.test.ts +89 -0
- package/tests/journal/compaction-load-bug.test.ts +409 -0
- package/tests/journal/compaction.test.ts +42 -2
- package/tests/journal/journal-rle.test.ts +511 -0
- package/tests/journal/lww-ordering-bug.test.ts +248 -0
- package/tests/journal/multi-session-coalescing.test.ts +871 -0
- package/tests/journal/rle-compression.test.ts +526 -0
- package/tests/journal/text-coalescing.test.ts +210 -0
- package/tests/unit/s3-compression.test.ts +257 -0
- package/PHASE1_SUMMARY.md +0 -94
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RLE Compression Demonstration
|
|
3
|
+
*
|
|
4
|
+
* Shows real-world compression stats for different journal patterns.
|
|
5
|
+
* Run with: node --loader ts-node/esm src/journal/rle-demo.ts
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { CRDTMessage } from '@vuer-ai/vuer-rtc';
|
|
9
|
+
import {
|
|
10
|
+
encodeJournalRLE,
|
|
11
|
+
getCompressionStats,
|
|
12
|
+
verifyRLEIntegrity,
|
|
13
|
+
} from './JournalRLE.js';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Create a test message
|
|
17
|
+
*/
|
|
18
|
+
function createMessage(
|
|
19
|
+
id: string,
|
|
20
|
+
sessionId: string,
|
|
21
|
+
lamportTime: number,
|
|
22
|
+
timestamp: number,
|
|
23
|
+
key: string = 'cube-1',
|
|
24
|
+
value: any = [lamportTime, 0, 0]
|
|
25
|
+
): CRDTMessage {
|
|
26
|
+
return {
|
|
27
|
+
id,
|
|
28
|
+
sessionId,
|
|
29
|
+
clock: { [sessionId]: lamportTime },
|
|
30
|
+
lamportTime,
|
|
31
|
+
timestamp,
|
|
32
|
+
ops: [
|
|
33
|
+
{
|
|
34
|
+
key,
|
|
35
|
+
otype: 'vector3.set',
|
|
36
|
+
path: 'position',
|
|
37
|
+
value,
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Run demonstrations
|
|
45
|
+
*/
|
|
46
|
+
function main() {
|
|
47
|
+
console.log('\n=== RLE Journal Compression Demo ===\n');
|
|
48
|
+
|
|
49
|
+
// Scenario 1: Single user, sequential edits
|
|
50
|
+
console.log('SCENARIO 1: Single user (50 sequential edits)');
|
|
51
|
+
console.log('─'.repeat(50));
|
|
52
|
+
{
|
|
53
|
+
const messages = Array.from({ length: 50 }, (_, i) =>
|
|
54
|
+
createMessage(`msg-${i}`, 'user-1', i, 1000 + i)
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
const encoded = encodeJournalRLE(messages);
|
|
58
|
+
const stats = getCompressionStats(encoded);
|
|
59
|
+
const verification = verifyRLEIntegrity(messages, encoded);
|
|
60
|
+
|
|
61
|
+
console.log(`Messages: ${stats.originalBytes.toLocaleString()} bytes`);
|
|
62
|
+
console.log(`Encoded: ${stats.compressedBytes.toLocaleString()} bytes`);
|
|
63
|
+
console.log(`Segments: ${stats.segmentCount}`);
|
|
64
|
+
console.log(`Avg/Segment: ${stats.avgMessagesPerSegment.toFixed(1)}`);
|
|
65
|
+
console.log(`Ratio: ${stats.ratio.toFixed(3)}`);
|
|
66
|
+
console.log(`Saved: ${stats.percentSaved.toFixed(1)}%`);
|
|
67
|
+
console.log(`Integrity: ${verification.valid ? 'PASS' : 'FAIL'}`);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Scenario 2: Two users, alternating edits
|
|
71
|
+
console.log('\n\nSCENARIO 2: Two users (50 alternating edits)');
|
|
72
|
+
console.log('─'.repeat(50));
|
|
73
|
+
{
|
|
74
|
+
const messages: CRDTMessage[] = [];
|
|
75
|
+
for (let i = 0; i < 50; i++) {
|
|
76
|
+
const user = i % 2 === 0 ? 'user-1' : 'user-2';
|
|
77
|
+
messages.push(createMessage(`msg-${i}`, user, i, 1000 + i));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const encoded = encodeJournalRLE(messages);
|
|
81
|
+
const stats = getCompressionStats(encoded);
|
|
82
|
+
const verification = verifyRLEIntegrity(messages, encoded);
|
|
83
|
+
|
|
84
|
+
console.log(`Messages: ${stats.originalBytes.toLocaleString()} bytes`);
|
|
85
|
+
console.log(`Encoded: ${stats.compressedBytes.toLocaleString()} bytes`);
|
|
86
|
+
console.log(`Segments: ${stats.segmentCount}`);
|
|
87
|
+
console.log(`Avg/Segment: ${stats.avgMessagesPerSegment.toFixed(1)}`);
|
|
88
|
+
console.log(`Ratio: ${stats.ratio.toFixed(3)}`);
|
|
89
|
+
console.log(`Saved: ${stats.percentSaved.toFixed(1)}%`);
|
|
90
|
+
console.log(`Integrity: ${verification.valid ? 'PASS' : 'FAIL'}`);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Scenario 3: Four users, burst patterns
|
|
94
|
+
console.log('\n\nSCENARIO 3: Four users (100 bursts of 5 edits)');
|
|
95
|
+
console.log('─'.repeat(50));
|
|
96
|
+
{
|
|
97
|
+
const messages: CRDTMessage[] = [];
|
|
98
|
+
let msgId = 0;
|
|
99
|
+
let lamportTime = 0;
|
|
100
|
+
|
|
101
|
+
for (let burst = 0; burst < 20; burst++) {
|
|
102
|
+
const user = `user-${(burst % 4) + 1}`;
|
|
103
|
+
for (let i = 0; i < 5; i++) {
|
|
104
|
+
messages.push(
|
|
105
|
+
createMessage(`msg-${msgId++}`, user, lamportTime++, 1000 + lamportTime)
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const encoded = encodeJournalRLE(messages);
|
|
111
|
+
const stats = getCompressionStats(encoded);
|
|
112
|
+
const verification = verifyRLEIntegrity(messages, encoded);
|
|
113
|
+
|
|
114
|
+
console.log(`Messages: ${stats.originalBytes.toLocaleString()} bytes`);
|
|
115
|
+
console.log(`Encoded: ${stats.compressedBytes.toLocaleString()} bytes`);
|
|
116
|
+
console.log(`Segments: ${stats.segmentCount}`);
|
|
117
|
+
console.log(`Avg/Segment: ${stats.avgMessagesPerSegment.toFixed(1)}`);
|
|
118
|
+
console.log(`Ratio: ${stats.ratio.toFixed(3)}`);
|
|
119
|
+
console.log(`Saved: ${stats.percentSaved.toFixed(1)}%`);
|
|
120
|
+
console.log(`Integrity: ${verification.valid ? 'PASS' : 'FAIL'}`);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Scenario 4: Realistic editing with large ops
|
|
124
|
+
console.log('\n\nSCENARIO 4: Large operations (node tree modifications)');
|
|
125
|
+
console.log('─'.repeat(50));
|
|
126
|
+
{
|
|
127
|
+
const messages: CRDTMessage[] = [];
|
|
128
|
+
|
|
129
|
+
for (let i = 0; i < 30; i++) {
|
|
130
|
+
const user = i < 15 ? 'user-1' : 'user-2';
|
|
131
|
+
const msg: CRDTMessage = {
|
|
132
|
+
id: `msg-${i}`,
|
|
133
|
+
sessionId: user,
|
|
134
|
+
clock: { [user]: i },
|
|
135
|
+
lamportTime: i,
|
|
136
|
+
timestamp: 1000 + i,
|
|
137
|
+
ops: [
|
|
138
|
+
{
|
|
139
|
+
key: `node-${i % 5}`,
|
|
140
|
+
otype: 'node.insert',
|
|
141
|
+
path: 'children',
|
|
142
|
+
value: {
|
|
143
|
+
key: `child-${i}`,
|
|
144
|
+
tag: 'Mesh',
|
|
145
|
+
name: `Node ${i}`,
|
|
146
|
+
position: [i * 0.1, 0, 0],
|
|
147
|
+
rotation: [0, Math.PI * (i % 4) / 4, 0],
|
|
148
|
+
scale: [1, 1, 1],
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
key: `node-${i % 5}`,
|
|
153
|
+
otype: 'vector3.set',
|
|
154
|
+
path: 'color',
|
|
155
|
+
value: [Math.random(), Math.random(), Math.random()],
|
|
156
|
+
},
|
|
157
|
+
],
|
|
158
|
+
};
|
|
159
|
+
messages.push(msg);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const encoded = encodeJournalRLE(messages);
|
|
163
|
+
const stats = getCompressionStats(encoded);
|
|
164
|
+
const verification = verifyRLEIntegrity(messages, encoded);
|
|
165
|
+
|
|
166
|
+
console.log(`Messages: ${stats.originalBytes.toLocaleString()} bytes`);
|
|
167
|
+
console.log(`Encoded: ${stats.compressedBytes.toLocaleString()} bytes`);
|
|
168
|
+
console.log(`Segments: ${stats.segmentCount}`);
|
|
169
|
+
console.log(`Avg/Segment: ${stats.avgMessagesPerSegment.toFixed(1)}`);
|
|
170
|
+
console.log(`Ratio: ${stats.ratio.toFixed(3)}`);
|
|
171
|
+
console.log(`Saved: ${stats.percentSaved.toFixed(1)}%`);
|
|
172
|
+
console.log(`Integrity: ${verification.valid ? 'PASS' : 'FAIL'}`);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Summary
|
|
176
|
+
console.log('\n\n=== Summary ===');
|
|
177
|
+
console.log('─'.repeat(50));
|
|
178
|
+
console.log(`
|
|
179
|
+
RLE encoding works by:
|
|
180
|
+
1. Grouping consecutive messages from the same agent
|
|
181
|
+
2. Storing agent ID once per segment instead of per message
|
|
182
|
+
3. Reducing redundancy in causality metadata
|
|
183
|
+
|
|
184
|
+
Key insights:
|
|
185
|
+
- More effective with binary codecs (not JSON strings)
|
|
186
|
+
- Best for single-agent or burst patterns
|
|
187
|
+
- Maintains all CRDT semantics and causality
|
|
188
|
+
- Zero overhead for integrity verification
|
|
189
|
+
- Ready for integration with storage backends
|
|
190
|
+
`);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
main();
|