@topgunbuild/server 0.2.0 → 0.3.0
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.d.mts +2960 -242
- package/dist/index.d.ts +2960 -242
- package/dist/index.js +7087 -587
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +7045 -556
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -2
package/dist/index.d.mts
CHANGED
|
@@ -1,333 +1,3051 @@
|
|
|
1
|
-
import { LWWRecord, ORMapRecord, Principal, PermissionPolicy, LWWMap, ORMap, PermissionType } from '@topgunbuild/core';
|
|
1
|
+
import { Timestamp, LWWRecord, ORMapRecord, Principal, PermissionPolicy, ConsistencyLevel, ReplicationConfig, LWWMap, ORMap, PermissionType, MigrationConfig, MigrationStatus, MigrationMetrics, PartitionMap, PartitionInfo, PartitionChange, ReplicationLag, ReplicationHealth, ReplicationResult } from '@topgunbuild/core';
|
|
2
2
|
import { WebSocket } from 'ws';
|
|
3
3
|
import { PoolConfig, Pool } from 'pg';
|
|
4
4
|
import pino from 'pino';
|
|
5
|
+
import { EventEmitter } from 'events';
|
|
5
6
|
|
|
6
|
-
type ORMapValue<V> = {
|
|
7
|
-
type: 'OR';
|
|
8
|
-
records: ORMapRecord<V>[];
|
|
9
|
-
};
|
|
10
|
-
type ORMapTombstones = {
|
|
11
|
-
type: 'OR_TOMBSTONES';
|
|
12
|
-
tags: string[];
|
|
13
|
-
};
|
|
14
|
-
type StorageValue<V> = LWWRecord<V> | ORMapValue<V> | ORMapTombstones;
|
|
15
7
|
/**
|
|
16
|
-
*
|
|
17
|
-
* Aligned with specifications/06_SERVER_INTEGRATIONS.md
|
|
8
|
+
* TaskletScheduler — Cooperative multitasking for long-running operations.
|
|
18
9
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
10
|
+
* Inspired by Hazelcast's Tasklet pattern, this scheduler allows long operations
|
|
11
|
+
* to yield control back to the event loop periodically, preventing event loop
|
|
12
|
+
* blocking and maintaining responsiveness.
|
|
13
|
+
*
|
|
14
|
+
* Key concepts:
|
|
15
|
+
* - Tasklet: A unit of work that can be paused and resumed
|
|
16
|
+
* - Time budget: Maximum time a tasklet can run before yielding
|
|
17
|
+
* - Cooperative scheduling: Tasklets voluntarily yield when time budget is exhausted
|
|
21
18
|
*/
|
|
22
|
-
|
|
19
|
+
/**
|
|
20
|
+
* Progress state returned by a tasklet after each execution step.
|
|
21
|
+
*/
|
|
22
|
+
type ProgressState = 'DONE' | 'MADE_PROGRESS' | 'NO_PROGRESS';
|
|
23
|
+
/**
|
|
24
|
+
* Interface for a tasklet that can be scheduled.
|
|
25
|
+
*/
|
|
26
|
+
interface Tasklet<T = void> {
|
|
27
|
+
/** Unique name for logging/metrics */
|
|
28
|
+
readonly name: string;
|
|
23
29
|
/**
|
|
24
|
-
*
|
|
30
|
+
* Execute one chunk of work.
|
|
31
|
+
* Should check time budget and return appropriate state.
|
|
25
32
|
*/
|
|
26
|
-
|
|
33
|
+
call(): ProgressState;
|
|
27
34
|
/**
|
|
28
|
-
*
|
|
35
|
+
* Get the result after tasklet is DONE.
|
|
36
|
+
* Only valid when call() returns 'DONE'.
|
|
29
37
|
*/
|
|
30
|
-
|
|
38
|
+
getResult(): T;
|
|
31
39
|
/**
|
|
32
|
-
*
|
|
33
|
-
* Called when a client requests a key that is not in the Server RAM (if using partial loading),
|
|
34
|
-
* or during sync.
|
|
40
|
+
* Called when tasklet is cancelled before completion.
|
|
35
41
|
*/
|
|
36
|
-
|
|
42
|
+
onCancel?(): void;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Configuration for TaskletScheduler.
|
|
46
|
+
*/
|
|
47
|
+
interface TaskletSchedulerConfig {
|
|
48
|
+
/** Default time budget per tasklet execution in ms (default: 5) */
|
|
49
|
+
defaultTimeBudgetMs?: number;
|
|
50
|
+
/** Maximum concurrent tasklets (default: 10) */
|
|
51
|
+
maxConcurrent?: number;
|
|
52
|
+
/** Interval between scheduler ticks in ms (default: 1) */
|
|
53
|
+
tickIntervalMs?: number;
|
|
54
|
+
/** Enable metrics collection (default: true) */
|
|
55
|
+
metricsEnabled?: boolean;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Metrics for monitoring scheduler performance.
|
|
59
|
+
*/
|
|
60
|
+
interface TaskletSchedulerStats {
|
|
61
|
+
/** Total tasklets scheduled */
|
|
62
|
+
totalScheduled: number;
|
|
63
|
+
/** Tasklets currently running */
|
|
64
|
+
activeTasklets: number;
|
|
65
|
+
/** Tasklets completed successfully */
|
|
66
|
+
completedTasklets: number;
|
|
67
|
+
/** Tasklets cancelled */
|
|
68
|
+
cancelledTasklets: number;
|
|
69
|
+
/** Total iterations across all tasklets */
|
|
70
|
+
totalIterations: number;
|
|
71
|
+
/** Average iterations per tasklet */
|
|
72
|
+
avgIterationsPerTasklet: number;
|
|
73
|
+
/** Tasklets that completed in a single iteration */
|
|
74
|
+
singleIterationCompletions: number;
|
|
75
|
+
/** Time spent in tasklet execution (ms) */
|
|
76
|
+
totalExecutionTimeMs: number;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* TaskletScheduler manages cooperative multitasking for long-running operations.
|
|
80
|
+
*
|
|
81
|
+
* Usage:
|
|
82
|
+
* ```typescript
|
|
83
|
+
* const scheduler = new TaskletScheduler();
|
|
84
|
+
*
|
|
85
|
+
* // Schedule a tasklet
|
|
86
|
+
* const result = await scheduler.schedule(new QueryExecutionTasklet(records, query));
|
|
87
|
+
*
|
|
88
|
+
* // Cancel all running tasklets
|
|
89
|
+
* scheduler.cancelAll();
|
|
90
|
+
*
|
|
91
|
+
* // Shutdown scheduler
|
|
92
|
+
* scheduler.shutdown();
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
95
|
+
declare class TaskletScheduler {
|
|
96
|
+
private readonly config;
|
|
97
|
+
private readonly activeTasklets;
|
|
98
|
+
private tickTimer;
|
|
99
|
+
private isRunning;
|
|
100
|
+
private isShuttingDown;
|
|
101
|
+
private totalScheduled;
|
|
102
|
+
private completedTasklets;
|
|
103
|
+
private cancelledTasklets;
|
|
104
|
+
private totalIterations;
|
|
105
|
+
private singleIterationCompletions;
|
|
106
|
+
private totalExecutionTimeMs;
|
|
107
|
+
constructor(config?: TaskletSchedulerConfig);
|
|
37
108
|
/**
|
|
38
|
-
*
|
|
39
|
-
*
|
|
109
|
+
* Schedule a tasklet for execution.
|
|
110
|
+
* Returns a promise that resolves when the tasklet completes.
|
|
40
111
|
*/
|
|
41
|
-
|
|
112
|
+
schedule<T>(tasklet: Tasklet<T>): Promise<T>;
|
|
42
113
|
/**
|
|
43
|
-
*
|
|
44
|
-
*
|
|
114
|
+
* Run a tasklet synchronously (blocking).
|
|
115
|
+
* Useful for small operations or when cooperative scheduling isn't needed.
|
|
45
116
|
*/
|
|
46
|
-
|
|
117
|
+
runSync<T>(tasklet: Tasklet<T>): T;
|
|
47
118
|
/**
|
|
48
|
-
*
|
|
119
|
+
* Cancel a specific tasklet by name pattern.
|
|
120
|
+
* Returns the number of tasklets cancelled.
|
|
49
121
|
*/
|
|
50
|
-
|
|
122
|
+
cancel(namePattern: string | RegExp): number;
|
|
51
123
|
/**
|
|
52
|
-
*
|
|
53
|
-
* Used for efficient batch writes to the DB.
|
|
124
|
+
* Cancel all running tasklets.
|
|
54
125
|
*/
|
|
55
|
-
|
|
126
|
+
cancelAll(): number;
|
|
56
127
|
/**
|
|
57
|
-
*
|
|
128
|
+
* Get scheduler statistics.
|
|
58
129
|
*/
|
|
59
|
-
|
|
130
|
+
getStats(): TaskletSchedulerStats;
|
|
60
131
|
/**
|
|
61
|
-
*
|
|
132
|
+
* Reset statistics.
|
|
62
133
|
*/
|
|
63
|
-
|
|
134
|
+
resetStats(): void;
|
|
135
|
+
/**
|
|
136
|
+
* Shutdown the scheduler.
|
|
137
|
+
* Cancels all running tasklets and stops the tick timer.
|
|
138
|
+
*/
|
|
139
|
+
shutdown(): void;
|
|
140
|
+
/**
|
|
141
|
+
* Check if scheduler is running.
|
|
142
|
+
*/
|
|
143
|
+
get running(): boolean;
|
|
144
|
+
/**
|
|
145
|
+
* Get number of active tasklets.
|
|
146
|
+
*/
|
|
147
|
+
get activeCount(): number;
|
|
148
|
+
private startScheduler;
|
|
149
|
+
private stopScheduler;
|
|
150
|
+
private scheduleTick;
|
|
151
|
+
private tick;
|
|
152
|
+
private completeTasklet;
|
|
153
|
+
private failTasklet;
|
|
154
|
+
private cancelTasklet;
|
|
64
155
|
}
|
|
65
156
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
157
|
+
/**
|
|
158
|
+
* IteratorTasklet — Base class for tasklets that iterate over collections.
|
|
159
|
+
*
|
|
160
|
+
* Provides cooperative iteration with time-budgeted chunks.
|
|
161
|
+
* Subclasses implement processItem() to handle each item.
|
|
162
|
+
*/
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Configuration for iterator tasklets.
|
|
166
|
+
*/
|
|
167
|
+
interface IteratorTaskletConfig {
|
|
168
|
+
/** Time budget per iteration in ms (default: 5) */
|
|
169
|
+
timeBudgetMs?: number;
|
|
170
|
+
/** Maximum items to process per iteration (default: 1000) */
|
|
171
|
+
maxItemsPerIteration?: number;
|
|
74
172
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
173
|
+
/**
|
|
174
|
+
* Abstract base class for iterator-based tasklets.
|
|
175
|
+
*
|
|
176
|
+
* Usage:
|
|
177
|
+
* ```typescript
|
|
178
|
+
* class MyTasklet extends IteratorTasklet<[string, Record], Record[]> {
|
|
179
|
+
* constructor(map: Map<string, Record>) {
|
|
180
|
+
* super('my-tasklet', map.entries());
|
|
181
|
+
* }
|
|
182
|
+
*
|
|
183
|
+
* protected processItem([key, record]: [string, Record]): void {
|
|
184
|
+
* if (matchesCriteria(record)) {
|
|
185
|
+
* this.results.push(record);
|
|
186
|
+
* }
|
|
187
|
+
* }
|
|
188
|
+
*
|
|
189
|
+
* getResult(): Record[] {
|
|
190
|
+
* return this.results;
|
|
191
|
+
* }
|
|
192
|
+
* }
|
|
193
|
+
* ```
|
|
194
|
+
*/
|
|
195
|
+
declare abstract class IteratorTasklet<TItem, TResult> implements Tasklet<TResult> {
|
|
196
|
+
abstract readonly name: string;
|
|
197
|
+
protected readonly config: Required<IteratorTaskletConfig>;
|
|
198
|
+
protected readonly iterator: Iterator<TItem>;
|
|
199
|
+
protected itemsProcessed: number;
|
|
200
|
+
protected isDone: boolean;
|
|
201
|
+
constructor(iterator: Iterator<TItem>, config?: IteratorTaskletConfig);
|
|
202
|
+
/**
|
|
203
|
+
* Process a single item from the iterator.
|
|
204
|
+
* Override this in subclasses.
|
|
205
|
+
*/
|
|
206
|
+
protected abstract processItem(item: TItem): void;
|
|
207
|
+
/**
|
|
208
|
+
* Get the final result after iteration completes.
|
|
209
|
+
*/
|
|
210
|
+
abstract getResult(): TResult;
|
|
211
|
+
/**
|
|
212
|
+
* Execute one chunk of iteration.
|
|
213
|
+
*/
|
|
214
|
+
call(): ProgressState;
|
|
215
|
+
/**
|
|
216
|
+
* Called when tasklet is cancelled.
|
|
217
|
+
*/
|
|
218
|
+
onCancel(): void;
|
|
219
|
+
/**
|
|
220
|
+
* Get number of items processed so far.
|
|
221
|
+
*/
|
|
222
|
+
get processed(): number;
|
|
80
223
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
224
|
+
/**
|
|
225
|
+
* Simple iterator tasklet that collects items matching a predicate.
|
|
226
|
+
*/
|
|
227
|
+
declare class FilterTasklet<T> extends IteratorTasklet<T, T[]> {
|
|
228
|
+
readonly name: string;
|
|
229
|
+
protected readonly results: T[];
|
|
230
|
+
private readonly predicate;
|
|
231
|
+
constructor(name: string, iterator: Iterator<T>, predicate: (item: T) => boolean, config?: IteratorTaskletConfig);
|
|
232
|
+
protected processItem(item: T): void;
|
|
233
|
+
getResult(): T[];
|
|
84
234
|
}
|
|
85
|
-
|
|
235
|
+
/**
|
|
236
|
+
* Iterator tasklet that transforms items.
|
|
237
|
+
*/
|
|
238
|
+
declare class MapTasklet<TIn, TOut> extends IteratorTasklet<TIn, TOut[]> {
|
|
239
|
+
readonly name: string;
|
|
240
|
+
protected readonly results: TOut[];
|
|
241
|
+
private readonly mapper;
|
|
242
|
+
constructor(name: string, iterator: Iterator<TIn>, mapper: (item: TIn) => TOut, config?: IteratorTaskletConfig);
|
|
243
|
+
protected processItem(item: TIn): void;
|
|
244
|
+
getResult(): TOut[];
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Iterator tasklet that applies a function to each item (side effects).
|
|
248
|
+
*/
|
|
249
|
+
declare class ForEachTasklet<T> extends IteratorTasklet<T, number> {
|
|
250
|
+
readonly name: string;
|
|
251
|
+
private readonly action;
|
|
252
|
+
constructor(name: string, iterator: Iterator<T>, action: (item: T) => void, config?: IteratorTaskletConfig);
|
|
253
|
+
protected processItem(item: T): void;
|
|
254
|
+
getResult(): number;
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Iterator tasklet that reduces items to a single value.
|
|
258
|
+
*/
|
|
259
|
+
declare class ReduceTasklet<T, TAccum> extends IteratorTasklet<T, TAccum> {
|
|
260
|
+
readonly name: string;
|
|
261
|
+
private accumulator;
|
|
262
|
+
private readonly reducer;
|
|
263
|
+
constructor(name: string, iterator: Iterator<T>, initialValue: TAccum, reducer: (acc: TAccum, item: T) => TAccum, config?: IteratorTaskletConfig);
|
|
264
|
+
protected processItem(item: T): void;
|
|
265
|
+
getResult(): TAccum;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* BufferPool - High-performance buffer reuse for serialization operations.
|
|
270
|
+
* Phase 2.03: Memory Pooling
|
|
271
|
+
*
|
|
272
|
+
* Reduces GC pressure by reusing pre-allocated Uint8Array buffers instead of
|
|
273
|
+
* creating new ones for each serialization operation.
|
|
274
|
+
*
|
|
275
|
+
* Reference: Hazelcast MemoryAllocator pattern
|
|
276
|
+
*/
|
|
277
|
+
interface BufferPoolConfig {
|
|
86
278
|
/**
|
|
87
|
-
*
|
|
279
|
+
* Size of each buffer chunk in bytes.
|
|
280
|
+
* Larger chunks = fewer allocations, but more memory waste.
|
|
281
|
+
* Default: 64KB (65536)
|
|
88
282
|
*/
|
|
89
|
-
|
|
283
|
+
chunkSize?: number;
|
|
90
284
|
/**
|
|
91
|
-
*
|
|
92
|
-
*
|
|
285
|
+
* Initial number of buffers to pre-allocate.
|
|
286
|
+
* Default: 16
|
|
93
287
|
*/
|
|
94
|
-
|
|
288
|
+
initialSize?: number;
|
|
95
289
|
/**
|
|
96
|
-
*
|
|
290
|
+
* Maximum buffers to keep in pool.
|
|
291
|
+
* Excess buffers are released to GC.
|
|
292
|
+
* Default: 256
|
|
97
293
|
*/
|
|
98
|
-
|
|
294
|
+
maxSize?: number;
|
|
99
295
|
/**
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
* Return null to silently drop the operation (no error sent to client, no persistence).
|
|
103
|
-
* Throwing an error will reject the operation and notify the client.
|
|
296
|
+
* Auto-shrink pool when idle buffers exceed this ratio.
|
|
297
|
+
* Default: true
|
|
104
298
|
*/
|
|
105
|
-
|
|
299
|
+
autoShrink?: boolean;
|
|
106
300
|
/**
|
|
107
|
-
*
|
|
301
|
+
* Shrink threshold - shrink if (available / maxSize) > threshold.
|
|
302
|
+
* Default: 0.75 (75% idle)
|
|
108
303
|
*/
|
|
109
|
-
|
|
304
|
+
shrinkThreshold?: number;
|
|
305
|
+
/**
|
|
306
|
+
* Enable metrics collection.
|
|
307
|
+
* Default: true
|
|
308
|
+
*/
|
|
309
|
+
metricsEnabled?: boolean;
|
|
110
310
|
}
|
|
111
|
-
|
|
112
|
-
|
|
311
|
+
interface BufferPoolStats {
|
|
312
|
+
/** Buffers currently available in pool */
|
|
313
|
+
available: number;
|
|
314
|
+
/** Buffers currently in use */
|
|
315
|
+
inUse: number;
|
|
316
|
+
/** Total buffers ever created */
|
|
317
|
+
created: number;
|
|
318
|
+
/** Total times a buffer was reused */
|
|
319
|
+
reused: number;
|
|
320
|
+
/** Reuse ratio: reused / (created + reused) */
|
|
321
|
+
reuseRatio: number;
|
|
322
|
+
/** Total bytes in pool (available buffers only) */
|
|
323
|
+
poolSizeBytes: number;
|
|
324
|
+
/** Peak concurrent usage */
|
|
325
|
+
peakUsage: number;
|
|
326
|
+
/** Times pool was exhausted (had to create new buffer) */
|
|
327
|
+
misses: number;
|
|
328
|
+
/** Chunk size configuration */
|
|
329
|
+
chunkSize: number;
|
|
330
|
+
/** Max pool size configuration */
|
|
331
|
+
maxSize: number;
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* High-performance buffer pool for serialization operations.
|
|
335
|
+
*
|
|
336
|
+
* Usage:
|
|
337
|
+
* ```typescript
|
|
338
|
+
* const pool = new BufferPool({ chunkSize: 64 * 1024 });
|
|
339
|
+
* const buffer = pool.acquire();
|
|
340
|
+
* // ... use buffer for serialization ...
|
|
341
|
+
* pool.release(buffer);
|
|
342
|
+
* ```
|
|
343
|
+
*
|
|
344
|
+
* Thread Safety:
|
|
345
|
+
* This pool is designed for single-threaded use (Node.js main thread).
|
|
346
|
+
* For worker threads, create separate pools or copy buffers.
|
|
347
|
+
*/
|
|
348
|
+
declare class BufferPool {
|
|
349
|
+
private readonly pool;
|
|
350
|
+
private readonly chunkSize;
|
|
351
|
+
private readonly maxSize;
|
|
352
|
+
private readonly autoShrink;
|
|
353
|
+
private readonly shrinkThreshold;
|
|
354
|
+
private readonly metricsEnabled;
|
|
355
|
+
private inUseCount;
|
|
356
|
+
private createdCount;
|
|
357
|
+
private reusedCount;
|
|
358
|
+
private peakUsage;
|
|
359
|
+
private missCount;
|
|
360
|
+
private readonly pooledBuffers;
|
|
361
|
+
private readonly releasedBuffers;
|
|
362
|
+
constructor(config?: BufferPoolConfig);
|
|
113
363
|
/**
|
|
114
|
-
*
|
|
115
|
-
*
|
|
364
|
+
* Acquire a buffer from the pool.
|
|
365
|
+
* Creates new buffer if pool is empty.
|
|
366
|
+
*
|
|
367
|
+
* @returns A Uint8Array of exactly chunkSize bytes
|
|
116
368
|
*/
|
|
117
|
-
|
|
369
|
+
acquire(): Uint8Array;
|
|
118
370
|
/**
|
|
119
|
-
*
|
|
120
|
-
*
|
|
371
|
+
* Release a buffer back to the pool.
|
|
372
|
+
* Buffer contents are cleared before returning to pool.
|
|
373
|
+
*
|
|
374
|
+
* @param buffer - The buffer to release
|
|
121
375
|
*/
|
|
122
|
-
|
|
376
|
+
release(buffer: Uint8Array): void;
|
|
123
377
|
/**
|
|
124
|
-
*
|
|
378
|
+
* Acquire a buffer of specific minimum size.
|
|
379
|
+
* May return a buffer larger than requested.
|
|
380
|
+
* For sizes > chunkSize, creates new buffer (not pooled).
|
|
381
|
+
*
|
|
382
|
+
* @param minSize - Minimum required size in bytes
|
|
383
|
+
* @returns A Uint8Array of at least minSize bytes
|
|
125
384
|
*/
|
|
126
|
-
|
|
385
|
+
acquireSize(minSize: number): Uint8Array;
|
|
127
386
|
/**
|
|
128
|
-
*
|
|
129
|
-
* Required for mTLS
|
|
130
|
-
* @optional
|
|
387
|
+
* Get current pool statistics.
|
|
131
388
|
*/
|
|
132
|
-
|
|
389
|
+
getStats(): BufferPoolStats;
|
|
133
390
|
/**
|
|
134
|
-
*
|
|
135
|
-
*
|
|
391
|
+
* Clear all buffers from pool.
|
|
392
|
+
* Use for shutdown or memory pressure situations.
|
|
136
393
|
*/
|
|
137
|
-
|
|
394
|
+
clear(): void;
|
|
138
395
|
/**
|
|
139
|
-
*
|
|
140
|
-
*
|
|
396
|
+
* Manually trigger shrink operation.
|
|
397
|
+
* Removes excess idle buffers to reduce memory footprint.
|
|
141
398
|
*/
|
|
142
|
-
|
|
399
|
+
shrink(): void;
|
|
143
400
|
/**
|
|
144
|
-
*
|
|
145
|
-
*
|
|
401
|
+
* Pre-warm pool by creating buffers up to specified count.
|
|
402
|
+
* Called automatically in constructor.
|
|
403
|
+
*
|
|
404
|
+
* @param count - Number of buffers to create
|
|
146
405
|
*/
|
|
147
|
-
|
|
148
|
-
}
|
|
149
|
-
interface ClusterTLSConfig extends TLSConfig {
|
|
406
|
+
prewarm(count?: number): void;
|
|
150
407
|
/**
|
|
151
|
-
*
|
|
152
|
-
*
|
|
408
|
+
* Reset all metrics to zero.
|
|
409
|
+
* Useful for testing or periodic metric collection.
|
|
153
410
|
*/
|
|
154
|
-
|
|
411
|
+
resetStats(): void;
|
|
155
412
|
/**
|
|
156
|
-
*
|
|
157
|
-
* Can be disabled in development for self-signed certs
|
|
158
|
-
* @default true
|
|
413
|
+
* Get configuration values.
|
|
159
414
|
*/
|
|
160
|
-
|
|
415
|
+
getConfig(): Readonly<Required<BufferPoolConfig>>;
|
|
416
|
+
private createBuffer;
|
|
417
|
+
private shouldShrink;
|
|
161
418
|
}
|
|
419
|
+
/**
|
|
420
|
+
* Get or create the global buffer pool.
|
|
421
|
+
* Uses default configuration (64KB chunks, 256 max).
|
|
422
|
+
*/
|
|
423
|
+
declare function getGlobalBufferPool(): BufferPool;
|
|
424
|
+
/**
|
|
425
|
+
* Replace the global buffer pool with a custom instance.
|
|
426
|
+
* Useful for testing or custom configurations.
|
|
427
|
+
*
|
|
428
|
+
* @param pool - New pool instance, or null to reset to default
|
|
429
|
+
*/
|
|
430
|
+
declare function setGlobalBufferPool(pool: BufferPool | null): void;
|
|
162
431
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
securityPolicies?: PermissionPolicy[];
|
|
172
|
-
/** Callback to resolve dynamic peer addresses after ports are known */
|
|
173
|
-
resolvePeers?: () => string[];
|
|
174
|
-
interceptors?: IInterceptor[];
|
|
175
|
-
metricsPort?: number;
|
|
176
|
-
discovery?: 'manual' | 'kubernetes';
|
|
177
|
-
serviceName?: string;
|
|
178
|
-
discoveryInterval?: number;
|
|
179
|
-
tls?: TLSConfig;
|
|
180
|
-
clusterTls?: ClusterTLSConfig;
|
|
181
|
-
}
|
|
182
|
-
declare class ServerCoordinator {
|
|
183
|
-
private httpServer;
|
|
184
|
-
private metricsServer?;
|
|
185
|
-
private metricsService;
|
|
186
|
-
private wss;
|
|
187
|
-
private clients;
|
|
188
|
-
private interceptors;
|
|
189
|
-
private maps;
|
|
190
|
-
private hlc;
|
|
191
|
-
private storage?;
|
|
192
|
-
private jwtSecret;
|
|
193
|
-
private queryRegistry;
|
|
194
|
-
private cluster;
|
|
195
|
-
private partitionService;
|
|
196
|
-
private lockManager;
|
|
197
|
-
private topicManager;
|
|
198
|
-
private securityManager;
|
|
199
|
-
private systemManager;
|
|
200
|
-
private pendingClusterQueries;
|
|
201
|
-
private gcInterval?;
|
|
202
|
-
private heartbeatCheckInterval?;
|
|
203
|
-
private gcReports;
|
|
204
|
-
private mapLoadingPromises;
|
|
205
|
-
private _actualPort;
|
|
206
|
-
private _actualClusterPort;
|
|
207
|
-
private _readyPromise;
|
|
208
|
-
private _readyResolve;
|
|
209
|
-
constructor(config: ServerCoordinatorConfig);
|
|
210
|
-
/** Wait for server to be fully ready (ports assigned) */
|
|
211
|
-
ready(): Promise<void>;
|
|
212
|
-
/** Get the actual port the server is listening on */
|
|
213
|
-
get port(): number;
|
|
214
|
-
/** Get the actual cluster port */
|
|
215
|
-
get clusterPort(): number;
|
|
216
|
-
shutdown(): Promise<void>;
|
|
217
|
-
private handleConnection;
|
|
218
|
-
private handleMessage;
|
|
219
|
-
private updateClientHlc;
|
|
220
|
-
private broadcast;
|
|
221
|
-
private setupClusterListeners;
|
|
222
|
-
private executeLocalQuery;
|
|
223
|
-
private finalizeClusterQuery;
|
|
224
|
-
private handleLockGranted;
|
|
225
|
-
private processLocalOp;
|
|
226
|
-
private handleClusterEvent;
|
|
227
|
-
getMap(name: string, typeHint?: 'LWW' | 'OR'): LWWMap<string, any> | ORMap<string, any>;
|
|
432
|
+
/**
|
|
433
|
+
* ObjectPool - Generic object pooling for reducing GC pressure.
|
|
434
|
+
* Phase 2.04: Object Pool Implementation
|
|
435
|
+
*
|
|
436
|
+
* Reuses objects instead of creating new ones in hot paths.
|
|
437
|
+
* Works with any plain data structure type.
|
|
438
|
+
*/
|
|
439
|
+
interface ObjectPoolConfig<T> {
|
|
228
440
|
/**
|
|
229
|
-
*
|
|
230
|
-
*
|
|
441
|
+
* Factory function to create new objects.
|
|
442
|
+
* Called when pool is empty.
|
|
231
443
|
*/
|
|
232
|
-
|
|
233
|
-
private loadMapFromStorage;
|
|
234
|
-
private startGarbageCollection;
|
|
444
|
+
factory: () => T;
|
|
235
445
|
/**
|
|
236
|
-
*
|
|
446
|
+
* Reset function to clean object before reuse.
|
|
447
|
+
* Should clear all fields to initial state.
|
|
237
448
|
*/
|
|
238
|
-
|
|
449
|
+
reset: (obj: T) => void;
|
|
239
450
|
/**
|
|
240
|
-
*
|
|
241
|
-
*
|
|
451
|
+
* Optional validator to check if object is reusable.
|
|
452
|
+
* Return false to discard corrupted objects.
|
|
242
453
|
*/
|
|
243
|
-
|
|
454
|
+
validate?: (obj: T) => boolean;
|
|
244
455
|
/**
|
|
245
|
-
*
|
|
456
|
+
* Initial pool size.
|
|
457
|
+
* Default: 32
|
|
246
458
|
*/
|
|
247
|
-
|
|
459
|
+
initialSize?: number;
|
|
248
460
|
/**
|
|
249
|
-
*
|
|
461
|
+
* Maximum pool size.
|
|
462
|
+
* Excess objects are discarded.
|
|
463
|
+
* Default: 512
|
|
250
464
|
*/
|
|
251
|
-
|
|
465
|
+
maxSize?: number;
|
|
252
466
|
/**
|
|
253
|
-
*
|
|
467
|
+
* Name for debugging/metrics.
|
|
254
468
|
*/
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
469
|
+
name?: string;
|
|
470
|
+
}
|
|
471
|
+
interface ObjectPoolStats {
|
|
472
|
+
/** Pool name */
|
|
473
|
+
name: string;
|
|
474
|
+
/** Objects currently available */
|
|
475
|
+
available: number;
|
|
476
|
+
/** Objects currently in use */
|
|
477
|
+
inUse: number;
|
|
478
|
+
/** Total objects created */
|
|
479
|
+
created: number;
|
|
480
|
+
/** Total times an object was reused */
|
|
481
|
+
reused: number;
|
|
482
|
+
/** Reuse ratio */
|
|
483
|
+
reuseRatio: number;
|
|
484
|
+
/** Objects discarded (failed validation) */
|
|
485
|
+
discarded: number;
|
|
486
|
+
/** Max pool size */
|
|
487
|
+
maxSize: number;
|
|
488
|
+
/** Peak concurrent usage */
|
|
489
|
+
peakUsage: number;
|
|
490
|
+
}
|
|
491
|
+
/**
|
|
492
|
+
* Generic object pool for reusing plain data structures.
|
|
493
|
+
*
|
|
494
|
+
* Usage:
|
|
495
|
+
* ```typescript
|
|
496
|
+
* const pool = new ObjectPool({
|
|
497
|
+
* factory: () => ({ name: '', value: 0 }),
|
|
498
|
+
* reset: (obj) => { obj.name = ''; obj.value = 0; },
|
|
499
|
+
* });
|
|
500
|
+
*
|
|
501
|
+
* const obj = pool.acquire();
|
|
502
|
+
* obj.name = 'test';
|
|
503
|
+
* obj.value = 42;
|
|
504
|
+
* // ... use obj ...
|
|
505
|
+
* pool.release(obj);
|
|
506
|
+
* ```
|
|
507
|
+
*
|
|
508
|
+
* Important:
|
|
509
|
+
* - Only use for plain data objects
|
|
510
|
+
* - Don't pool objects with closures or event listeners
|
|
511
|
+
* - Don't keep references after release
|
|
512
|
+
*/
|
|
513
|
+
declare class ObjectPool<T> {
|
|
514
|
+
private readonly pool;
|
|
515
|
+
private readonly factory;
|
|
516
|
+
private readonly resetFn;
|
|
517
|
+
private readonly validate?;
|
|
518
|
+
private readonly maxSize;
|
|
519
|
+
private readonly name;
|
|
520
|
+
private inUseCount;
|
|
521
|
+
private createdCount;
|
|
522
|
+
private reusedCount;
|
|
523
|
+
private discardedCount;
|
|
524
|
+
private peakUsage;
|
|
525
|
+
private readonly releasedObjects;
|
|
526
|
+
constructor(config: ObjectPoolConfig<T>);
|
|
527
|
+
/**
|
|
528
|
+
* Acquire an object from the pool.
|
|
529
|
+
* Creates new object via factory if pool is empty.
|
|
530
|
+
*
|
|
531
|
+
* @returns A clean, ready-to-use object
|
|
532
|
+
*/
|
|
533
|
+
acquire(): T;
|
|
534
|
+
/**
|
|
535
|
+
* Release an object back to the pool.
|
|
536
|
+
* Object is reset before returning to pool.
|
|
537
|
+
*
|
|
538
|
+
* @param obj - The object to release
|
|
539
|
+
*/
|
|
540
|
+
release(obj: T): void;
|
|
541
|
+
/**
|
|
542
|
+
* Acquire multiple objects at once.
|
|
543
|
+
* More efficient than multiple acquire() calls.
|
|
544
|
+
*
|
|
545
|
+
* @param count - Number of objects to acquire
|
|
546
|
+
* @returns Array of objects
|
|
547
|
+
*/
|
|
548
|
+
acquireBatch(count: number): T[];
|
|
549
|
+
/**
|
|
550
|
+
* Release multiple objects at once.
|
|
551
|
+
*
|
|
552
|
+
* @param objects - Objects to release
|
|
553
|
+
*/
|
|
554
|
+
releaseBatch(objects: T[]): void;
|
|
555
|
+
/**
|
|
556
|
+
* Get pool statistics.
|
|
557
|
+
*/
|
|
558
|
+
getStats(): ObjectPoolStats;
|
|
559
|
+
/**
|
|
560
|
+
* Clear all objects from pool.
|
|
561
|
+
*/
|
|
562
|
+
clear(): void;
|
|
563
|
+
/**
|
|
564
|
+
* Pre-warm pool with objects.
|
|
565
|
+
*
|
|
566
|
+
* @param count - Number of objects to create
|
|
567
|
+
*/
|
|
568
|
+
prewarm(count?: number): void;
|
|
569
|
+
/**
|
|
570
|
+
* Reset statistics.
|
|
571
|
+
*/
|
|
572
|
+
resetStats(): void;
|
|
573
|
+
private createObject;
|
|
260
574
|
}
|
|
261
575
|
|
|
262
|
-
|
|
263
|
-
|
|
576
|
+
/**
|
|
577
|
+
* MessagePool - Pre-configured ObjectPool for message objects.
|
|
578
|
+
* Phase 2.04: Object Pool Implementation
|
|
579
|
+
*
|
|
580
|
+
* Reduces GC pressure when processing incoming messages.
|
|
581
|
+
*/
|
|
582
|
+
|
|
583
|
+
/**
|
|
584
|
+
* Pooled message structure matching common message format.
|
|
585
|
+
*/
|
|
586
|
+
interface PooledMessage {
|
|
587
|
+
type: string;
|
|
588
|
+
payload: unknown;
|
|
589
|
+
timestamp: number | null;
|
|
590
|
+
clientId: string | null;
|
|
591
|
+
mapName: string | null;
|
|
592
|
+
key: string | null;
|
|
264
593
|
}
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
594
|
+
/**
|
|
595
|
+
* Create a new MessagePool instance.
|
|
596
|
+
*
|
|
597
|
+
* @param config - Pool configuration
|
|
598
|
+
* @returns Configured ObjectPool for messages
|
|
599
|
+
*/
|
|
600
|
+
declare function createMessagePool(config?: {
|
|
601
|
+
maxSize?: number;
|
|
602
|
+
initialSize?: number;
|
|
603
|
+
}): ObjectPool<PooledMessage>;
|
|
604
|
+
/**
|
|
605
|
+
* Get or create the global message pool.
|
|
606
|
+
*/
|
|
607
|
+
declare function getGlobalMessagePool(): ObjectPool<PooledMessage>;
|
|
608
|
+
/**
|
|
609
|
+
* Replace the global message pool (for testing).
|
|
610
|
+
*/
|
|
611
|
+
declare function setGlobalMessagePool(pool: ObjectPool<PooledMessage> | null): void;
|
|
612
|
+
|
|
613
|
+
/**
|
|
614
|
+
* TimestampPool - Pre-configured ObjectPool for HLC timestamp objects.
|
|
615
|
+
* Phase 2.04: Object Pool Implementation
|
|
616
|
+
*
|
|
617
|
+
* Reduces GC pressure for frequent timestamp operations.
|
|
618
|
+
* Timestamps are created on every operation (set, merge, etc.).
|
|
619
|
+
*/
|
|
620
|
+
|
|
621
|
+
/**
|
|
622
|
+
* Pooled timestamp structure matching HLC format.
|
|
623
|
+
*/
|
|
624
|
+
interface PooledTimestamp {
|
|
625
|
+
millis: number;
|
|
626
|
+
counter: number;
|
|
627
|
+
nodeId: string;
|
|
280
628
|
}
|
|
629
|
+
/**
|
|
630
|
+
* Create a new TimestampPool instance.
|
|
631
|
+
*
|
|
632
|
+
* @param config - Pool configuration
|
|
633
|
+
* @returns Configured ObjectPool for timestamps
|
|
634
|
+
*/
|
|
635
|
+
declare function createTimestampPool(config?: {
|
|
636
|
+
maxSize?: number;
|
|
637
|
+
initialSize?: number;
|
|
638
|
+
}): ObjectPool<PooledTimestamp>;
|
|
639
|
+
/**
|
|
640
|
+
* Get or create the global timestamp pool.
|
|
641
|
+
*/
|
|
642
|
+
declare function getGlobalTimestampPool(): ObjectPool<PooledTimestamp>;
|
|
643
|
+
/**
|
|
644
|
+
* Replace the global timestamp pool (for testing).
|
|
645
|
+
*/
|
|
646
|
+
declare function setGlobalTimestampPool(pool: ObjectPool<PooledTimestamp> | null): void;
|
|
281
647
|
|
|
282
648
|
/**
|
|
283
|
-
*
|
|
284
|
-
*
|
|
649
|
+
* RecordPool - Pre-configured ObjectPool for LWW/OR record objects.
|
|
650
|
+
* Phase 2.04: Object Pool Implementation
|
|
285
651
|
*
|
|
286
|
-
*
|
|
652
|
+
* Reduces GC pressure when processing CRDT operations.
|
|
287
653
|
*/
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
654
|
+
|
|
655
|
+
/**
|
|
656
|
+
* Pooled record structure matching LWWRecord format.
|
|
657
|
+
*/
|
|
658
|
+
interface PooledRecord<T = unknown> {
|
|
659
|
+
value: T | null;
|
|
660
|
+
timestamp: PooledTimestamp | null;
|
|
661
|
+
ttlMs?: number;
|
|
662
|
+
}
|
|
663
|
+
/**
|
|
664
|
+
* Pooled event payload structure.
|
|
665
|
+
*/
|
|
666
|
+
interface PooledEventPayload {
|
|
667
|
+
mapName: string;
|
|
668
|
+
key: string;
|
|
669
|
+
eventType: string;
|
|
670
|
+
record: PooledRecord | null;
|
|
671
|
+
orRecord: PooledRecord | null;
|
|
672
|
+
orTag: string | null;
|
|
300
673
|
}
|
|
674
|
+
/**
|
|
675
|
+
* Create a new RecordPool instance.
|
|
676
|
+
*
|
|
677
|
+
* @param config - Pool configuration
|
|
678
|
+
* @returns Configured ObjectPool for records
|
|
679
|
+
*/
|
|
680
|
+
declare function createRecordPool<T = unknown>(config?: {
|
|
681
|
+
maxSize?: number;
|
|
682
|
+
initialSize?: number;
|
|
683
|
+
}): ObjectPool<PooledRecord<T>>;
|
|
684
|
+
/**
|
|
685
|
+
* Create a new EventPayloadPool instance.
|
|
686
|
+
*
|
|
687
|
+
* @param config - Pool configuration
|
|
688
|
+
* @returns Configured ObjectPool for event payloads
|
|
689
|
+
*/
|
|
690
|
+
declare function createEventPayloadPool(config?: {
|
|
691
|
+
maxSize?: number;
|
|
692
|
+
initialSize?: number;
|
|
693
|
+
}): ObjectPool<PooledEventPayload>;
|
|
694
|
+
/**
|
|
695
|
+
* Get or create the global record pool.
|
|
696
|
+
*/
|
|
697
|
+
declare function getGlobalRecordPool(): ObjectPool<PooledRecord>;
|
|
698
|
+
/**
|
|
699
|
+
* Replace the global record pool (for testing).
|
|
700
|
+
*/
|
|
701
|
+
declare function setGlobalRecordPool(pool: ObjectPool<PooledRecord> | null): void;
|
|
702
|
+
/**
|
|
703
|
+
* Get or create the global event payload pool.
|
|
704
|
+
*/
|
|
705
|
+
declare function getGlobalEventPayloadPool(): ObjectPool<PooledEventPayload>;
|
|
706
|
+
/**
|
|
707
|
+
* Replace the global event payload pool (for testing).
|
|
708
|
+
*/
|
|
709
|
+
declare function setGlobalEventPayloadPool(pool: ObjectPool<PooledEventPayload> | null): void;
|
|
301
710
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
711
|
+
/**
|
|
712
|
+
* WorkerPool Types
|
|
713
|
+
* Phase 1.02: Worker Threads Implementation
|
|
714
|
+
*/
|
|
715
|
+
/**
|
|
716
|
+
* Configuration for WorkerPool
|
|
717
|
+
*/
|
|
718
|
+
interface WorkerPoolConfig {
|
|
719
|
+
/** Minimum number of workers (default: 2) */
|
|
720
|
+
minWorkers?: number;
|
|
721
|
+
/** Maximum number of workers (default: os.cpus().length - 1) */
|
|
722
|
+
maxWorkers?: number;
|
|
723
|
+
/** Task execution timeout in ms (default: 5000) */
|
|
724
|
+
taskTimeout?: number;
|
|
725
|
+
/** Worker idle timeout before termination in ms (default: 30000) */
|
|
726
|
+
idleTimeout?: number;
|
|
727
|
+
/** Enable worker restart on crash (default: true) */
|
|
728
|
+
autoRestart?: boolean;
|
|
729
|
+
/** Custom worker script path (for testing or custom workers) */
|
|
730
|
+
workerScript?: string;
|
|
731
|
+
}
|
|
732
|
+
/**
|
|
733
|
+
* Task types supported by workers
|
|
734
|
+
*/
|
|
735
|
+
type WorkerTaskType = 'merkle-hash' | 'merkle-hash-ormap' | 'merkle-diff' | 'merkle-rebuild' | 'merkle-rebuild-ormap' | 'lww-merge' | 'ormap-merge' | 'serialize' | 'deserialize';
|
|
736
|
+
/**
|
|
737
|
+
* Task priority levels
|
|
738
|
+
*/
|
|
739
|
+
type TaskPriority = 'high' | 'normal' | 'low';
|
|
740
|
+
/**
|
|
741
|
+
* Task to be executed by a worker
|
|
742
|
+
*/
|
|
743
|
+
interface WorkerTask<TPayload = unknown, TResult = unknown> {
|
|
744
|
+
/** Unique task ID */
|
|
745
|
+
id: string;
|
|
746
|
+
/** Task type for routing to correct handler */
|
|
747
|
+
type: WorkerTaskType;
|
|
748
|
+
/** Task payload (must be serializable) */
|
|
749
|
+
payload: TPayload;
|
|
750
|
+
/** Priority (default: 'normal') */
|
|
751
|
+
priority?: TaskPriority;
|
|
752
|
+
/** Internal: Expected result type marker */
|
|
753
|
+
_resultType?: TResult;
|
|
754
|
+
}
|
|
755
|
+
/**
|
|
756
|
+
* Pool statistics
|
|
757
|
+
*/
|
|
758
|
+
interface WorkerPoolStats {
|
|
759
|
+
/** Number of workers currently executing tasks */
|
|
760
|
+
activeWorkers: number;
|
|
761
|
+
/** Number of workers waiting for tasks */
|
|
762
|
+
idleWorkers: number;
|
|
763
|
+
/** Number of tasks in queue */
|
|
764
|
+
pendingTasks: number;
|
|
765
|
+
/** Total tasks completed successfully */
|
|
766
|
+
completedTasks: number;
|
|
767
|
+
/** Total tasks that failed */
|
|
768
|
+
failedTasks: number;
|
|
769
|
+
/** Average task execution time in ms */
|
|
770
|
+
avgTaskDuration: number;
|
|
310
771
|
}
|
|
311
772
|
|
|
312
|
-
|
|
313
|
-
|
|
773
|
+
/**
|
|
774
|
+
* WorkerPool Implementation
|
|
775
|
+
* Phase 1.02: Worker Threads Implementation
|
|
776
|
+
*
|
|
777
|
+
* Manages a pool of worker threads for CPU-bound operations.
|
|
778
|
+
* Features:
|
|
779
|
+
* - Auto-scaling (minWorkers to maxWorkers)
|
|
780
|
+
* - Priority queue (high > normal > low)
|
|
781
|
+
* - Task timeouts
|
|
782
|
+
* - Worker crash recovery
|
|
783
|
+
* - Graceful shutdown
|
|
784
|
+
*/
|
|
314
785
|
|
|
315
|
-
declare class
|
|
316
|
-
|
|
317
|
-
|
|
786
|
+
declare class WorkerPool {
|
|
787
|
+
private readonly config;
|
|
788
|
+
private readonly workers;
|
|
789
|
+
private readonly taskQueue;
|
|
790
|
+
private readonly pendingTasks;
|
|
791
|
+
private workerIdCounter;
|
|
792
|
+
private isShuttingDown;
|
|
793
|
+
private isShutdown;
|
|
794
|
+
private completedTaskCount;
|
|
795
|
+
private failedTaskCount;
|
|
796
|
+
private totalTaskDuration;
|
|
797
|
+
private idleCheckInterval?;
|
|
798
|
+
constructor(config?: WorkerPoolConfig);
|
|
799
|
+
/**
|
|
800
|
+
* Resolve the worker script path based on environment
|
|
801
|
+
*/
|
|
802
|
+
private resolveWorkerScript;
|
|
803
|
+
/**
|
|
804
|
+
* Submit a task to the pool
|
|
805
|
+
*/
|
|
806
|
+
submit<TPayload, TResult>(task: WorkerTask<TPayload, TResult>): Promise<TResult>;
|
|
807
|
+
/**
|
|
808
|
+
* Get current pool statistics
|
|
809
|
+
*/
|
|
810
|
+
getStats(): WorkerPoolStats;
|
|
811
|
+
/**
|
|
812
|
+
* Gracefully shutdown all workers
|
|
813
|
+
*/
|
|
814
|
+
shutdown(timeout?: number): Promise<void>;
|
|
815
|
+
/**
|
|
816
|
+
* Check if pool is accepting tasks
|
|
817
|
+
*/
|
|
818
|
+
isRunning(): boolean;
|
|
819
|
+
private initializeWorkers;
|
|
820
|
+
private createWorker;
|
|
821
|
+
private findIdleWorker;
|
|
822
|
+
private assignTaskToWorker;
|
|
823
|
+
private enqueueTask;
|
|
824
|
+
private tryScaleUp;
|
|
825
|
+
private handleWorkerResponse;
|
|
826
|
+
private handleTaskTimeout;
|
|
827
|
+
private handleWorkerError;
|
|
828
|
+
private handleWorkerExit;
|
|
829
|
+
private startIdleCheck;
|
|
830
|
+
private checkIdleWorkers;
|
|
318
831
|
}
|
|
319
832
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
833
|
+
/**
|
|
834
|
+
* Merkle Worker Types
|
|
835
|
+
* Phase 1.03: MerkleWorker Implementation
|
|
836
|
+
*/
|
|
837
|
+
|
|
838
|
+
/**
|
|
839
|
+
* Entry for Merkle hash computation (LWWMap style)
|
|
840
|
+
*/
|
|
841
|
+
interface MerkleHashEntry {
|
|
842
|
+
key: string;
|
|
843
|
+
timestamp: Timestamp;
|
|
323
844
|
}
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
845
|
+
/**
|
|
846
|
+
* Entry for ORMap Merkle hash computation
|
|
847
|
+
*/
|
|
848
|
+
interface ORMapMerkleHashEntry {
|
|
849
|
+
key: string;
|
|
850
|
+
records: Array<{
|
|
851
|
+
tag: string;
|
|
852
|
+
timestamp: Timestamp;
|
|
853
|
+
}>;
|
|
854
|
+
}
|
|
855
|
+
/**
|
|
856
|
+
* Payload for merkle-hash task (LWWMap)
|
|
857
|
+
*/
|
|
858
|
+
interface MerkleHashPayload {
|
|
859
|
+
entries: MerkleHashEntry[];
|
|
860
|
+
depth?: number;
|
|
861
|
+
}
|
|
862
|
+
/**
|
|
863
|
+
* Result of merkle-hash task
|
|
864
|
+
*/
|
|
865
|
+
interface MerkleHashResult {
|
|
866
|
+
/** Map of key -> hash for each entry */
|
|
867
|
+
hashes: Array<[string, number]>;
|
|
868
|
+
/** Root hash of all entries */
|
|
869
|
+
rootHash: number;
|
|
870
|
+
/** Bucket structure: path -> { hash, keys } */
|
|
871
|
+
buckets: Array<[string, {
|
|
872
|
+
hash: number;
|
|
873
|
+
keys: string[];
|
|
874
|
+
}]>;
|
|
875
|
+
}
|
|
876
|
+
/**
|
|
877
|
+
* Payload for merkle-hash-ormap task
|
|
878
|
+
*/
|
|
879
|
+
interface ORMapMerkleHashPayload {
|
|
880
|
+
entries: ORMapMerkleHashEntry[];
|
|
881
|
+
depth?: number;
|
|
882
|
+
}
|
|
883
|
+
/**
|
|
884
|
+
* Result of merkle-hash-ormap task
|
|
885
|
+
*/
|
|
886
|
+
interface ORMapMerkleHashResult {
|
|
887
|
+
/** Map of key -> hash for each entry */
|
|
888
|
+
hashes: Array<[string, number]>;
|
|
889
|
+
/** Root hash of all entries */
|
|
890
|
+
rootHash: number;
|
|
891
|
+
/** Bucket structure: path -> { hash, keys } */
|
|
892
|
+
buckets: Array<[string, {
|
|
893
|
+
hash: number;
|
|
894
|
+
keys: string[];
|
|
895
|
+
}]>;
|
|
896
|
+
}
|
|
897
|
+
/**
|
|
898
|
+
* Bucket info for diff comparison
|
|
899
|
+
*/
|
|
900
|
+
interface BucketInfo {
|
|
901
|
+
hash: number;
|
|
902
|
+
keys: string[];
|
|
903
|
+
}
|
|
904
|
+
/**
|
|
905
|
+
* Payload for merkle-diff task
|
|
906
|
+
*/
|
|
907
|
+
interface MerkleDiffPayload {
|
|
908
|
+
/** Local buckets: path -> bucket info */
|
|
909
|
+
localBuckets: Array<[string, BucketInfo]>;
|
|
910
|
+
/** Remote buckets: path -> bucket info */
|
|
911
|
+
remoteBuckets: Array<[string, BucketInfo]>;
|
|
912
|
+
}
|
|
913
|
+
/**
|
|
914
|
+
* Result of merkle-diff task
|
|
915
|
+
*/
|
|
916
|
+
interface MerkleDiffResult {
|
|
917
|
+
/** Keys that exist on remote but not locally */
|
|
918
|
+
missingLocal: string[];
|
|
919
|
+
/** Keys that exist locally but not on remote */
|
|
920
|
+
missingRemote: string[];
|
|
921
|
+
/** Paths where buckets differ (need deeper comparison) */
|
|
922
|
+
differingPaths: string[];
|
|
923
|
+
}
|
|
924
|
+
/**
|
|
925
|
+
* Payload for merkle-rebuild task (LWWMap)
|
|
926
|
+
*/
|
|
927
|
+
interface MerkleRebuildPayload {
|
|
928
|
+
records: Array<{
|
|
929
|
+
key: string;
|
|
930
|
+
timestamp: Timestamp;
|
|
931
|
+
}>;
|
|
932
|
+
depth?: number;
|
|
933
|
+
}
|
|
934
|
+
/**
|
|
935
|
+
* Payload for merkle-rebuild-ormap task
|
|
936
|
+
*/
|
|
937
|
+
interface ORMapMerkleRebuildPayload {
|
|
938
|
+
records: Array<{
|
|
939
|
+
key: string;
|
|
940
|
+
tags: Array<{
|
|
941
|
+
tag: string;
|
|
942
|
+
timestamp: Timestamp;
|
|
943
|
+
}>;
|
|
944
|
+
}>;
|
|
945
|
+
depth?: number;
|
|
946
|
+
}
|
|
947
|
+
/**
|
|
948
|
+
* Result of merkle-rebuild task
|
|
949
|
+
*/
|
|
950
|
+
interface MerkleRebuildResult {
|
|
951
|
+
/** Root hash of rebuilt tree */
|
|
952
|
+
rootHash: number;
|
|
953
|
+
/** All buckets in the tree */
|
|
954
|
+
buckets: Array<[string, {
|
|
955
|
+
hash: number;
|
|
956
|
+
keys: string[];
|
|
957
|
+
}]>;
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
/**
|
|
961
|
+
* MerkleWorker - High-level API for Merkle tree operations in worker threads
|
|
962
|
+
* Phase 1.03: MerkleWorker Implementation
|
|
963
|
+
*
|
|
964
|
+
* Provides a clean interface for CPU-intensive Merkle tree operations.
|
|
965
|
+
* Delegates actual work to worker threads via WorkerPool.
|
|
966
|
+
*/
|
|
967
|
+
|
|
968
|
+
/**
|
|
969
|
+
* MerkleWorker provides methods for Merkle tree operations.
|
|
970
|
+
* Automatically decides whether to use worker threads based on workload size.
|
|
971
|
+
*/
|
|
972
|
+
declare class MerkleWorker {
|
|
973
|
+
private readonly pool;
|
|
974
|
+
private readonly workerScript;
|
|
975
|
+
constructor(pool: WorkerPool);
|
|
976
|
+
private resolveMerkleWorkerScript;
|
|
977
|
+
/**
|
|
978
|
+
* Compute hashes for a batch of LWWMap entries.
|
|
979
|
+
* Uses worker thread if entries count exceeds threshold.
|
|
980
|
+
*/
|
|
981
|
+
computeHashes(payload: MerkleHashPayload): Promise<MerkleHashResult>;
|
|
982
|
+
/**
|
|
983
|
+
* Compute hashes for a batch of ORMap entries.
|
|
984
|
+
* Uses worker thread if entries count exceeds threshold.
|
|
985
|
+
*/
|
|
986
|
+
computeORMapHashes(payload: ORMapMerkleHashPayload): Promise<ORMapMerkleHashResult>;
|
|
987
|
+
/**
|
|
988
|
+
* Find differences between local and remote Merkle trees.
|
|
989
|
+
*/
|
|
990
|
+
diff(payload: MerkleDiffPayload): Promise<MerkleDiffResult>;
|
|
991
|
+
/**
|
|
992
|
+
* Rebuild Merkle tree from LWWMap records.
|
|
993
|
+
* Always uses worker thread as this is typically a heavy operation.
|
|
994
|
+
*/
|
|
995
|
+
rebuild(payload: MerkleRebuildPayload): Promise<MerkleRebuildResult>;
|
|
996
|
+
/**
|
|
997
|
+
* Rebuild Merkle tree from ORMap records.
|
|
998
|
+
*/
|
|
999
|
+
rebuildORMap(payload: ORMapMerkleRebuildPayload): Promise<MerkleRebuildResult>;
|
|
1000
|
+
private computeHashesInline;
|
|
1001
|
+
private computeORMapHashesInline;
|
|
1002
|
+
private diffInline;
|
|
1003
|
+
private rebuildInline;
|
|
1004
|
+
private rebuildORMapInline;
|
|
1005
|
+
private hashString;
|
|
1006
|
+
private buildTree;
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
/**
|
|
1010
|
+
* CRDT Worker Types
|
|
1011
|
+
* Phase 1.04: CRDTMergeWorker Implementation
|
|
1012
|
+
*/
|
|
1013
|
+
|
|
1014
|
+
/**
|
|
1015
|
+
* Single LWW record for merge
|
|
1016
|
+
*/
|
|
1017
|
+
interface LWWMergeRecord {
|
|
1018
|
+
key: string;
|
|
1019
|
+
value: unknown;
|
|
1020
|
+
timestamp: Timestamp;
|
|
1021
|
+
ttlMs?: number;
|
|
1022
|
+
}
|
|
1023
|
+
/**
|
|
1024
|
+
* Existing state for a key (for merge comparison)
|
|
1025
|
+
* Note: value is optional since we only need timestamp for comparison
|
|
1026
|
+
*/
|
|
1027
|
+
interface LWWExistingRecord {
|
|
1028
|
+
key: string;
|
|
1029
|
+
timestamp: Timestamp;
|
|
1030
|
+
value?: unknown;
|
|
1031
|
+
ttlMs?: number;
|
|
1032
|
+
}
|
|
1033
|
+
/**
|
|
1034
|
+
* Payload for lww-merge task
|
|
1035
|
+
*/
|
|
1036
|
+
interface LWWMergePayload {
|
|
1037
|
+
mapName: string;
|
|
1038
|
+
/** Records to merge */
|
|
1039
|
+
records: LWWMergeRecord[];
|
|
1040
|
+
/** Current state of affected keys (for comparison) */
|
|
1041
|
+
existingState: LWWExistingRecord[];
|
|
1042
|
+
}
|
|
1043
|
+
/**
|
|
1044
|
+
* Result of lww-merge task
|
|
1045
|
+
*/
|
|
1046
|
+
interface LWWMergeResult {
|
|
1047
|
+
/** Records that should be applied (newer than existing) */
|
|
1048
|
+
toApply: Array<{
|
|
1049
|
+
key: string;
|
|
1050
|
+
value: unknown;
|
|
1051
|
+
timestamp: Timestamp;
|
|
1052
|
+
ttlMs?: number;
|
|
1053
|
+
}>;
|
|
1054
|
+
/** Number of records skipped (older timestamp) */
|
|
1055
|
+
skipped: number;
|
|
1056
|
+
/** Keys with concurrent updates (same timestamp, different nodeId) */
|
|
1057
|
+
conflicts: string[];
|
|
1058
|
+
}
|
|
1059
|
+
/**
|
|
1060
|
+
* Single ORMap item for merge
|
|
1061
|
+
*/
|
|
1062
|
+
interface ORMapMergeItem {
|
|
1063
|
+
key: string;
|
|
1064
|
+
value: unknown;
|
|
1065
|
+
timestamp: Timestamp;
|
|
1066
|
+
tag: string;
|
|
1067
|
+
ttlMs?: number;
|
|
1068
|
+
}
|
|
1069
|
+
/**
|
|
1070
|
+
* Single ORMap tombstone for merge
|
|
1071
|
+
*/
|
|
1072
|
+
interface ORMapMergeTombstone {
|
|
1073
|
+
tag: string;
|
|
1074
|
+
timestamp: Timestamp;
|
|
1075
|
+
}
|
|
1076
|
+
/**
|
|
1077
|
+
* Payload for ormap-merge task
|
|
1078
|
+
*/
|
|
1079
|
+
interface ORMapMergePayload {
|
|
1080
|
+
mapName: string;
|
|
1081
|
+
/** Items to merge */
|
|
1082
|
+
items: ORMapMergeItem[];
|
|
1083
|
+
/** Tombstones to merge */
|
|
1084
|
+
tombstones: ORMapMergeTombstone[];
|
|
1085
|
+
/** Existing tags in the map (for tombstone check) */
|
|
1086
|
+
existingTags: string[];
|
|
1087
|
+
/** Existing tombstones (to avoid re-adding deleted items) */
|
|
1088
|
+
existingTombstones: string[];
|
|
1089
|
+
}
|
|
1090
|
+
/**
|
|
1091
|
+
* Result of ormap-merge task
|
|
1092
|
+
*/
|
|
1093
|
+
interface ORMapMergeResult {
|
|
1094
|
+
/** Items that should be applied */
|
|
1095
|
+
itemsToApply: Array<{
|
|
1096
|
+
key: string;
|
|
1097
|
+
value: unknown;
|
|
1098
|
+
timestamp: Timestamp;
|
|
1099
|
+
tag: string;
|
|
1100
|
+
ttlMs?: number;
|
|
1101
|
+
}>;
|
|
1102
|
+
/** Tombstones that should be applied */
|
|
1103
|
+
tombstonesToApply: string[];
|
|
1104
|
+
/** Tags that need to be removed due to new tombstones */
|
|
1105
|
+
tagsToRemove: string[];
|
|
1106
|
+
/** Number of items skipped */
|
|
1107
|
+
itemsSkipped: number;
|
|
1108
|
+
/** Number of tombstones skipped */
|
|
1109
|
+
tombstonesSkipped: number;
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
/**
|
|
1113
|
+
* CRDTMergeWorker - High-level API for CRDT merge operations in worker threads
|
|
1114
|
+
* Phase 1.04: CRDTMergeWorker Implementation
|
|
1115
|
+
*
|
|
1116
|
+
* Provides a clean interface for CPU-intensive CRDT merge operations.
|
|
1117
|
+
* Delegates actual work to worker threads via WorkerPool for large batches.
|
|
1118
|
+
*/
|
|
1119
|
+
|
|
1120
|
+
/**
|
|
1121
|
+
* CRDTMergeWorker provides methods for CRDT merge operations.
|
|
1122
|
+
* Automatically decides whether to use worker threads based on batch size.
|
|
1123
|
+
*/
|
|
1124
|
+
declare class CRDTMergeWorker {
|
|
1125
|
+
private readonly pool;
|
|
1126
|
+
/** Threshold for using worker (operations below this go to main thread) */
|
|
1127
|
+
static readonly BATCH_THRESHOLD = 10;
|
|
1128
|
+
constructor(pool: WorkerPool);
|
|
1129
|
+
/**
|
|
1130
|
+
* Decide if batch should go to worker
|
|
1131
|
+
*/
|
|
1132
|
+
shouldUseWorker(batchSize: number): boolean;
|
|
1133
|
+
/**
|
|
1134
|
+
* Merge LWW records
|
|
1135
|
+
* @param payload - Records to merge and existing state
|
|
1136
|
+
* @returns Which records should be applied
|
|
1137
|
+
*/
|
|
1138
|
+
mergeLWW(payload: LWWMergePayload): Promise<LWWMergeResult>;
|
|
1139
|
+
/**
|
|
1140
|
+
* Merge ORMap items and tombstones
|
|
1141
|
+
* @param payload - Items, tombstones, and existing state
|
|
1142
|
+
* @returns Which items/tombstones should be applied
|
|
1143
|
+
*/
|
|
1144
|
+
mergeORMap(payload: ORMapMergePayload): Promise<ORMapMergeResult>;
|
|
1145
|
+
private mergeLWWInline;
|
|
1146
|
+
private mergeORMapInline;
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
/**
|
|
1150
|
+
* SerializationWorker - High-level API for serialization operations in worker threads
|
|
1151
|
+
* Phase 1.07: SerializationWorker Implementation
|
|
1152
|
+
*
|
|
1153
|
+
* Provides a clean interface for CPU-intensive serialization/deserialization.
|
|
1154
|
+
* Delegates actual work to worker threads via WorkerPool for large payloads.
|
|
1155
|
+
*
|
|
1156
|
+
* Uses base64 encoding to transfer binary data through postMessage.
|
|
1157
|
+
* This adds ~33% overhead but is necessary since Uint8Array cannot be
|
|
1158
|
+
* directly transferred through structured clone algorithm for our use case.
|
|
1159
|
+
*/
|
|
1160
|
+
|
|
1161
|
+
/**
|
|
1162
|
+
* SerializationWorker provides methods for serialization operations.
|
|
1163
|
+
* Automatically decides whether to use worker threads based on payload size.
|
|
1164
|
+
*/
|
|
1165
|
+
declare class SerializationWorker {
|
|
1166
|
+
private readonly pool;
|
|
1167
|
+
private readonly workerScript;
|
|
1168
|
+
/** Threshold for using worker (items below this go to main thread) */
|
|
1169
|
+
static readonly BATCH_THRESHOLD = 10;
|
|
1170
|
+
/** Size threshold for using worker (bytes) */
|
|
1171
|
+
static readonly SIZE_THRESHOLD: number;
|
|
1172
|
+
constructor(pool: WorkerPool);
|
|
1173
|
+
private resolveWorkerScript;
|
|
1174
|
+
/**
|
|
1175
|
+
* Decide if batch should go to worker based on count or size
|
|
1176
|
+
*/
|
|
1177
|
+
shouldUseWorker(items: unknown[]): boolean;
|
|
1178
|
+
/**
|
|
1179
|
+
* Serialize multiple objects to MessagePack binary format.
|
|
1180
|
+
* Uses worker thread for large batches.
|
|
1181
|
+
*
|
|
1182
|
+
* @param items - Objects to serialize
|
|
1183
|
+
* @returns Array of Uint8Array containing serialized data
|
|
1184
|
+
*/
|
|
1185
|
+
serializeBatch(items: unknown[]): Promise<Uint8Array[]>;
|
|
1186
|
+
/**
|
|
1187
|
+
* Deserialize multiple MessagePack binary payloads to objects.
|
|
1188
|
+
* Uses worker thread for large batches.
|
|
1189
|
+
*
|
|
1190
|
+
* @param items - Binary data to deserialize
|
|
1191
|
+
* @returns Array of deserialized objects
|
|
1192
|
+
*/
|
|
1193
|
+
deserializeBatch<T = unknown>(items: Uint8Array[]): Promise<T[]>;
|
|
1194
|
+
/**
|
|
1195
|
+
* Serialize a single object (always inline, too small for worker)
|
|
1196
|
+
*/
|
|
1197
|
+
serialize(data: unknown): Uint8Array;
|
|
1198
|
+
/**
|
|
1199
|
+
* Deserialize a single payload (always inline, too small for worker)
|
|
1200
|
+
*/
|
|
1201
|
+
deserialize<T = unknown>(data: Uint8Array | ArrayBuffer): T;
|
|
1202
|
+
private serializeBatchInline;
|
|
1203
|
+
private deserializeBatchInline;
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
/**
|
|
1207
|
+
* SharedMemoryManager - Zero-copy data transfer between main thread and workers
|
|
1208
|
+
*
|
|
1209
|
+
* Uses SharedArrayBuffer with Atomics for synchronization.
|
|
1210
|
+
* Provides slot-based allocation for concurrent operations.
|
|
1211
|
+
*
|
|
1212
|
+
* Phase 3.04: SharedArrayBuffer Integration
|
|
1213
|
+
*/
|
|
1214
|
+
interface SharedMemoryConfig {
|
|
1215
|
+
/**
|
|
1216
|
+
* Size of shared buffer in bytes.
|
|
1217
|
+
* Default: 16MB
|
|
1218
|
+
*/
|
|
1219
|
+
bufferSize?: number;
|
|
1220
|
+
/**
|
|
1221
|
+
* Number of slots for concurrent operations.
|
|
1222
|
+
* Each slot can hold one transfer.
|
|
1223
|
+
* Default: 256
|
|
1224
|
+
*/
|
|
1225
|
+
slotCount?: number;
|
|
1226
|
+
/**
|
|
1227
|
+
* Reserved bytes per slot for metadata (length, status, etc).
|
|
1228
|
+
* Default: 16 (must be multiple of 8 for alignment)
|
|
1229
|
+
*/
|
|
1230
|
+
metadataSize?: number;
|
|
1231
|
+
}
|
|
1232
|
+
interface SharedMemoryStats {
|
|
1233
|
+
/** Total buffer size in bytes */
|
|
1234
|
+
totalSize: number;
|
|
1235
|
+
/** Number of slots */
|
|
1236
|
+
slotCount: number;
|
|
1237
|
+
/** Size of each slot in bytes */
|
|
1238
|
+
slotSize: number;
|
|
1239
|
+
/** Currently allocated slots */
|
|
1240
|
+
allocatedSlots: number;
|
|
1241
|
+
/** Available slots */
|
|
1242
|
+
availableSlots: number;
|
|
1243
|
+
/** Peak concurrent allocations */
|
|
1244
|
+
peakUsage: number;
|
|
1245
|
+
/** Total allocations since creation */
|
|
1246
|
+
totalAllocations: number;
|
|
1247
|
+
/** Total releases since creation */
|
|
1248
|
+
totalReleases: number;
|
|
1249
|
+
}
|
|
1250
|
+
interface SharedSlot {
|
|
1251
|
+
/** Slot index */
|
|
1252
|
+
index: number;
|
|
1253
|
+
/** View into shared buffer for this slot's data area */
|
|
1254
|
+
dataView: Uint8Array;
|
|
1255
|
+
/** Maximum data size (excluding metadata) */
|
|
1256
|
+
maxDataSize: number;
|
|
1257
|
+
}
|
|
1258
|
+
/**
|
|
1259
|
+
* Slot status values (stored in first 4 bytes of slot metadata)
|
|
1260
|
+
*/
|
|
1261
|
+
declare enum SlotStatus {
|
|
1262
|
+
FREE = 0,// Slot is available
|
|
1263
|
+
ALLOCATED = 1,// Slot is allocated, no data yet
|
|
1264
|
+
DATA_READY = 2,// Data written, ready for reading
|
|
1265
|
+
PROCESSING = 3,// Worker is processing
|
|
1266
|
+
RESULT_READY = 4,// Worker has written result
|
|
1267
|
+
ERROR = 255
|
|
1268
|
+
}
|
|
1269
|
+
/**
|
|
1270
|
+
* Manages shared memory for zero-copy data transfer between threads.
|
|
1271
|
+
*
|
|
1272
|
+
* Usage:
|
|
1273
|
+
* 1. Main thread allocates a slot
|
|
1274
|
+
* 2. Main thread writes data to slot
|
|
1275
|
+
* 3. Main thread sends slot index to worker via postMessage
|
|
1276
|
+
* 4. Worker reads data from shared memory (zero-copy)
|
|
1277
|
+
* 5. Worker writes result to slot
|
|
1278
|
+
* 6. Main thread reads result (zero-copy)
|
|
1279
|
+
* 7. Main thread releases slot
|
|
1280
|
+
*/
|
|
1281
|
+
declare class SharedMemoryManager {
|
|
1282
|
+
private readonly buffer;
|
|
1283
|
+
private readonly statusArray;
|
|
1284
|
+
private readonly slotSize;
|
|
1285
|
+
private readonly slotCount;
|
|
1286
|
+
private readonly metadataSize;
|
|
1287
|
+
private readonly freeSlots;
|
|
1288
|
+
private allocatedCount;
|
|
1289
|
+
private peakUsage;
|
|
1290
|
+
private totalAllocations;
|
|
1291
|
+
private totalReleases;
|
|
1292
|
+
constructor(config?: SharedMemoryConfig);
|
|
1293
|
+
/**
|
|
1294
|
+
* Get offset in Int32Array for slot status.
|
|
1295
|
+
* Status is at the beginning of each slot's metadata.
|
|
1296
|
+
*/
|
|
1297
|
+
private getStatusOffset;
|
|
1298
|
+
/**
|
|
1299
|
+
* Get byte offset for slot length field.
|
|
1300
|
+
*/
|
|
1301
|
+
private getLengthOffset;
|
|
1302
|
+
/**
|
|
1303
|
+
* Get byte offset for slot data (after metadata).
|
|
1304
|
+
*/
|
|
1305
|
+
private getDataOffset;
|
|
1306
|
+
/**
|
|
1307
|
+
* Allocate a slot for data transfer.
|
|
1308
|
+
* Returns null if no slots available.
|
|
1309
|
+
*/
|
|
1310
|
+
allocate(): SharedSlot | null;
|
|
1311
|
+
/**
|
|
1312
|
+
* Release a slot back to the pool.
|
|
1313
|
+
*/
|
|
1314
|
+
release(slot: SharedSlot): void;
|
|
1315
|
+
/**
|
|
1316
|
+
* Write data to a slot and signal it's ready.
|
|
1317
|
+
* Returns false if data is too large.
|
|
1318
|
+
*/
|
|
1319
|
+
writeData(slot: SharedSlot, data: Uint8Array): boolean;
|
|
1320
|
+
/**
|
|
1321
|
+
* Read data length from a slot.
|
|
1322
|
+
*/
|
|
1323
|
+
getDataLength(slotIndex: number): number;
|
|
1324
|
+
/**
|
|
1325
|
+
* Get data view for a slot (for reading).
|
|
1326
|
+
*/
|
|
1327
|
+
getDataView(slotIndex: number): Uint8Array;
|
|
1328
|
+
/**
|
|
1329
|
+
* Get slot status.
|
|
1330
|
+
*/
|
|
1331
|
+
getStatus(slotIndex: number): SlotStatus;
|
|
1332
|
+
/**
|
|
1333
|
+
* Wait for a specific status with timeout.
|
|
1334
|
+
* Returns the actual status (may differ if timeout occurred).
|
|
1335
|
+
*/
|
|
1336
|
+
waitForStatus(slotIndex: number, expectedStatus: SlotStatus, timeoutMs?: number): SlotStatus;
|
|
1337
|
+
/**
|
|
1338
|
+
* Wait for result and read it.
|
|
1339
|
+
* Returns null on timeout or error.
|
|
1340
|
+
*/
|
|
1341
|
+
waitForResult(slot: SharedSlot, timeoutMs?: number): Uint8Array | null;
|
|
1342
|
+
/**
|
|
1343
|
+
* Get the SharedArrayBuffer for passing to workers.
|
|
1344
|
+
*/
|
|
1345
|
+
getBuffer(): SharedArrayBuffer;
|
|
1346
|
+
/**
|
|
1347
|
+
* Get configuration needed by workers.
|
|
1348
|
+
*/
|
|
1349
|
+
getWorkerConfig(): SharedWorkerConfig;
|
|
1350
|
+
/**
|
|
1351
|
+
* Get statistics.
|
|
1352
|
+
*/
|
|
1353
|
+
getStats(): SharedMemoryStats;
|
|
1354
|
+
/**
|
|
1355
|
+
* Check if SharedArrayBuffer is available in current environment.
|
|
1356
|
+
*/
|
|
1357
|
+
static isAvailable(): boolean;
|
|
1358
|
+
/**
|
|
1359
|
+
* Shutdown and release resources.
|
|
1360
|
+
* Resets all slots to FREE status.
|
|
1361
|
+
*/
|
|
1362
|
+
shutdown(): void;
|
|
1363
|
+
}
|
|
1364
|
+
/**
|
|
1365
|
+
* Configuration passed to workers for shared memory access.
|
|
1366
|
+
*/
|
|
1367
|
+
interface SharedWorkerConfig {
|
|
1368
|
+
sharedBuffer: SharedArrayBuffer;
|
|
1369
|
+
slotSize: number;
|
|
1370
|
+
slotCount: number;
|
|
1371
|
+
metadataSize: number;
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1374
|
+
interface QueueMetrics {
|
|
1375
|
+
enqueued: number;
|
|
1376
|
+
dequeued: number;
|
|
1377
|
+
rejected: number;
|
|
1378
|
+
currentSize: number;
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1381
|
+
interface StripeMetrics {
|
|
1382
|
+
stripe: number;
|
|
1383
|
+
metrics: QueueMetrics;
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1386
|
+
type ORMapValue<V> = {
|
|
1387
|
+
type: 'OR';
|
|
1388
|
+
records: ORMapRecord<V>[];
|
|
1389
|
+
};
|
|
1390
|
+
type ORMapTombstones = {
|
|
1391
|
+
type: 'OR_TOMBSTONES';
|
|
1392
|
+
tags: string[];
|
|
1393
|
+
};
|
|
1394
|
+
type StorageValue<V> = LWWRecord<V> | ORMapValue<V> | ORMapTombstones;
|
|
1395
|
+
/**
|
|
1396
|
+
* Server Persistence Interface (MapStore).
|
|
1397
|
+
* Aligned with specifications/06_SERVER_INTEGRATIONS.md
|
|
1398
|
+
*
|
|
1399
|
+
* Note: We include mapName in all methods because a single storage adapter
|
|
1400
|
+
* instance typically handles multiple maps in the system (e.g. single DB connection).
|
|
1401
|
+
*/
|
|
1402
|
+
interface IServerStorage {
|
|
1403
|
+
/**
|
|
1404
|
+
* Initialize the storage connection.
|
|
1405
|
+
*/
|
|
1406
|
+
initialize(): Promise<void>;
|
|
1407
|
+
/**
|
|
1408
|
+
* Close the storage connection.
|
|
1409
|
+
*/
|
|
1410
|
+
close(): Promise<void>;
|
|
1411
|
+
/**
|
|
1412
|
+
* Loads the value of a given key.
|
|
1413
|
+
* Called when a client requests a key that is not in the Server RAM (if using partial loading),
|
|
1414
|
+
* or during sync.
|
|
1415
|
+
*/
|
|
1416
|
+
load(mapName: string, key: string): Promise<StorageValue<any> | undefined>;
|
|
1417
|
+
/**
|
|
1418
|
+
* Loads multiple keys.
|
|
1419
|
+
* Optimization for batch requests.
|
|
1420
|
+
*/
|
|
1421
|
+
loadAll(mapName: string, keys: string[]): Promise<Map<string, StorageValue<any>>>;
|
|
1422
|
+
/**
|
|
1423
|
+
* Loads all keys from the store for a specific map.
|
|
1424
|
+
* Used for pre-loading the cache on startup or understanding dataset size.
|
|
1425
|
+
*/
|
|
1426
|
+
loadAllKeys(mapName: string): Promise<string[]>;
|
|
1427
|
+
/**
|
|
1428
|
+
* Stores the key-value pair.
|
|
1429
|
+
*/
|
|
1430
|
+
store(mapName: string, key: string, record: StorageValue<any>): Promise<void>;
|
|
1431
|
+
/**
|
|
1432
|
+
* Stores multiple entries.
|
|
1433
|
+
* Used for efficient batch writes to the DB.
|
|
1434
|
+
*/
|
|
1435
|
+
storeAll(mapName: string, records: Map<string, StorageValue<any>>): Promise<void>;
|
|
1436
|
+
/**
|
|
1437
|
+
* Deletes the entry with the given key.
|
|
1438
|
+
*/
|
|
1439
|
+
delete(mapName: string, key: string): Promise<void>;
|
|
1440
|
+
/**
|
|
1441
|
+
* Deletes multiple entries.
|
|
1442
|
+
*/
|
|
1443
|
+
deleteAll(mapName: string, keys: string[]): Promise<void>;
|
|
1444
|
+
}
|
|
1445
|
+
|
|
1446
|
+
interface ServerOp {
|
|
1447
|
+
mapName: string;
|
|
1448
|
+
key: string;
|
|
1449
|
+
opType: 'PUT' | 'REMOVE' | 'OR_ADD' | 'OR_REMOVE';
|
|
1450
|
+
record?: LWWRecord<any>;
|
|
1451
|
+
orRecord?: ORMapRecord<any>;
|
|
1452
|
+
orTag?: string;
|
|
1453
|
+
id?: string;
|
|
1454
|
+
}
|
|
1455
|
+
interface ConnectionContext {
|
|
1456
|
+
clientId: string;
|
|
1457
|
+
socket?: WebSocket;
|
|
1458
|
+
principal?: Principal;
|
|
1459
|
+
isAuthenticated: boolean;
|
|
1460
|
+
}
|
|
1461
|
+
interface OpContext extends ConnectionContext {
|
|
1462
|
+
fromCluster: boolean;
|
|
1463
|
+
originalSenderId?: string;
|
|
1464
|
+
}
|
|
1465
|
+
interface IInterceptor {
|
|
1466
|
+
/**
|
|
1467
|
+
* Name of the interceptor for logging and debugging.
|
|
1468
|
+
*/
|
|
1469
|
+
name: string;
|
|
1470
|
+
/**
|
|
1471
|
+
* Called when a new client connects.
|
|
1472
|
+
* Throwing an error here will reject the connection.
|
|
1473
|
+
*/
|
|
1474
|
+
onConnection?(context: ConnectionContext): Promise<void>;
|
|
1475
|
+
/**
|
|
1476
|
+
* Called when a client disconnects.
|
|
1477
|
+
*/
|
|
1478
|
+
onDisconnect?(context: ConnectionContext): Promise<void>;
|
|
1479
|
+
/**
|
|
1480
|
+
* Called before an operation is applied to the local map/storage.
|
|
1481
|
+
* Return the (possibly modified) op.
|
|
1482
|
+
* Return null to silently drop the operation (no error sent to client, no persistence).
|
|
1483
|
+
* Throwing an error will reject the operation and notify the client.
|
|
1484
|
+
*/
|
|
1485
|
+
onBeforeOp?(op: ServerOp, context: OpContext): Promise<ServerOp | null>;
|
|
1486
|
+
/**
|
|
1487
|
+
* Called after an operation has been successfully applied and stored.
|
|
1488
|
+
*/
|
|
1489
|
+
onAfterOp?(op: ServerOp, context: OpContext): Promise<void>;
|
|
1490
|
+
}
|
|
1491
|
+
|
|
1492
|
+
interface TLSConfig {
|
|
1493
|
+
/**
|
|
1494
|
+
* Enable TLS for client-facing server (HTTPS + WSS)
|
|
1495
|
+
* @default false
|
|
1496
|
+
*/
|
|
1497
|
+
enabled: boolean;
|
|
1498
|
+
/**
|
|
1499
|
+
* Path to certificate file (PEM format)
|
|
1500
|
+
* Supports chain certificates
|
|
1501
|
+
*/
|
|
1502
|
+
certPath: string;
|
|
1503
|
+
/**
|
|
1504
|
+
* Path to private key file (PEM format)
|
|
1505
|
+
*/
|
|
1506
|
+
keyPath: string;
|
|
1507
|
+
/**
|
|
1508
|
+
* Path to CA certificate for verifying client certificates
|
|
1509
|
+
* Required for mTLS
|
|
1510
|
+
* @optional
|
|
1511
|
+
*/
|
|
1512
|
+
caCertPath?: string;
|
|
1513
|
+
/**
|
|
1514
|
+
* Minimum TLS version
|
|
1515
|
+
* @default 'TLSv1.2'
|
|
1516
|
+
*/
|
|
1517
|
+
minVersion?: 'TLSv1.2' | 'TLSv1.3';
|
|
1518
|
+
/**
|
|
1519
|
+
* List of allowed cipher suites
|
|
1520
|
+
* @optional - use Node.js defaults if not specified
|
|
1521
|
+
*/
|
|
1522
|
+
ciphers?: string;
|
|
1523
|
+
/**
|
|
1524
|
+
* Passphrase for encrypted private key
|
|
1525
|
+
* @optional
|
|
1526
|
+
*/
|
|
1527
|
+
passphrase?: string;
|
|
1528
|
+
}
|
|
1529
|
+
interface ClusterTLSConfig extends TLSConfig {
|
|
1530
|
+
/**
|
|
1531
|
+
* Require client certificate (mTLS)
|
|
1532
|
+
* @default false
|
|
1533
|
+
*/
|
|
1534
|
+
requireClientCert?: boolean;
|
|
1535
|
+
/**
|
|
1536
|
+
* Verify peer certificates
|
|
1537
|
+
* Can be disabled in development for self-signed certs
|
|
1538
|
+
* @default true
|
|
1539
|
+
*/
|
|
1540
|
+
rejectUnauthorized?: boolean;
|
|
1541
|
+
}
|
|
1542
|
+
|
|
1543
|
+
interface CoalescingWriterOptions {
|
|
1544
|
+
/**
|
|
1545
|
+
* Maximum messages to batch before forcing flush.
|
|
1546
|
+
* Default: 100
|
|
1547
|
+
*/
|
|
1548
|
+
maxBatchSize: number;
|
|
1549
|
+
/**
|
|
1550
|
+
* Maximum time to wait before flushing (ms).
|
|
1551
|
+
* Default: 5 (similar to Nagle's algorithm)
|
|
1552
|
+
*/
|
|
1553
|
+
maxDelayMs: number;
|
|
1554
|
+
/**
|
|
1555
|
+
* Maximum batch size in bytes.
|
|
1556
|
+
* Default: 65536 (64KB)
|
|
1557
|
+
*/
|
|
1558
|
+
maxBatchBytes: number;
|
|
1559
|
+
/**
|
|
1560
|
+
* Optional BufferPool for batch buffer reuse.
|
|
1561
|
+
* If not provided, uses the global buffer pool.
|
|
1562
|
+
*/
|
|
1563
|
+
bufferPool?: BufferPool;
|
|
1564
|
+
}
|
|
1565
|
+
/**
|
|
1566
|
+
* Extended metrics for CoalescingWriter performance analysis.
|
|
1567
|
+
*/
|
|
1568
|
+
interface CoalescingWriterMetrics {
|
|
1569
|
+
/** Total messages sent */
|
|
1570
|
+
messagesSent: number;
|
|
1571
|
+
/** Total batches sent */
|
|
1572
|
+
batchesSent: number;
|
|
1573
|
+
/** Total bytes sent */
|
|
1574
|
+
bytesSent: number;
|
|
1575
|
+
/** Average messages per batch */
|
|
1576
|
+
avgMessagesPerBatch: number;
|
|
1577
|
+
/** Average bytes per batch */
|
|
1578
|
+
avgBytesPerBatch: number;
|
|
1579
|
+
/** Messages currently in queue */
|
|
1580
|
+
pendingMessages: number;
|
|
1581
|
+
/** Bytes currently pending */
|
|
1582
|
+
pendingBytes: number;
|
|
1583
|
+
/** Count of flushes triggered by size limits (batch full or bytes exceeded) */
|
|
1584
|
+
immediateFlushes: number;
|
|
1585
|
+
/** Count of flushes triggered by timer expiration */
|
|
1586
|
+
timedFlushes: number;
|
|
1587
|
+
/** Ratio of actual batch size to maxBatchSize (0-1, higher = better utilization) */
|
|
1588
|
+
batchUtilization: number;
|
|
1589
|
+
/** Ratio of immediate flushes to total flushes (high = batches filling up quickly) */
|
|
1590
|
+
immediateFlushRatio: number;
|
|
1591
|
+
/** Count of pooled buffer acquisitions (for monitoring buffer pool usage) */
|
|
1592
|
+
pooledBuffersUsed: number;
|
|
1593
|
+
/** Count of oversized (non-pooled) buffers that were allocated directly */
|
|
1594
|
+
oversizedBuffers: number;
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1597
|
+
/**
|
|
1598
|
+
* Preset configurations for CoalescingWriter.
|
|
1599
|
+
* Based on Hazelcast OutboxImpl (batch size 2048) and real-world benchmarking.
|
|
1600
|
+
*
|
|
1601
|
+
* Trade-offs:
|
|
1602
|
+
* - Larger batch size = higher throughput, higher latency
|
|
1603
|
+
* - Longer delay = more messages per batch, higher latency
|
|
1604
|
+
* - Larger maxBatchBytes = handles larger payloads, more memory
|
|
1605
|
+
*
|
|
1606
|
+
* NOTE: A/B testing (Dec 2024) showed maxDelayMs is the primary bottleneck:
|
|
1607
|
+
* - 10ms delay: ~10K ops/sec, p50=11ms
|
|
1608
|
+
* - 1ms delay: ~18K ops/sec, p50=8ms (+80% throughput)
|
|
1609
|
+
* - 0ms (disabled): ~18K ops/sec, p50=2ms (best latency)
|
|
1610
|
+
*/
|
|
1611
|
+
declare const coalescingPresets: {
|
|
1612
|
+
/**
|
|
1613
|
+
* Low latency - optimized for minimal response time.
|
|
1614
|
+
* Best for: gaming, real-time chat, interactive applications.
|
|
1615
|
+
* Benchmark: p50=2ms, ~18K ops/sec
|
|
1616
|
+
*/
|
|
1617
|
+
readonly lowLatency: {
|
|
1618
|
+
readonly maxBatchSize: 100;
|
|
1619
|
+
readonly maxDelayMs: 1;
|
|
1620
|
+
readonly maxBatchBytes: 65536;
|
|
1621
|
+
};
|
|
1622
|
+
/**
|
|
1623
|
+
* Conservative - good balance of latency and batching.
|
|
1624
|
+
* Use for: general purpose with latency sensitivity.
|
|
1625
|
+
*/
|
|
1626
|
+
readonly conservative: {
|
|
1627
|
+
readonly maxBatchSize: 100;
|
|
1628
|
+
readonly maxDelayMs: 2;
|
|
1629
|
+
readonly maxBatchBytes: 65536;
|
|
1630
|
+
};
|
|
1631
|
+
/**
|
|
1632
|
+
* Balanced - good for most workloads.
|
|
1633
|
+
* Reasonable trade-off between throughput and latency.
|
|
1634
|
+
* Use for: mixed read/write applications, general purpose.
|
|
1635
|
+
*/
|
|
1636
|
+
readonly balanced: {
|
|
1637
|
+
readonly maxBatchSize: 300;
|
|
1638
|
+
readonly maxDelayMs: 2;
|
|
1639
|
+
readonly maxBatchBytes: 131072;
|
|
1640
|
+
};
|
|
1641
|
+
/**
|
|
1642
|
+
* High throughput - optimized for write-heavy workloads.
|
|
1643
|
+
* Higher batching for better network utilization.
|
|
1644
|
+
* Use for: data ingestion, logging, IoT data streams.
|
|
1645
|
+
* Benchmark: p50=7ms, ~18K ops/sec
|
|
1646
|
+
*/
|
|
1647
|
+
readonly highThroughput: {
|
|
1648
|
+
readonly maxBatchSize: 500;
|
|
1649
|
+
readonly maxDelayMs: 2;
|
|
1650
|
+
readonly maxBatchBytes: 262144;
|
|
1651
|
+
};
|
|
1652
|
+
/**
|
|
1653
|
+
* Aggressive - maximum batching for batch processing.
|
|
1654
|
+
* Closest to Hazelcast's OutboxImpl (batch size 2048).
|
|
1655
|
+
* Use for: batch imports, bulk operations, offline sync.
|
|
1656
|
+
*/
|
|
1657
|
+
readonly aggressive: {
|
|
1658
|
+
readonly maxBatchSize: 1000;
|
|
1659
|
+
readonly maxDelayMs: 5;
|
|
1660
|
+
readonly maxBatchBytes: 524288;
|
|
1661
|
+
};
|
|
1662
|
+
};
|
|
1663
|
+
/**
|
|
1664
|
+
* Available preset names for type safety.
|
|
1665
|
+
*/
|
|
1666
|
+
type CoalescingPreset = keyof typeof coalescingPresets;
|
|
1667
|
+
/**
|
|
1668
|
+
* Get preset configuration by name.
|
|
1669
|
+
* @param preset - Preset name
|
|
1670
|
+
* @returns CoalescingWriterOptions
|
|
1671
|
+
*/
|
|
1672
|
+
declare function getCoalescingPreset(preset: CoalescingPreset): CoalescingWriterOptions;
|
|
1673
|
+
|
|
1674
|
+
interface ServerCoordinatorConfig {
|
|
1675
|
+
port: number;
|
|
1676
|
+
nodeId: string;
|
|
1677
|
+
storage?: IServerStorage;
|
|
1678
|
+
jwtSecret?: string;
|
|
1679
|
+
host?: string;
|
|
1680
|
+
clusterPort?: number;
|
|
1681
|
+
peers?: string[];
|
|
1682
|
+
securityPolicies?: PermissionPolicy[];
|
|
1683
|
+
/** Callback to resolve dynamic peer addresses after ports are known */
|
|
1684
|
+
resolvePeers?: () => string[];
|
|
1685
|
+
interceptors?: IInterceptor[];
|
|
1686
|
+
metricsPort?: number;
|
|
1687
|
+
discovery?: 'manual' | 'kubernetes';
|
|
1688
|
+
serviceName?: string;
|
|
1689
|
+
discoveryInterval?: number;
|
|
1690
|
+
tls?: TLSConfig;
|
|
1691
|
+
clusterTls?: ClusterTLSConfig;
|
|
1692
|
+
/** Total event queue capacity for bounded queue (default: 10000) */
|
|
1693
|
+
eventQueueCapacity?: number;
|
|
1694
|
+
/** Number of event queue stripes for parallel processing (default: 4) */
|
|
1695
|
+
eventStripeCount?: number;
|
|
1696
|
+
/** Enable/disable backpressure (default: true) */
|
|
1697
|
+
backpressureEnabled?: boolean;
|
|
1698
|
+
/** How often to force sync processing (default: 100 operations) */
|
|
1699
|
+
backpressureSyncFrequency?: number;
|
|
1700
|
+
/** Maximum pending async operations before blocking (default: 1000) */
|
|
1701
|
+
backpressureMaxPending?: number;
|
|
1702
|
+
/** Backoff timeout in ms when at capacity (default: 5000) */
|
|
1703
|
+
backpressureBackoffMs?: number;
|
|
1704
|
+
/** Enable/disable write coalescing (default: true) */
|
|
1705
|
+
writeCoalescingEnabled?: boolean;
|
|
1706
|
+
/** Coalescing preset: 'conservative', 'balanced', 'highThroughput', 'aggressive' (default: 'highThroughput') */
|
|
1707
|
+
writeCoalescingPreset?: CoalescingPreset;
|
|
1708
|
+
/** Maximum messages to batch before forcing flush (default: 500 for highThroughput) */
|
|
1709
|
+
writeCoalescingMaxBatch?: number;
|
|
1710
|
+
/** Maximum delay before flushing in ms (default: 10 for highThroughput) */
|
|
1711
|
+
writeCoalescingMaxDelayMs?: number;
|
|
1712
|
+
/** Maximum batch size in bytes (default: 262144/256KB for highThroughput) */
|
|
1713
|
+
writeCoalescingMaxBytes?: number;
|
|
1714
|
+
/** WebSocket backlog for pending connections (default: 511) */
|
|
1715
|
+
wsBacklog?: number;
|
|
1716
|
+
/** Enable WebSocket per-message compression (default: false for CPU savings) */
|
|
1717
|
+
wsCompression?: boolean;
|
|
1718
|
+
/** Maximum WebSocket payload size in bytes (default: 64MB) */
|
|
1719
|
+
wsMaxPayload?: number;
|
|
1720
|
+
/** Maximum server connections (default: 10000) */
|
|
1721
|
+
maxConnections?: number;
|
|
1722
|
+
/** Server timeout in ms (default: 120000 = 2 min) */
|
|
1723
|
+
serverTimeout?: number;
|
|
1724
|
+
/** Keep-alive timeout in ms (default: 5000) */
|
|
1725
|
+
keepAliveTimeout?: number;
|
|
1726
|
+
/** Headers timeout in ms (default: 60000) */
|
|
1727
|
+
headersTimeout?: number;
|
|
1728
|
+
/** Enable connection rate limiting (default: true) */
|
|
1729
|
+
rateLimitingEnabled?: boolean;
|
|
1730
|
+
/** Maximum new connections per second (default: 100) */
|
|
1731
|
+
maxConnectionsPerSecond?: number;
|
|
1732
|
+
/** Maximum pending connections (default: 1000) */
|
|
1733
|
+
maxPendingConnections?: number;
|
|
1734
|
+
/** Enable worker pool for CPU-bound operations (default: false) */
|
|
1735
|
+
workerPoolEnabled?: boolean;
|
|
1736
|
+
/** Worker pool configuration */
|
|
1737
|
+
workerPoolConfig?: Partial<WorkerPoolConfig>;
|
|
1738
|
+
/** Default timeout for Write Concern acknowledgments in ms (default: 5000) */
|
|
1739
|
+
writeAckTimeout?: number;
|
|
1740
|
+
/** Enable replication to backup nodes (default: true when cluster has peers) */
|
|
1741
|
+
replicationEnabled?: boolean;
|
|
1742
|
+
/** Default consistency level for replication (default: EVENTUAL) */
|
|
1743
|
+
defaultConsistency?: ConsistencyLevel;
|
|
1744
|
+
/** Replication configuration */
|
|
1745
|
+
replicationConfig?: Partial<ReplicationConfig>;
|
|
1746
|
+
}
|
|
1747
|
+
declare class ServerCoordinator {
|
|
1748
|
+
private httpServer;
|
|
1749
|
+
private metricsServer?;
|
|
1750
|
+
private metricsService;
|
|
1751
|
+
private wss;
|
|
1752
|
+
private clients;
|
|
1753
|
+
private interceptors;
|
|
1754
|
+
private maps;
|
|
1755
|
+
private hlc;
|
|
1756
|
+
private storage?;
|
|
1757
|
+
private jwtSecret;
|
|
1758
|
+
private queryRegistry;
|
|
1759
|
+
private cluster;
|
|
1760
|
+
private partitionService;
|
|
1761
|
+
private replicationPipeline?;
|
|
1762
|
+
private lockManager;
|
|
1763
|
+
private topicManager;
|
|
1764
|
+
private securityManager;
|
|
1765
|
+
private systemManager;
|
|
1766
|
+
private pendingClusterQueries;
|
|
1767
|
+
private gcInterval?;
|
|
1768
|
+
private heartbeatCheckInterval?;
|
|
1769
|
+
private gcReports;
|
|
1770
|
+
private mapLoadingPromises;
|
|
1771
|
+
private pendingBatchOperations;
|
|
1772
|
+
private eventExecutor;
|
|
1773
|
+
private backpressure;
|
|
1774
|
+
private writeCoalescingEnabled;
|
|
1775
|
+
private writeCoalescingOptions;
|
|
1776
|
+
private rateLimiter;
|
|
1777
|
+
private rateLimitingEnabled;
|
|
1778
|
+
private workerPool?;
|
|
1779
|
+
private merkleWorker?;
|
|
1780
|
+
private crdtMergeWorker?;
|
|
1781
|
+
private serializationWorker?;
|
|
1782
|
+
private eventPayloadPool;
|
|
1783
|
+
private taskletScheduler;
|
|
1784
|
+
private writeAckManager;
|
|
1785
|
+
private _actualPort;
|
|
1786
|
+
private _actualClusterPort;
|
|
1787
|
+
private _readyPromise;
|
|
1788
|
+
private _readyResolve;
|
|
1789
|
+
constructor(config: ServerCoordinatorConfig);
|
|
1790
|
+
/** Wait for server to be fully ready (ports assigned) */
|
|
1791
|
+
ready(): Promise<void>;
|
|
1792
|
+
/**
|
|
1793
|
+
* Wait for all pending batch operations to complete.
|
|
1794
|
+
* Useful for tests that need to verify state after OP_BATCH.
|
|
1795
|
+
*/
|
|
1796
|
+
waitForPendingBatches(): Promise<void>;
|
|
1797
|
+
/** Get the actual port the server is listening on */
|
|
1798
|
+
get port(): number;
|
|
1799
|
+
/** Get the actual cluster port */
|
|
1800
|
+
get clusterPort(): number;
|
|
1801
|
+
/** Get event executor metrics for monitoring */
|
|
1802
|
+
getEventExecutorMetrics(): StripeMetrics[];
|
|
1803
|
+
/** Get total event executor metrics across all stripes */
|
|
1804
|
+
getEventExecutorTotalMetrics(): QueueMetrics;
|
|
1805
|
+
/** Get connection rate limiter stats for monitoring */
|
|
1806
|
+
getRateLimiterStats(): RateLimiterStats;
|
|
1807
|
+
/** Get worker pool stats for monitoring */
|
|
1808
|
+
getWorkerPoolStats(): WorkerPoolStats | null;
|
|
1809
|
+
/** Check if worker pool is enabled */
|
|
1810
|
+
get workerPoolEnabled(): boolean;
|
|
1811
|
+
/** Get MerkleWorker for external use (null if worker pool disabled) */
|
|
1812
|
+
getMerkleWorker(): MerkleWorker | null;
|
|
1813
|
+
/** Get CRDTMergeWorker for external use (null if worker pool disabled) */
|
|
1814
|
+
getCRDTMergeWorker(): CRDTMergeWorker | null;
|
|
1815
|
+
/** Get SerializationWorker for external use (null if worker pool disabled) */
|
|
1816
|
+
getSerializationWorker(): SerializationWorker | null;
|
|
1817
|
+
/** Get memory pool stats for monitoring GC pressure reduction */
|
|
1818
|
+
getMemoryPoolStats(): {
|
|
1819
|
+
eventPayloadPool: ObjectPoolStats;
|
|
1820
|
+
};
|
|
1821
|
+
/** Get tasklet scheduler stats for monitoring cooperative multitasking */
|
|
1822
|
+
getTaskletSchedulerStats(): TaskletSchedulerStats;
|
|
1823
|
+
/** Get tasklet scheduler for scheduling long-running operations */
|
|
1824
|
+
getTaskletScheduler(): TaskletScheduler;
|
|
1825
|
+
shutdown(): Promise<void>;
|
|
1826
|
+
private handleConnection;
|
|
1827
|
+
private handleMessage;
|
|
1828
|
+
private updateClientHlc;
|
|
1829
|
+
/**
|
|
1830
|
+
* Broadcast partition map to all connected and authenticated clients.
|
|
1831
|
+
* Called when partition topology changes (node join/leave/failover).
|
|
1832
|
+
*/
|
|
1833
|
+
private broadcastPartitionMap;
|
|
1834
|
+
private broadcast;
|
|
1835
|
+
/**
|
|
1836
|
+
* === OPTIMIZATION 2 & 3: Batched Broadcast with Serialization Caching ===
|
|
1837
|
+
* Groups clients by their permission roles and serializes once per group.
|
|
1838
|
+
* Also batches multiple events into a single SERVER_BATCH_EVENT message.
|
|
1839
|
+
* === OPTIMIZATION 4: Subscription-based Routing ===
|
|
1840
|
+
* Only sends events to clients with active subscriptions for affected maps.
|
|
1841
|
+
*/
|
|
1842
|
+
private broadcastBatch;
|
|
1843
|
+
/**
|
|
1844
|
+
* Helper method to get role signature for a client (for caching key)
|
|
1845
|
+
*/
|
|
1846
|
+
private getClientRoleSignature;
|
|
1847
|
+
/**
|
|
1848
|
+
* === BACKPRESSURE: Synchronous Broadcast ===
|
|
1849
|
+
* Same as broadcastBatch but waits for all sends to complete.
|
|
1850
|
+
* Used when backpressure forces sync processing to drain the pipeline.
|
|
1851
|
+
*/
|
|
1852
|
+
private broadcastBatchSync;
|
|
1853
|
+
private setupClusterListeners;
|
|
1854
|
+
private executeLocalQuery;
|
|
1855
|
+
private finalizeClusterQuery;
|
|
1856
|
+
/**
|
|
1857
|
+
* Core operation application logic shared between processLocalOp and processLocalOpForBatch.
|
|
1858
|
+
* Handles map merge, storage persistence, query evaluation, and event generation.
|
|
1859
|
+
*
|
|
1860
|
+
* @returns Event payload for broadcasting (or null if operation failed)
|
|
1861
|
+
*/
|
|
1862
|
+
private applyOpToMap;
|
|
1863
|
+
/**
|
|
1864
|
+
* Broadcast event to cluster members (excluding self).
|
|
1865
|
+
*/
|
|
1866
|
+
private broadcastToCluster;
|
|
1867
|
+
/**
|
|
1868
|
+
* Apply replicated operation from another node (callback for ReplicationPipeline)
|
|
1869
|
+
* This is called when we receive a replicated operation as a backup node
|
|
1870
|
+
*/
|
|
1871
|
+
private applyReplicatedOperation;
|
|
1872
|
+
/**
|
|
1873
|
+
* Build OpContext for interceptors.
|
|
1874
|
+
*/
|
|
1875
|
+
private buildOpContext;
|
|
1876
|
+
/**
|
|
1877
|
+
* Run onBeforeOp interceptors. Returns modified op or null if dropped.
|
|
1878
|
+
*/
|
|
1879
|
+
private runBeforeInterceptors;
|
|
1880
|
+
/**
|
|
1881
|
+
* Run onAfterOp interceptors (fire-and-forget).
|
|
1882
|
+
*/
|
|
1883
|
+
private runAfterInterceptors;
|
|
1884
|
+
private handleLockGranted;
|
|
1885
|
+
private processLocalOp;
|
|
1886
|
+
/**
|
|
1887
|
+
* === OPTIMIZATION 1: Async Batch Processing with Backpressure ===
|
|
1888
|
+
* Processes validated operations asynchronously after ACK has been sent.
|
|
1889
|
+
* Uses BackpressureRegulator to periodically force sync processing and
|
|
1890
|
+
* prevent unbounded accumulation of async work.
|
|
1891
|
+
*/
|
|
1892
|
+
private processBatchAsync;
|
|
1893
|
+
/**
|
|
1894
|
+
* === BACKPRESSURE: Synchronous Batch Processing ===
|
|
1895
|
+
* Processes operations synchronously, waiting for broadcast completion.
|
|
1896
|
+
* Used when backpressure forces sync to drain the pipeline.
|
|
1897
|
+
*/
|
|
1898
|
+
private processBatchSync;
|
|
1899
|
+
/**
|
|
1900
|
+
* Forward operation to owner node and wait for completion.
|
|
1901
|
+
* Used in sync processing mode.
|
|
1902
|
+
*/
|
|
1903
|
+
private forwardOpAndWait;
|
|
1904
|
+
/**
|
|
1905
|
+
* Process a single operation for batch processing.
|
|
1906
|
+
* Uses shared applyOpToMap but collects events instead of broadcasting immediately.
|
|
1907
|
+
*/
|
|
1908
|
+
private processLocalOpForBatch;
|
|
1909
|
+
private handleClusterEvent;
|
|
1910
|
+
getMap(name: string, typeHint?: 'LWW' | 'OR'): LWWMap<string, any> | ORMap<string, any>;
|
|
1911
|
+
/**
|
|
1912
|
+
* Returns map after ensuring it's fully loaded from storage.
|
|
1913
|
+
* Use this for queries to avoid returning empty results during initial load.
|
|
1914
|
+
*/
|
|
1915
|
+
getMapAsync(name: string, typeHint?: 'LWW' | 'OR'): Promise<LWWMap<string, any> | ORMap<string, any>>;
|
|
1916
|
+
private loadMapFromStorage;
|
|
1917
|
+
private startGarbageCollection;
|
|
1918
|
+
/**
|
|
1919
|
+
* Starts the periodic check for dead clients (those that haven't sent PING).
|
|
1920
|
+
*/
|
|
1921
|
+
private startHeartbeatCheck;
|
|
1922
|
+
/**
|
|
1923
|
+
* Handles incoming PING message from client.
|
|
1924
|
+
* Responds with PONG immediately.
|
|
1925
|
+
*/
|
|
1926
|
+
private handlePing;
|
|
1927
|
+
/**
|
|
1928
|
+
* Checks if a client is still alive based on heartbeat.
|
|
1929
|
+
*/
|
|
1930
|
+
isClientAlive(clientId: string): boolean;
|
|
1931
|
+
/**
|
|
1932
|
+
* Returns how long the client has been idle (no PING received).
|
|
1933
|
+
*/
|
|
1934
|
+
getClientIdleTime(clientId: string): number;
|
|
1935
|
+
/**
|
|
1936
|
+
* Evicts clients that haven't sent a PING within the timeout period.
|
|
1937
|
+
*/
|
|
1938
|
+
private evictDeadClients;
|
|
1939
|
+
private reportLocalHlc;
|
|
1940
|
+
private handleGcReport;
|
|
1941
|
+
private performGarbageCollection;
|
|
1942
|
+
private buildTLSOptions;
|
|
1943
|
+
/**
|
|
1944
|
+
* Get effective Write Concern level for an operation.
|
|
1945
|
+
* Per-op writeConcern overrides batch-level.
|
|
1946
|
+
*/
|
|
1947
|
+
private getEffectiveWriteConcern;
|
|
1948
|
+
/**
|
|
1949
|
+
* Convert string WriteConcern value to enum.
|
|
1950
|
+
*/
|
|
1951
|
+
private stringToWriteConcern;
|
|
1952
|
+
/**
|
|
1953
|
+
* Process batch with Write Concern tracking.
|
|
1954
|
+
* Notifies WriteAckManager at each stage of processing.
|
|
1955
|
+
*/
|
|
1956
|
+
private processBatchAsyncWithWriteConcern;
|
|
1957
|
+
/**
|
|
1958
|
+
* Synchronous batch processing with Write Concern.
|
|
1959
|
+
*/
|
|
1960
|
+
private processBatchSyncWithWriteConcern;
|
|
1961
|
+
/**
|
|
1962
|
+
* Process a single operation with Write Concern level notifications.
|
|
1963
|
+
*/
|
|
1964
|
+
private processLocalOpWithWriteConcern;
|
|
1965
|
+
/**
|
|
1966
|
+
* Persist operation synchronously (blocking).
|
|
1967
|
+
* Used for PERSISTED Write Concern.
|
|
1968
|
+
*/
|
|
1969
|
+
private persistOpSync;
|
|
1970
|
+
/**
|
|
1971
|
+
* Persist operation asynchronously (fire-and-forget).
|
|
1972
|
+
* Used for non-PERSISTED Write Concern levels.
|
|
1973
|
+
*/
|
|
1974
|
+
private persistOpAsync;
|
|
1975
|
+
}
|
|
1976
|
+
|
|
1977
|
+
interface PostgresAdapterOptions {
|
|
1978
|
+
tableName?: string;
|
|
1979
|
+
}
|
|
1980
|
+
declare class PostgresAdapter implements IServerStorage {
|
|
1981
|
+
private pool;
|
|
1982
|
+
private tableName;
|
|
1983
|
+
constructor(configOrPool: PoolConfig | Pool, options?: PostgresAdapterOptions);
|
|
1984
|
+
initialize(): Promise<void>;
|
|
1985
|
+
close(): Promise<void>;
|
|
1986
|
+
load(mapName: string, key: string): Promise<StorageValue<any> | undefined>;
|
|
1987
|
+
loadAll(mapName: string, keys: string[]): Promise<Map<string, StorageValue<any>>>;
|
|
1988
|
+
loadAllKeys(mapName: string): Promise<string[]>;
|
|
1989
|
+
store(mapName: string, key: string, record: StorageValue<any>): Promise<void>;
|
|
1990
|
+
storeAll(mapName: string, records: Map<string, StorageValue<any>>): Promise<void>;
|
|
1991
|
+
delete(mapName: string, key: string): Promise<void>;
|
|
1992
|
+
deleteAll(mapName: string, keys: string[]): Promise<void>;
|
|
1993
|
+
private mapRowToRecord;
|
|
1994
|
+
private isORMapValue;
|
|
1995
|
+
}
|
|
1996
|
+
|
|
1997
|
+
/**
|
|
1998
|
+
* In-memory implementation of IServerStorage.
|
|
1999
|
+
* Useful for development, testing, and demos without requiring a database.
|
|
2000
|
+
*
|
|
2001
|
+
* Note: Data is lost when the server restarts.
|
|
2002
|
+
*/
|
|
2003
|
+
declare class MemoryServerAdapter implements IServerStorage {
|
|
2004
|
+
private storage;
|
|
2005
|
+
initialize(): Promise<void>;
|
|
2006
|
+
close(): Promise<void>;
|
|
2007
|
+
private getMap;
|
|
2008
|
+
load(mapName: string, key: string): Promise<StorageValue<any> | undefined>;
|
|
2009
|
+
loadAll(mapName: string, keys: string[]): Promise<Map<string, StorageValue<any>>>;
|
|
2010
|
+
loadAllKeys(mapName: string): Promise<string[]>;
|
|
2011
|
+
store(mapName: string, key: string, record: StorageValue<any>): Promise<void>;
|
|
2012
|
+
storeAll(mapName: string, records: Map<string, StorageValue<any>>): Promise<void>;
|
|
2013
|
+
delete(mapName: string, key: string): Promise<void>;
|
|
2014
|
+
deleteAll(mapName: string, keys: string[]): Promise<void>;
|
|
2015
|
+
}
|
|
2016
|
+
|
|
2017
|
+
declare class SecurityManager {
|
|
2018
|
+
private policies;
|
|
2019
|
+
constructor(policies?: PermissionPolicy[]);
|
|
2020
|
+
addPolicy(policy: PermissionPolicy): void;
|
|
2021
|
+
checkPermission(principal: Principal, mapName: string, action: PermissionType): boolean;
|
|
2022
|
+
filterObject(object: any, principal: Principal, mapName: string): any;
|
|
2023
|
+
private hasRole;
|
|
2024
|
+
private matchesMap;
|
|
2025
|
+
}
|
|
2026
|
+
|
|
2027
|
+
declare const logger: pino.Logger<never, boolean>;
|
|
2028
|
+
type Logger = typeof logger;
|
|
2029
|
+
|
|
2030
|
+
/**
|
|
2031
|
+
* ConnectionRateLimiter - Rate limiter for incoming WebSocket connections.
|
|
2032
|
+
*
|
|
2033
|
+
* Implements connection rate limiting to prevent connection storms and
|
|
2034
|
+
* protect the server from being overwhelmed during high load scenarios.
|
|
2035
|
+
*
|
|
2036
|
+
* Features:
|
|
2037
|
+
* - Limits connections per second to prevent TCP backlog exhaustion
|
|
2038
|
+
* - Tracks pending connections (in-progress handshakes)
|
|
2039
|
+
* - Provides graceful rejection when limits are exceeded
|
|
2040
|
+
* - Auto-resets counters after cooldown period
|
|
2041
|
+
*/
|
|
2042
|
+
interface RateLimiterConfig {
|
|
2043
|
+
/** Maximum new connections allowed per second (default: 100) */
|
|
2044
|
+
maxConnectionsPerSecond: number;
|
|
2045
|
+
/** Maximum pending connections waiting for handshake (default: 1000) */
|
|
2046
|
+
maxPendingConnections: number;
|
|
2047
|
+
/** Cooldown period in ms after which counters reset (default: 1000) */
|
|
2048
|
+
cooldownMs: number;
|
|
2049
|
+
}
|
|
2050
|
+
interface RateLimiterStats {
|
|
2051
|
+
/** Current connections per second rate */
|
|
2052
|
+
connectionsPerSecond: number;
|
|
2053
|
+
/** Number of connections currently pending (handshake in progress) */
|
|
2054
|
+
pendingConnections: number;
|
|
2055
|
+
/** Total connections established since start */
|
|
2056
|
+
totalConnections: number;
|
|
2057
|
+
/** Total connections rejected due to rate limiting */
|
|
2058
|
+
totalRejected: number;
|
|
2059
|
+
}
|
|
2060
|
+
declare class ConnectionRateLimiter {
|
|
2061
|
+
private config;
|
|
2062
|
+
/** Connection attempts in current window */
|
|
2063
|
+
private connectionCount;
|
|
2064
|
+
/** Timestamp when current window started */
|
|
2065
|
+
private windowStart;
|
|
2066
|
+
/** Currently pending connections (handshake in progress) */
|
|
2067
|
+
private pendingCount;
|
|
2068
|
+
/** Total connections established since start */
|
|
2069
|
+
private totalConnections;
|
|
2070
|
+
/** Total connections rejected since start */
|
|
2071
|
+
private totalRejected;
|
|
2072
|
+
constructor(config?: Partial<RateLimiterConfig>);
|
|
2073
|
+
/**
|
|
2074
|
+
* Check if a new connection should be accepted.
|
|
2075
|
+
* @returns true if the connection should be accepted, false if it should be rejected
|
|
2076
|
+
*/
|
|
2077
|
+
shouldAccept(): boolean;
|
|
2078
|
+
/**
|
|
2079
|
+
* Register a connection attempt.
|
|
2080
|
+
* Call this when a new connection is initiated (before handshake completes).
|
|
2081
|
+
*/
|
|
2082
|
+
onConnectionAttempt(): void;
|
|
2083
|
+
/**
|
|
2084
|
+
* Register that a connection has been established (handshake complete).
|
|
2085
|
+
* Call this when the connection is fully established and authenticated.
|
|
2086
|
+
*/
|
|
2087
|
+
onConnectionEstablished(): void;
|
|
2088
|
+
/**
|
|
2089
|
+
* Register that a connection has been closed.
|
|
2090
|
+
* Call this when a pending connection is closed before completing handshake.
|
|
2091
|
+
*/
|
|
2092
|
+
onConnectionClosed(): void;
|
|
2093
|
+
/**
|
|
2094
|
+
* Register that a connection was rejected.
|
|
2095
|
+
* Call this when shouldAccept() returns false and the connection is rejected.
|
|
2096
|
+
*/
|
|
2097
|
+
onConnectionRejected(): void;
|
|
2098
|
+
/**
|
|
2099
|
+
* Decrease pending count when a connection times out or fails.
|
|
2100
|
+
* Call this when a pending connection fails to complete handshake.
|
|
2101
|
+
*/
|
|
2102
|
+
onPendingConnectionFailed(): void;
|
|
2103
|
+
/**
|
|
2104
|
+
* Get current rate limiter statistics.
|
|
2105
|
+
*/
|
|
2106
|
+
getStats(): RateLimiterStats;
|
|
2107
|
+
/**
|
|
2108
|
+
* Reset the rate limiter state.
|
|
2109
|
+
* Useful for testing or when recovering from errors.
|
|
2110
|
+
*/
|
|
2111
|
+
reset(): void;
|
|
2112
|
+
/**
|
|
2113
|
+
* Update configuration at runtime.
|
|
2114
|
+
*/
|
|
2115
|
+
updateConfig(config: Partial<RateLimiterConfig>): void;
|
|
2116
|
+
/**
|
|
2117
|
+
* Check if window has expired and reset if needed.
|
|
2118
|
+
*/
|
|
2119
|
+
private maybeResetWindow;
|
|
2120
|
+
}
|
|
2121
|
+
|
|
2122
|
+
declare class TimestampInterceptor implements IInterceptor {
|
|
2123
|
+
name: string;
|
|
2124
|
+
onBeforeOp(op: ServerOp, context: OpContext): Promise<ServerOp>;
|
|
2125
|
+
}
|
|
2126
|
+
|
|
2127
|
+
interface RateLimitConfig {
|
|
2128
|
+
windowMs: number;
|
|
2129
|
+
maxOps: number;
|
|
2130
|
+
}
|
|
2131
|
+
declare class RateLimitInterceptor implements IInterceptor {
|
|
2132
|
+
name: string;
|
|
2133
|
+
private limits;
|
|
2134
|
+
private config;
|
|
2135
|
+
constructor(config?: RateLimitConfig);
|
|
2136
|
+
onBeforeOp(op: ServerOp, context: OpContext): Promise<ServerOp | null>;
|
|
2137
|
+
onDisconnect(context: any): Promise<void>;
|
|
2138
|
+
}
|
|
2139
|
+
|
|
2140
|
+
/**
|
|
2141
|
+
* Native Module Statistics
|
|
2142
|
+
*
|
|
2143
|
+
* Provides information about available native optimizations.
|
|
2144
|
+
*
|
|
2145
|
+
* Phase 3.05: Integration
|
|
2146
|
+
*/
|
|
2147
|
+
|
|
2148
|
+
/**
|
|
2149
|
+
* Native module availability status
|
|
2150
|
+
*/
|
|
2151
|
+
interface NativeModuleStatus {
|
|
2152
|
+
/** Native xxHash64 is available and being used */
|
|
2153
|
+
nativeHash: boolean;
|
|
2154
|
+
/** SharedArrayBuffer is available */
|
|
2155
|
+
sharedArrayBuffer: boolean;
|
|
2156
|
+
}
|
|
2157
|
+
/**
|
|
2158
|
+
* Comprehensive native statistics
|
|
2159
|
+
*/
|
|
2160
|
+
interface NativeStats {
|
|
2161
|
+
/** Module availability status */
|
|
2162
|
+
modules: NativeModuleStatus;
|
|
2163
|
+
/** Shared memory statistics (if enabled) */
|
|
2164
|
+
sharedMemory: SharedMemoryStats | null;
|
|
2165
|
+
/** Summary of what's being used */
|
|
2166
|
+
summary: string;
|
|
2167
|
+
}
|
|
2168
|
+
/**
|
|
2169
|
+
* Check which native modules are available.
|
|
2170
|
+
*/
|
|
2171
|
+
declare function getNativeModuleStatus(): NativeModuleStatus;
|
|
2172
|
+
/**
|
|
2173
|
+
* Get native statistics including shared memory.
|
|
2174
|
+
*
|
|
2175
|
+
* @param sharedMemoryManager - Optional SharedMemoryManager instance
|
|
2176
|
+
*/
|
|
2177
|
+
declare function getNativeStats(sharedMemoryManager?: SharedMemoryManager): NativeStats;
|
|
2178
|
+
/**
|
|
2179
|
+
* Log native module status to console.
|
|
2180
|
+
*/
|
|
2181
|
+
declare function logNativeStatus(): void;
|
|
2182
|
+
|
|
2183
|
+
/**
|
|
2184
|
+
* FailureDetector - Phi Accrual Failure Detector
|
|
2185
|
+
*
|
|
2186
|
+
* Implements the Phi Accrual Failure Detection algorithm for distributed systems.
|
|
2187
|
+
* Based on the paper: "The φ Accrual Failure Detector" by Hayashibara et al.
|
|
2188
|
+
*
|
|
2189
|
+
* The detector provides a suspicion level (phi) rather than binary alive/dead status,
|
|
2190
|
+
* allowing the application to make decisions based on configurable thresholds.
|
|
2191
|
+
*
|
|
2192
|
+
* Hazelcast equivalent: com.hazelcast.internal.cluster.fd.PhiAccrualFailureDetector
|
|
2193
|
+
*/
|
|
2194
|
+
|
|
2195
|
+
interface FailureDetectorConfig {
|
|
2196
|
+
/** Interval between heartbeat checks (ms). Default: 1000 */
|
|
2197
|
+
heartbeatIntervalMs: number;
|
|
2198
|
+
/** Time after which a node is suspected if no heartbeat received (ms). Default: 5000 */
|
|
2199
|
+
suspicionTimeoutMs: number;
|
|
2200
|
+
/** Time after suspicion before confirming failure (ms). Default: 10000 */
|
|
2201
|
+
confirmationTimeoutMs: number;
|
|
2202
|
+
/** Phi threshold above which a node is considered suspected. Default: 8 */
|
|
2203
|
+
phiThreshold: number;
|
|
2204
|
+
/** Minimum samples required for accurate phi calculation. Default: 10 */
|
|
2205
|
+
minSamples: number;
|
|
2206
|
+
/** Maximum samples to keep in history. Default: 100 */
|
|
2207
|
+
maxSamples: number;
|
|
2208
|
+
/** Initial heartbeat interval estimate (ms). Default: 1000 */
|
|
2209
|
+
initialHeartbeatIntervalMs: number;
|
|
2210
|
+
}
|
|
2211
|
+
declare class FailureDetector extends EventEmitter {
|
|
2212
|
+
private config;
|
|
2213
|
+
private nodeStates;
|
|
2214
|
+
private monitoringNodes;
|
|
2215
|
+
private checkTimer?;
|
|
2216
|
+
private confirmationTimers;
|
|
2217
|
+
private started;
|
|
2218
|
+
constructor(config?: Partial<FailureDetectorConfig>);
|
|
2219
|
+
/**
|
|
2220
|
+
* Start the failure detector monitoring loop.
|
|
2221
|
+
*/
|
|
2222
|
+
start(): void;
|
|
2223
|
+
/**
|
|
2224
|
+
* Stop the failure detector and clean up.
|
|
2225
|
+
*/
|
|
2226
|
+
stop(): void;
|
|
2227
|
+
/**
|
|
2228
|
+
* Start monitoring a node.
|
|
2229
|
+
*/
|
|
2230
|
+
startMonitoring(nodeId: string): void;
|
|
2231
|
+
/**
|
|
2232
|
+
* Stop monitoring a node.
|
|
2233
|
+
*/
|
|
2234
|
+
stopMonitoring(nodeId: string): void;
|
|
2235
|
+
/**
|
|
2236
|
+
* Record a heartbeat from a node.
|
|
2237
|
+
* This updates the node's state and clears any suspicion.
|
|
2238
|
+
*/
|
|
2239
|
+
recordHeartbeat(nodeId: string): void;
|
|
2240
|
+
/**
|
|
2241
|
+
* Check all monitored nodes for failure.
|
|
2242
|
+
*/
|
|
2243
|
+
private checkAllNodes;
|
|
2244
|
+
/**
|
|
2245
|
+
* Schedule failure confirmation after suspicion timeout.
|
|
2246
|
+
*/
|
|
2247
|
+
private scheduleConfirmation;
|
|
2248
|
+
/**
|
|
2249
|
+
* Confirm node failure after confirmation timeout.
|
|
2250
|
+
*/
|
|
2251
|
+
private confirmFailure;
|
|
2252
|
+
/**
|
|
2253
|
+
* Calculate the phi value for a node using the Phi Accrual algorithm.
|
|
2254
|
+
*
|
|
2255
|
+
* Phi = -log10(P_later(t_now - t_last))
|
|
2256
|
+
*
|
|
2257
|
+
* where P_later is the probability that a heartbeat will arrive later than expected.
|
|
2258
|
+
*/
|
|
2259
|
+
calculatePhi(nodeId: string): number;
|
|
2260
|
+
/**
|
|
2261
|
+
* Calculate mean of an array of numbers.
|
|
2262
|
+
*/
|
|
2263
|
+
private calculateMean;
|
|
2264
|
+
/**
|
|
2265
|
+
* Calculate variance of an array of numbers.
|
|
2266
|
+
*/
|
|
2267
|
+
private calculateVariance;
|
|
2268
|
+
/**
|
|
2269
|
+
* Get list of currently suspected nodes.
|
|
2270
|
+
*/
|
|
2271
|
+
getSuspectedNodes(): string[];
|
|
2272
|
+
/**
|
|
2273
|
+
* Get list of confirmed failed nodes.
|
|
2274
|
+
*/
|
|
2275
|
+
getConfirmedFailedNodes(): string[];
|
|
2276
|
+
/**
|
|
2277
|
+
* Check if a specific node is suspected.
|
|
2278
|
+
*/
|
|
2279
|
+
isSuspected(nodeId: string): boolean;
|
|
2280
|
+
/**
|
|
2281
|
+
* Check if a specific node's failure is confirmed.
|
|
2282
|
+
*/
|
|
2283
|
+
isConfirmedFailed(nodeId: string): boolean;
|
|
2284
|
+
/**
|
|
2285
|
+
* Get the current phi value for a node.
|
|
2286
|
+
*/
|
|
2287
|
+
getPhi(nodeId: string): number;
|
|
2288
|
+
/**
|
|
2289
|
+
* Get all monitored nodes.
|
|
2290
|
+
*/
|
|
2291
|
+
getMonitoredNodes(): string[];
|
|
2292
|
+
/**
|
|
2293
|
+
* Get metrics for monitoring.
|
|
2294
|
+
*/
|
|
2295
|
+
getMetrics(): {
|
|
2296
|
+
monitoredNodes: number;
|
|
2297
|
+
suspectedNodes: number;
|
|
2298
|
+
confirmedFailedNodes: number;
|
|
2299
|
+
};
|
|
2300
|
+
}
|
|
2301
|
+
|
|
2302
|
+
interface ClusterConfig {
|
|
2303
|
+
nodeId: string;
|
|
2304
|
+
host: string;
|
|
2305
|
+
port: number;
|
|
2306
|
+
peers: string[];
|
|
2307
|
+
discovery?: 'manual' | 'kubernetes';
|
|
2308
|
+
serviceName?: string;
|
|
2309
|
+
discoveryInterval?: number;
|
|
2310
|
+
tls?: ClusterTLSConfig;
|
|
2311
|
+
/** Heartbeat interval in milliseconds. Default: 1000 */
|
|
2312
|
+
heartbeatIntervalMs?: number;
|
|
2313
|
+
/** Failure detection configuration */
|
|
2314
|
+
failureDetection?: Partial<FailureDetectorConfig>;
|
|
2315
|
+
}
|
|
2316
|
+
interface ClusterMember {
|
|
2317
|
+
nodeId: string;
|
|
2318
|
+
host: string;
|
|
2319
|
+
port: number;
|
|
2320
|
+
socket: WebSocket;
|
|
2321
|
+
isSelf: boolean;
|
|
2322
|
+
}
|
|
2323
|
+
interface ClusterMessage {
|
|
2324
|
+
type: 'HELLO' | 'OP_FORWARD' | 'PARTITION_UPDATE' | 'HEARTBEAT' | 'CLUSTER_EVENT' | 'CLUSTER_QUERY_EXEC' | 'CLUSTER_QUERY_RESP' | 'CLUSTER_GC_REPORT' | 'CLUSTER_GC_COMMIT' | 'CLUSTER_LOCK_REQ' | 'CLUSTER_LOCK_RELEASE' | 'CLUSTER_LOCK_GRANTED' | 'CLUSTER_LOCK_RELEASED' | 'CLUSTER_CLIENT_DISCONNECTED' | 'CLUSTER_TOPIC_PUB';
|
|
2325
|
+
senderId: string;
|
|
2326
|
+
payload: any;
|
|
2327
|
+
}
|
|
2328
|
+
declare class ClusterManager extends EventEmitter {
|
|
2329
|
+
readonly config: ClusterConfig;
|
|
2330
|
+
private server?;
|
|
2331
|
+
private members;
|
|
2332
|
+
private pendingConnections;
|
|
2333
|
+
private reconnectIntervals;
|
|
2334
|
+
private discoveryTimer?;
|
|
2335
|
+
private heartbeatTimer?;
|
|
2336
|
+
private failureDetector;
|
|
2337
|
+
constructor(config: ClusterConfig);
|
|
2338
|
+
/**
|
|
2339
|
+
* Get the failure detector instance.
|
|
2340
|
+
*/
|
|
2341
|
+
getFailureDetector(): FailureDetector;
|
|
2342
|
+
private _actualPort;
|
|
2343
|
+
/** Get the actual port the cluster is listening on */
|
|
2344
|
+
get port(): number;
|
|
2345
|
+
start(): Promise<number>;
|
|
2346
|
+
/** Called when server is ready - registers self and initiates peer connections */
|
|
2347
|
+
private onServerReady;
|
|
2348
|
+
stop(): void;
|
|
2349
|
+
/**
|
|
2350
|
+
* Start sending heartbeats to all peers.
|
|
2351
|
+
*/
|
|
2352
|
+
private startHeartbeat;
|
|
2353
|
+
/**
|
|
2354
|
+
* Stop sending heartbeats.
|
|
2355
|
+
*/
|
|
2356
|
+
private stopHeartbeat;
|
|
2357
|
+
/**
|
|
2358
|
+
* Send heartbeat to all connected peers.
|
|
2359
|
+
*/
|
|
2360
|
+
private sendHeartbeatToAll;
|
|
2361
|
+
/**
|
|
2362
|
+
* Handle incoming heartbeat from a peer.
|
|
2363
|
+
*/
|
|
2364
|
+
private handleHeartbeat;
|
|
2365
|
+
/**
|
|
2366
|
+
* Handle confirmed node failure.
|
|
2367
|
+
*/
|
|
2368
|
+
private handleNodeFailure;
|
|
2369
|
+
private connectToPeers;
|
|
2370
|
+
private startDiscovery;
|
|
2371
|
+
private scheduleReconnect;
|
|
2372
|
+
private connectToPeerWithBackoff;
|
|
2373
|
+
private connectToPeer;
|
|
2374
|
+
private _connectToPeerInternal;
|
|
2375
|
+
private handleSocket;
|
|
2376
|
+
send(nodeId: string, type: ClusterMessage['type'], payload: any): void;
|
|
2377
|
+
sendToNode(nodeId: string, message: any): void;
|
|
2378
|
+
getMembers(): string[];
|
|
2379
|
+
isLocal(nodeId: string): boolean;
|
|
2380
|
+
private buildClusterTLSOptions;
|
|
2381
|
+
}
|
|
2382
|
+
|
|
2383
|
+
/**
|
|
2384
|
+
* MigrationManager - Manages gradual partition rebalancing
|
|
2385
|
+
*
|
|
2386
|
+
* Phase 4 Task 03: Parallel Partition Sync
|
|
2387
|
+
*
|
|
2388
|
+
* Features:
|
|
2389
|
+
* - Gradual rebalancing with configurable batch size
|
|
2390
|
+
* - State machine for migration lifecycle
|
|
2391
|
+
* - Backpressure via chunk acknowledgments
|
|
2392
|
+
* - Retry logic for failed migrations
|
|
2393
|
+
* - Metrics and observability
|
|
2394
|
+
*/
|
|
2395
|
+
|
|
2396
|
+
declare class MigrationManager extends EventEmitter {
|
|
2397
|
+
private readonly config;
|
|
2398
|
+
private readonly clusterManager;
|
|
2399
|
+
private readonly partitionService;
|
|
2400
|
+
private activeMigrations;
|
|
2401
|
+
private migrationQueue;
|
|
2402
|
+
private incomingMigrations;
|
|
2403
|
+
private pendingChunkAcks;
|
|
2404
|
+
private pendingVerifications;
|
|
2405
|
+
private metrics;
|
|
2406
|
+
private batchTimer;
|
|
2407
|
+
private dataCollector;
|
|
2408
|
+
private dataStorer;
|
|
2409
|
+
constructor(clusterManager: ClusterManager, partitionService: PartitionService, config?: Partial<MigrationConfig>);
|
|
2410
|
+
/**
|
|
2411
|
+
* Set the data collector callback
|
|
2412
|
+
* Called to collect all records for a partition before migration
|
|
2413
|
+
*/
|
|
2414
|
+
setDataCollector(collector: (partitionId: number) => Promise<Uint8Array[]>): void;
|
|
2415
|
+
/**
|
|
2416
|
+
* Set the data storer callback
|
|
2417
|
+
* Called to store received records after successful migration
|
|
2418
|
+
*/
|
|
2419
|
+
setDataStorer(storer: (partitionId: number, data: Uint8Array[]) => Promise<void>): void;
|
|
2420
|
+
/**
|
|
2421
|
+
* Plan migration for topology change
|
|
2422
|
+
*/
|
|
2423
|
+
planMigration(oldDistribution: Map<number, PartitionDistribution>, newDistribution: Map<number, PartitionDistribution>): void;
|
|
2424
|
+
/**
|
|
2425
|
+
* Start batch processing timer
|
|
2426
|
+
*/
|
|
2427
|
+
private startBatchProcessing;
|
|
2428
|
+
/**
|
|
2429
|
+
* Stop batch processing
|
|
2430
|
+
*/
|
|
2431
|
+
private stopBatchProcessing;
|
|
2432
|
+
/**
|
|
2433
|
+
* Start next batch of migrations
|
|
2434
|
+
*/
|
|
2435
|
+
startNextBatch(): Promise<void>;
|
|
2436
|
+
/**
|
|
2437
|
+
* Start migration for a single partition
|
|
2438
|
+
*/
|
|
2439
|
+
private startPartitionMigration;
|
|
2440
|
+
/**
|
|
2441
|
+
* Split records into chunks
|
|
2442
|
+
*/
|
|
2443
|
+
private chunkify;
|
|
2444
|
+
/**
|
|
2445
|
+
* Calculate checksum for a chunk using native xxhash
|
|
2446
|
+
*/
|
|
2447
|
+
private calculateChecksum;
|
|
2448
|
+
/**
|
|
2449
|
+
* Calculate checksum for all partition records using streaming xxhash
|
|
2450
|
+
*/
|
|
2451
|
+
private calculatePartitionChecksum;
|
|
2452
|
+
/**
|
|
2453
|
+
* Wait for chunk acknowledgment
|
|
2454
|
+
*/
|
|
2455
|
+
private waitForChunkAck;
|
|
2456
|
+
/**
|
|
2457
|
+
* Wait for migration verification
|
|
2458
|
+
*/
|
|
2459
|
+
private waitForVerification;
|
|
2460
|
+
/**
|
|
2461
|
+
* Handle successful migration completion
|
|
2462
|
+
*/
|
|
2463
|
+
private onMigrationComplete;
|
|
2464
|
+
/**
|
|
2465
|
+
* Handle migration failure
|
|
2466
|
+
*/
|
|
2467
|
+
private onMigrationFailed;
|
|
2468
|
+
/**
|
|
2469
|
+
* Handle MIGRATION_START message
|
|
2470
|
+
*/
|
|
2471
|
+
private handleMigrationStart;
|
|
2472
|
+
/**
|
|
2473
|
+
* Handle MIGRATION_CHUNK message
|
|
2474
|
+
*/
|
|
2475
|
+
private handleMigrationChunk;
|
|
2476
|
+
/**
|
|
2477
|
+
* Handle MIGRATION_COMPLETE message
|
|
2478
|
+
*/
|
|
2479
|
+
private handleMigrationComplete;
|
|
2480
|
+
/**
|
|
2481
|
+
* Handle MIGRATION_CHUNK_ACK message
|
|
2482
|
+
*/
|
|
2483
|
+
private handleMigrationChunkAck;
|
|
2484
|
+
/**
|
|
2485
|
+
* Handle MIGRATION_VERIFY message
|
|
2486
|
+
*/
|
|
2487
|
+
private handleMigrationVerify;
|
|
2488
|
+
/**
|
|
2489
|
+
* Reassemble chunks into continuous data
|
|
2490
|
+
*/
|
|
2491
|
+
private reassemble;
|
|
2492
|
+
/**
|
|
2493
|
+
* Deserialize records from chunk data
|
|
2494
|
+
*/
|
|
2495
|
+
private deserializeRecords;
|
|
2496
|
+
/**
|
|
2497
|
+
* Setup cluster message handlers
|
|
2498
|
+
*/
|
|
2499
|
+
private setupMessageHandlers;
|
|
2500
|
+
/**
|
|
2501
|
+
* Check if a partition is currently migrating
|
|
2502
|
+
*/
|
|
2503
|
+
isActive(partitionId: number): boolean;
|
|
2504
|
+
/**
|
|
2505
|
+
* Get migration status
|
|
2506
|
+
*/
|
|
2507
|
+
getStatus(): MigrationStatus;
|
|
2508
|
+
/**
|
|
2509
|
+
* Get migration metrics
|
|
2510
|
+
*/
|
|
2511
|
+
getMetrics(): MigrationMetrics;
|
|
2512
|
+
/**
|
|
2513
|
+
* Cancel all active and queued migrations
|
|
2514
|
+
*/
|
|
2515
|
+
cancelAll(): Promise<void>;
|
|
2516
|
+
/**
|
|
2517
|
+
* Cleanup resources (sync version for backwards compatibility)
|
|
2518
|
+
*/
|
|
2519
|
+
close(): void;
|
|
2520
|
+
/**
|
|
2521
|
+
* Async cleanup - waits for cancellation to complete
|
|
2522
|
+
*/
|
|
2523
|
+
closeAsync(): Promise<void>;
|
|
2524
|
+
}
|
|
2525
|
+
|
|
2526
|
+
interface PartitionDistribution {
|
|
2527
|
+
owner: string;
|
|
2528
|
+
backups: string[];
|
|
2529
|
+
}
|
|
2530
|
+
interface PartitionServiceEvents {
|
|
2531
|
+
'rebalanced': (map: PartitionMap, changes: PartitionChange[]) => void;
|
|
2532
|
+
'partitionMoved': (info: {
|
|
2533
|
+
partitionId: number;
|
|
2534
|
+
previousOwner: string;
|
|
2535
|
+
newOwner: string;
|
|
2536
|
+
version: number;
|
|
2537
|
+
}) => void;
|
|
2538
|
+
}
|
|
2539
|
+
interface PartitionServiceConfig {
|
|
2540
|
+
/** Enable gradual rebalancing (default: false for backward compatibility) */
|
|
2541
|
+
gradualRebalancing: boolean;
|
|
2542
|
+
/** Migration configuration */
|
|
2543
|
+
migration: Partial<MigrationConfig>;
|
|
2544
|
+
}
|
|
2545
|
+
declare class PartitionService extends EventEmitter {
|
|
2546
|
+
private cluster;
|
|
2547
|
+
private partitions;
|
|
2548
|
+
private readonly PARTITION_COUNT;
|
|
2549
|
+
private readonly BACKUP_COUNT;
|
|
2550
|
+
private mapVersion;
|
|
2551
|
+
private lastRebalanceTime;
|
|
2552
|
+
private config;
|
|
2553
|
+
private migrationManager;
|
|
2554
|
+
constructor(cluster: ClusterManager, config?: Partial<PartitionServiceConfig>);
|
|
2555
|
+
/**
|
|
2556
|
+
* Handle membership change
|
|
2557
|
+
*/
|
|
2558
|
+
private onMembershipChange;
|
|
2559
|
+
getPartitionId(key: string): number;
|
|
2560
|
+
getDistribution(key: string): PartitionDistribution;
|
|
2561
|
+
getOwner(key: string): string;
|
|
2562
|
+
isLocalOwner(key: string): boolean;
|
|
2563
|
+
isLocalBackup(key: string): boolean;
|
|
2564
|
+
isRelated(key: string): boolean;
|
|
2565
|
+
/**
|
|
2566
|
+
* Get current partition map version
|
|
2567
|
+
*/
|
|
2568
|
+
getMapVersion(): number;
|
|
2569
|
+
/**
|
|
2570
|
+
* Generate full PartitionMap for client consumption
|
|
2571
|
+
*/
|
|
2572
|
+
getPartitionMap(): PartitionMap;
|
|
2573
|
+
/**
|
|
2574
|
+
* Get partition info by ID
|
|
2575
|
+
*/
|
|
2576
|
+
getPartitionInfo(partitionId: number): PartitionInfo | null;
|
|
2577
|
+
/**
|
|
2578
|
+
* Get owner node for a partition ID
|
|
2579
|
+
*/
|
|
2580
|
+
getPartitionOwner(partitionId: number): string | null;
|
|
2581
|
+
private rebalance;
|
|
2582
|
+
/**
|
|
2583
|
+
* Perform gradual rebalancing using MigrationManager
|
|
2584
|
+
*/
|
|
2585
|
+
private rebalanceGradual;
|
|
2586
|
+
/**
|
|
2587
|
+
* Set partition owner (called after migration completes)
|
|
2588
|
+
*/
|
|
2589
|
+
setOwner(partitionId: number, nodeId: string): void;
|
|
2590
|
+
/**
|
|
2591
|
+
* Get backups for a partition
|
|
2592
|
+
*/
|
|
2593
|
+
getBackups(partitionId: number): string[];
|
|
2594
|
+
/**
|
|
2595
|
+
* Get migration status
|
|
2596
|
+
*/
|
|
2597
|
+
getMigrationStatus(): MigrationStatus | null;
|
|
2598
|
+
/**
|
|
2599
|
+
* Check if partition is currently migrating
|
|
2600
|
+
*/
|
|
2601
|
+
isMigrating(partitionId: number): boolean;
|
|
2602
|
+
/**
|
|
2603
|
+
* Check if any partition is currently migrating
|
|
2604
|
+
*/
|
|
2605
|
+
isRebalancing(): boolean;
|
|
2606
|
+
/**
|
|
2607
|
+
* Get MigrationManager for configuration
|
|
2608
|
+
*/
|
|
2609
|
+
getMigrationManager(): MigrationManager | null;
|
|
2610
|
+
/**
|
|
2611
|
+
* Cancel all migrations
|
|
2612
|
+
*/
|
|
2613
|
+
cancelMigrations(): Promise<void>;
|
|
2614
|
+
}
|
|
2615
|
+
|
|
2616
|
+
/**
|
|
2617
|
+
* LagTracker - Monitors replication lag across cluster nodes
|
|
2618
|
+
*
|
|
2619
|
+
* Phase 4 Task 04: Async Replication Pipeline
|
|
2620
|
+
*
|
|
2621
|
+
* Features:
|
|
2622
|
+
* - Tracks replication lag per node
|
|
2623
|
+
* - Maintains historical lag data for percentile calculations
|
|
2624
|
+
* - Identifies unhealthy and laggy nodes
|
|
2625
|
+
* - Provides health metrics for monitoring
|
|
2626
|
+
*/
|
|
2627
|
+
|
|
2628
|
+
interface LagInfo {
|
|
2629
|
+
current: number;
|
|
2630
|
+
history: number[];
|
|
2631
|
+
lastUpdate: number;
|
|
2632
|
+
pendingOps: number;
|
|
2633
|
+
}
|
|
2634
|
+
interface LagTrackerConfig {
|
|
2635
|
+
/** Number of lag samples to keep in history (default: 100) */
|
|
2636
|
+
historySize: number;
|
|
2637
|
+
/** Threshold in ms for considering a node laggy (default: 5000) */
|
|
2638
|
+
laggyThresholdMs: number;
|
|
2639
|
+
/** Threshold in ms for considering a node unhealthy (default: 30000) */
|
|
2640
|
+
unhealthyThresholdMs: number;
|
|
2641
|
+
}
|
|
2642
|
+
declare const DEFAULT_LAG_TRACKER_CONFIG: LagTrackerConfig;
|
|
2643
|
+
declare class LagTracker {
|
|
2644
|
+
private readonly config;
|
|
2645
|
+
private lagByNode;
|
|
2646
|
+
constructor(config?: Partial<LagTrackerConfig>);
|
|
2647
|
+
/**
|
|
2648
|
+
* Update lag measurement for a node
|
|
2649
|
+
*/
|
|
2650
|
+
update(nodeId: string, lagMs: number): void;
|
|
2651
|
+
/**
|
|
2652
|
+
* Record acknowledgment from a node (lag effectively becomes 0)
|
|
2653
|
+
*/
|
|
2654
|
+
recordAck(nodeId: string): void;
|
|
2655
|
+
/**
|
|
2656
|
+
* Increment pending operations counter for a node
|
|
2657
|
+
*/
|
|
2658
|
+
incrementPending(nodeId: string): void;
|
|
2659
|
+
/**
|
|
2660
|
+
* Get lag statistics for a specific node
|
|
2661
|
+
*/
|
|
2662
|
+
getLag(nodeId: string): ReplicationLag;
|
|
2663
|
+
/**
|
|
2664
|
+
* Get pending operations count for a node
|
|
2665
|
+
*/
|
|
2666
|
+
getPendingOps(nodeId: string): number;
|
|
2667
|
+
/**
|
|
2668
|
+
* Get overall replication health status
|
|
2669
|
+
*/
|
|
2670
|
+
getHealth(): ReplicationHealth;
|
|
2671
|
+
/**
|
|
2672
|
+
* Get average lag across all tracked nodes
|
|
2673
|
+
*/
|
|
2674
|
+
getAverageLag(): number;
|
|
2675
|
+
/**
|
|
2676
|
+
* Check if a specific node is considered healthy
|
|
2677
|
+
*/
|
|
2678
|
+
isNodeHealthy(nodeId: string): boolean;
|
|
2679
|
+
/**
|
|
2680
|
+
* Check if a specific node is considered laggy
|
|
2681
|
+
*/
|
|
2682
|
+
isNodeLaggy(nodeId: string): boolean;
|
|
2683
|
+
/**
|
|
2684
|
+
* Remove a node from tracking
|
|
2685
|
+
*/
|
|
2686
|
+
removeNode(nodeId: string): void;
|
|
2687
|
+
/**
|
|
2688
|
+
* Get all tracked node IDs
|
|
2689
|
+
*/
|
|
2690
|
+
getTrackedNodes(): string[];
|
|
2691
|
+
/**
|
|
2692
|
+
* Get raw lag info for a node (for advanced monitoring)
|
|
2693
|
+
*/
|
|
2694
|
+
getRawLagInfo(nodeId: string): LagInfo | undefined;
|
|
2695
|
+
/**
|
|
2696
|
+
* Clear all tracking data
|
|
2697
|
+
*/
|
|
2698
|
+
clear(): void;
|
|
2699
|
+
/**
|
|
2700
|
+
* Export metrics in Prometheus format
|
|
2701
|
+
*/
|
|
2702
|
+
toPrometheusMetrics(): string;
|
|
2703
|
+
}
|
|
2704
|
+
|
|
2705
|
+
/**
|
|
2706
|
+
* ReplicationPipeline - Manages async replication with configurable consistency levels
|
|
2707
|
+
*
|
|
2708
|
+
* Phase 4 Task 04: Async Replication Pipeline
|
|
2709
|
+
*
|
|
2710
|
+
* Features:
|
|
2711
|
+
* - Three consistency levels: STRONG, QUORUM, EVENTUAL
|
|
2712
|
+
* - Async replication queue for high throughput
|
|
2713
|
+
* - Backpressure handling with queue limits
|
|
2714
|
+
* - Retry logic for failed replications
|
|
2715
|
+
* - Integration with LagTracker for monitoring
|
|
2716
|
+
* - Pluggable operation applier for storage integration
|
|
2717
|
+
*/
|
|
2718
|
+
|
|
2719
|
+
/**
|
|
2720
|
+
* Callback to apply replicated operation to local storage
|
|
2721
|
+
* @param operation - The operation to apply
|
|
2722
|
+
* @param opId - Unique operation ID
|
|
2723
|
+
* @param sourceNode - Node that originated the operation
|
|
2724
|
+
* @returns Promise<boolean> - true if applied successfully
|
|
2725
|
+
*/
|
|
2726
|
+
type OperationApplier = (operation: unknown, opId: string, sourceNode: string) => Promise<boolean>;
|
|
2727
|
+
declare class ReplicationPipeline extends EventEmitter {
|
|
2728
|
+
private readonly config;
|
|
2729
|
+
private readonly clusterManager;
|
|
2730
|
+
private readonly partitionService;
|
|
2731
|
+
private readonly lagTracker;
|
|
2732
|
+
private readonly nodeId;
|
|
2733
|
+
private replicationQueue;
|
|
2734
|
+
private pendingAcks;
|
|
2735
|
+
private queueProcessorTimer;
|
|
2736
|
+
private operationApplier;
|
|
2737
|
+
constructor(clusterManager: ClusterManager, partitionService: PartitionService, config?: Partial<ReplicationConfig>);
|
|
2738
|
+
/**
|
|
2739
|
+
* Set the operation applier callback
|
|
2740
|
+
* This is called when replicated operations are received from other nodes
|
|
2741
|
+
*/
|
|
2742
|
+
setOperationApplier(applier: OperationApplier): void;
|
|
2743
|
+
/**
|
|
2744
|
+
* Replicate operation to backup nodes
|
|
2745
|
+
*/
|
|
2746
|
+
replicate(operation: unknown, opId: string, key: string, options?: {
|
|
2747
|
+
consistency?: ConsistencyLevel;
|
|
2748
|
+
timeout?: number;
|
|
2749
|
+
}): Promise<ReplicationResult>;
|
|
2750
|
+
/**
|
|
2751
|
+
* STRONG: Wait for all replicas to acknowledge
|
|
2752
|
+
*/
|
|
2753
|
+
private replicateStrong;
|
|
2754
|
+
/**
|
|
2755
|
+
* QUORUM: Wait for majority of replicas
|
|
2756
|
+
*/
|
|
2757
|
+
private replicateQuorum;
|
|
2758
|
+
/**
|
|
2759
|
+
* EVENTUAL: Fire-and-forget with queue
|
|
2760
|
+
*/
|
|
2761
|
+
private replicateEventual;
|
|
2762
|
+
/**
|
|
2763
|
+
* Add task to replication queue
|
|
2764
|
+
*/
|
|
2765
|
+
private enqueue;
|
|
2766
|
+
/**
|
|
2767
|
+
* Start queue processor
|
|
2768
|
+
*/
|
|
2769
|
+
private startQueueProcessor;
|
|
2770
|
+
/**
|
|
2771
|
+
* Stop queue processor
|
|
2772
|
+
*/
|
|
2773
|
+
private stopQueueProcessor;
|
|
2774
|
+
/**
|
|
2775
|
+
* Process replication queue for a node
|
|
2776
|
+
*/
|
|
2777
|
+
private processQueue;
|
|
2778
|
+
/**
|
|
2779
|
+
* Send replication message to a node
|
|
2780
|
+
*/
|
|
2781
|
+
private sendReplication;
|
|
2782
|
+
/**
|
|
2783
|
+
* Setup cluster message handlers
|
|
2784
|
+
*/
|
|
2785
|
+
private setupMessageHandlers;
|
|
2786
|
+
/**
|
|
2787
|
+
* Handle incoming replication request (on backup node)
|
|
2788
|
+
*/
|
|
2789
|
+
private handleReplication;
|
|
2790
|
+
/**
|
|
2791
|
+
* Handle incoming batch replication (on backup node)
|
|
2792
|
+
*/
|
|
2793
|
+
private handleReplicationBatch;
|
|
2794
|
+
/**
|
|
2795
|
+
* Handle replication acknowledgment (on owner node)
|
|
2796
|
+
*/
|
|
2797
|
+
private handleReplicationAck;
|
|
2798
|
+
/**
|
|
2799
|
+
* Handle batch acknowledgment (on owner node)
|
|
2800
|
+
*/
|
|
2801
|
+
private handleReplicationBatchAck;
|
|
2802
|
+
/**
|
|
2803
|
+
* Get replication lag for a specific node
|
|
2804
|
+
*/
|
|
2805
|
+
getLag(nodeId: string): ReplicationLag;
|
|
2806
|
+
/**
|
|
2807
|
+
* Get overall replication health
|
|
2808
|
+
*/
|
|
2809
|
+
getHealth(): ReplicationHealth;
|
|
2810
|
+
/**
|
|
2811
|
+
* Get queue size for a specific node
|
|
2812
|
+
*/
|
|
2813
|
+
getQueueSize(nodeId: string): number;
|
|
2814
|
+
/**
|
|
2815
|
+
* Get total pending operations across all nodes
|
|
2816
|
+
*/
|
|
2817
|
+
getTotalPending(): number;
|
|
2818
|
+
/**
|
|
2819
|
+
* Check if a node is considered synced (low lag)
|
|
2820
|
+
*/
|
|
2821
|
+
isSynced(nodeId: string, maxLagMs?: number): boolean;
|
|
2822
|
+
/**
|
|
2823
|
+
* Get LagTracker for advanced monitoring
|
|
2824
|
+
*/
|
|
2825
|
+
getLagTracker(): LagTracker;
|
|
2826
|
+
/**
|
|
2827
|
+
* Export metrics in Prometheus format
|
|
2828
|
+
*/
|
|
2829
|
+
toPrometheusMetrics(): string;
|
|
2830
|
+
/**
|
|
2831
|
+
* Cleanup resources
|
|
2832
|
+
*/
|
|
2833
|
+
close(): void;
|
|
2834
|
+
}
|
|
2835
|
+
|
|
2836
|
+
declare class LockManager extends EventEmitter {
|
|
2837
|
+
private locks;
|
|
2838
|
+
private checkInterval;
|
|
2839
|
+
private static readonly MIN_TTL;
|
|
2840
|
+
private static readonly MAX_TTL;
|
|
2841
|
+
constructor();
|
|
2842
|
+
stop(): void;
|
|
2843
|
+
acquire(name: string, clientId: string, requestId: string, ttl: number): {
|
|
2844
|
+
granted: boolean;
|
|
2845
|
+
fencingToken?: number;
|
|
2846
|
+
error?: string;
|
|
2847
|
+
};
|
|
2848
|
+
release(name: string, clientId: string, fencingToken: number): boolean;
|
|
2849
|
+
handleClientDisconnect(clientId: string): void;
|
|
2850
|
+
private grantLock;
|
|
2851
|
+
private processNext;
|
|
2852
|
+
private cleanupExpiredLocks;
|
|
2853
|
+
}
|
|
2854
|
+
|
|
2855
|
+
/**
|
|
2856
|
+
* ClusterCoordinator - Unified cluster integration layer
|
|
2857
|
+
*
|
|
2858
|
+
* Phase 4 Task 06: System Integration
|
|
2859
|
+
*
|
|
2860
|
+
* Coordinates all cluster components:
|
|
2861
|
+
* - ClusterManager: P2P WebSocket mesh
|
|
2862
|
+
* - PartitionService: Consistent hashing & routing
|
|
2863
|
+
* - MigrationManager: Gradual rebalancing
|
|
2864
|
+
* - ReplicationPipeline: Async replication with consistency levels
|
|
2865
|
+
* - LagTracker: Replication health monitoring
|
|
2866
|
+
*/
|
|
2867
|
+
|
|
2868
|
+
interface ClusterCoordinatorConfig {
|
|
2869
|
+
/** Cluster node configuration */
|
|
2870
|
+
cluster: ClusterConfig;
|
|
2871
|
+
/** Enable gradual partition rebalancing (default: true) */
|
|
2872
|
+
gradualRebalancing: boolean;
|
|
2873
|
+
/** Migration configuration for gradual rebalancing */
|
|
2874
|
+
migration: Partial<MigrationConfig>;
|
|
2875
|
+
/** Replication configuration */
|
|
2876
|
+
replication: Partial<ReplicationConfig>;
|
|
2877
|
+
/** Enable async replication pipeline (default: true) */
|
|
2878
|
+
replicationEnabled: boolean;
|
|
2879
|
+
/** Data collector callback for migrations */
|
|
2880
|
+
dataCollector?: (partitionId: number) => Promise<Uint8Array[]>;
|
|
2881
|
+
/** Data storer callback for incoming migrations */
|
|
2882
|
+
dataStorer?: (partitionId: number, data: Uint8Array[]) => Promise<void>;
|
|
2883
|
+
}
|
|
2884
|
+
declare const DEFAULT_CLUSTER_COORDINATOR_CONFIG: Omit<ClusterCoordinatorConfig, 'cluster'>;
|
|
2885
|
+
interface ClusterCoordinatorEvents {
|
|
2886
|
+
'started': () => void;
|
|
2887
|
+
'stopped': () => void;
|
|
2888
|
+
'member:joined': (nodeId: string) => void;
|
|
2889
|
+
'member:left': (nodeId: string) => void;
|
|
2890
|
+
'partition:rebalanced': (map: PartitionMap, changes: PartitionChange[]) => void;
|
|
2891
|
+
'partition:moved': (info: {
|
|
2892
|
+
partitionId: number;
|
|
2893
|
+
previousOwner: string;
|
|
2894
|
+
newOwner: string;
|
|
2895
|
+
version: number;
|
|
2896
|
+
}) => void;
|
|
2897
|
+
'migration:started': (partitionId: number, targetNode: string) => void;
|
|
2898
|
+
'migration:completed': (partitionId: number) => void;
|
|
2899
|
+
'migration:failed': (partitionId: number, error: Error) => void;
|
|
2900
|
+
'replication:unhealthy': (nodeId: string) => void;
|
|
2901
|
+
'replication:healthy': (nodeId: string) => void;
|
|
2902
|
+
'error': (error: Error) => void;
|
|
2903
|
+
}
|
|
2904
|
+
declare class ClusterCoordinator extends EventEmitter {
|
|
2905
|
+
private readonly config;
|
|
2906
|
+
private clusterManager;
|
|
2907
|
+
private partitionService;
|
|
2908
|
+
private replicationPipeline;
|
|
2909
|
+
private lagTracker;
|
|
2910
|
+
private started;
|
|
2911
|
+
private actualPort;
|
|
2912
|
+
constructor(config: ClusterCoordinatorConfig);
|
|
2913
|
+
/**
|
|
2914
|
+
* Start the cluster coordinator
|
|
2915
|
+
*/
|
|
2916
|
+
start(): Promise<number>;
|
|
2917
|
+
/**
|
|
2918
|
+
* Stop the cluster coordinator
|
|
2919
|
+
*/
|
|
2920
|
+
stop(): Promise<void>;
|
|
2921
|
+
/**
|
|
2922
|
+
* Get local node ID
|
|
2923
|
+
*/
|
|
2924
|
+
getNodeId(): string;
|
|
2925
|
+
/**
|
|
2926
|
+
* Get cluster port
|
|
2927
|
+
*/
|
|
2928
|
+
getPort(): number;
|
|
2929
|
+
/**
|
|
2930
|
+
* Get all cluster members
|
|
2931
|
+
*/
|
|
2932
|
+
getMembers(): string[];
|
|
2933
|
+
/**
|
|
2934
|
+
* Check if this is the local node
|
|
2935
|
+
*/
|
|
2936
|
+
isLocal(nodeId: string): boolean;
|
|
2937
|
+
/**
|
|
2938
|
+
* Check if coordinator is started
|
|
2939
|
+
*/
|
|
2940
|
+
isStarted(): boolean;
|
|
2941
|
+
/**
|
|
2942
|
+
* Get current partition map
|
|
2943
|
+
*/
|
|
2944
|
+
getPartitionMap(): PartitionMap;
|
|
2945
|
+
/**
|
|
2946
|
+
* Get partition map version
|
|
2947
|
+
*/
|
|
2948
|
+
getPartitionMapVersion(): number;
|
|
2949
|
+
/**
|
|
2950
|
+
* Get partition ID for a key
|
|
2951
|
+
*/
|
|
2952
|
+
getPartitionId(key: string): number;
|
|
2953
|
+
/**
|
|
2954
|
+
* Get owner node for a key
|
|
2955
|
+
*/
|
|
2956
|
+
getOwner(key: string): string;
|
|
2957
|
+
/**
|
|
2958
|
+
* Check if this node owns the key
|
|
2959
|
+
*/
|
|
2960
|
+
isLocalOwner(key: string): boolean;
|
|
2961
|
+
/**
|
|
2962
|
+
* Check if this node is a backup for the key
|
|
2963
|
+
*/
|
|
2964
|
+
isLocalBackup(key: string): boolean;
|
|
2965
|
+
/**
|
|
2966
|
+
* Get backup nodes for a partition
|
|
2967
|
+
*/
|
|
2968
|
+
getBackups(partitionId: number): string[];
|
|
2969
|
+
/**
|
|
2970
|
+
* Check if partition is currently migrating
|
|
2971
|
+
*/
|
|
2972
|
+
isMigrating(partitionId: number): boolean;
|
|
2973
|
+
/**
|
|
2974
|
+
* Check if any rebalancing is in progress
|
|
2975
|
+
*/
|
|
2976
|
+
isRebalancing(): boolean;
|
|
2977
|
+
/**
|
|
2978
|
+
* Get migration status
|
|
2979
|
+
*/
|
|
2980
|
+
getMigrationStatus(): MigrationStatus | null;
|
|
2981
|
+
/**
|
|
2982
|
+
* Get migration metrics
|
|
2983
|
+
*/
|
|
2984
|
+
getMigrationMetrics(): MigrationMetrics | null;
|
|
2985
|
+
/**
|
|
2986
|
+
* Cancel all active migrations
|
|
2987
|
+
*/
|
|
2988
|
+
cancelMigrations(): Promise<void>;
|
|
2989
|
+
/**
|
|
2990
|
+
* Set data collector for migrations
|
|
2991
|
+
*/
|
|
2992
|
+
setDataCollector(collector: (partitionId: number) => Promise<Uint8Array[]>): void;
|
|
2993
|
+
/**
|
|
2994
|
+
* Set data storer for incoming migrations
|
|
2995
|
+
*/
|
|
2996
|
+
setDataStorer(storer: (partitionId: number, data: Uint8Array[]) => Promise<void>): void;
|
|
2997
|
+
/**
|
|
2998
|
+
* Replicate an operation to backup nodes
|
|
2999
|
+
*/
|
|
3000
|
+
replicate(operation: unknown, opId: string, key: string, options?: {
|
|
3001
|
+
consistency?: ConsistencyLevel;
|
|
3002
|
+
timeout?: number;
|
|
3003
|
+
}): Promise<ReplicationResult>;
|
|
3004
|
+
/**
|
|
3005
|
+
* Get replication health status
|
|
3006
|
+
*/
|
|
3007
|
+
getReplicationHealth(): ReplicationHealth;
|
|
3008
|
+
/**
|
|
3009
|
+
* Get replication lag for a specific node
|
|
3010
|
+
*/
|
|
3011
|
+
getReplicationLag(nodeId: string): ReplicationLag;
|
|
3012
|
+
/**
|
|
3013
|
+
* Check if a node is healthy for replication
|
|
3014
|
+
*/
|
|
3015
|
+
isNodeHealthy(nodeId: string): boolean;
|
|
3016
|
+
/**
|
|
3017
|
+
* Check if a node is laggy
|
|
3018
|
+
*/
|
|
3019
|
+
isNodeLaggy(nodeId: string): boolean;
|
|
3020
|
+
/**
|
|
3021
|
+
* Send message to a specific node
|
|
3022
|
+
*/
|
|
3023
|
+
send(nodeId: string, message: unknown): void;
|
|
3024
|
+
/**
|
|
3025
|
+
* Broadcast message to all nodes
|
|
3026
|
+
*/
|
|
3027
|
+
broadcast(message: unknown): void;
|
|
3028
|
+
/**
|
|
3029
|
+
* Get underlying ClusterManager
|
|
3030
|
+
*/
|
|
3031
|
+
getClusterManager(): ClusterManager;
|
|
3032
|
+
/**
|
|
3033
|
+
* Get underlying PartitionService
|
|
3034
|
+
*/
|
|
3035
|
+
getPartitionService(): PartitionService;
|
|
3036
|
+
/**
|
|
3037
|
+
* Get underlying ReplicationPipeline
|
|
3038
|
+
*/
|
|
3039
|
+
getReplicationPipeline(): ReplicationPipeline | null;
|
|
3040
|
+
/**
|
|
3041
|
+
* Get underlying LagTracker
|
|
3042
|
+
*/
|
|
3043
|
+
getLagTracker(): LagTracker;
|
|
3044
|
+
/**
|
|
3045
|
+
* Get all metrics in Prometheus format
|
|
3046
|
+
*/
|
|
3047
|
+
getPrometheusMetrics(): string;
|
|
3048
|
+
private setupEventHandlers;
|
|
331
3049
|
}
|
|
332
3050
|
|
|
333
|
-
export { type ConnectionContext, type IInterceptor, type IServerStorage, type Logger, MemoryServerAdapter, type ORMapTombstones, type ORMapValue, type OpContext, PostgresAdapter, type PostgresAdapterOptions, RateLimitInterceptor, SecurityManager, ServerCoordinator, type ServerCoordinatorConfig, type ServerOp, type StorageValue, TimestampInterceptor, logger };
|
|
3051
|
+
export { BufferPool, type BufferPoolConfig, type BufferPoolStats, type ClusterConfig, ClusterCoordinator, type ClusterCoordinatorConfig, type ClusterCoordinatorEvents, ClusterManager, type ClusterMember, type ClusterMessage, type CoalescingPreset, type CoalescingWriterMetrics, type CoalescingWriterOptions, type ConnectionContext, ConnectionRateLimiter, DEFAULT_CLUSTER_COORDINATOR_CONFIG, DEFAULT_LAG_TRACKER_CONFIG, FilterTasklet, ForEachTasklet, type IInterceptor, type IServerStorage, IteratorTasklet, type IteratorTaskletConfig, type LagInfo, LagTracker, type LagTrackerConfig, LockManager, type Logger, MapTasklet, MemoryServerAdapter, MigrationManager, type NativeModuleStatus, type NativeStats, type ORMapTombstones, type ORMapValue, ObjectPool, type ObjectPoolConfig, type ObjectPoolStats, type OpContext, type PartitionDistribution, PartitionService, type PartitionServiceConfig, type PartitionServiceEvents, type PooledEventPayload, type PooledMessage, type PooledRecord, type PooledTimestamp, PostgresAdapter, type PostgresAdapterOptions, type ProgressState, RateLimitInterceptor, type RateLimiterConfig, type RateLimiterStats, ReduceTasklet, ReplicationPipeline, SecurityManager, ServerCoordinator, type ServerCoordinatorConfig, type ServerOp, type StorageValue, type Tasklet, TaskletScheduler, type TaskletSchedulerConfig, type TaskletSchedulerStats, TimestampInterceptor, coalescingPresets, createEventPayloadPool, createMessagePool, createRecordPool, createTimestampPool, getCoalescingPreset, getGlobalBufferPool, getGlobalEventPayloadPool, getGlobalMessagePool, getGlobalRecordPool, getGlobalTimestampPool, getNativeModuleStatus, getNativeStats, logNativeStatus, logger, setGlobalBufferPool, setGlobalEventPayloadPool, setGlobalMessagePool, setGlobalRecordPool, setGlobalTimestampPool };
|