@kronos-ts/axon-server 0.5.0 → 0.7.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/axon-server-event-store.d.ts.map +1 -1
- package/dist/axon-server-event-store.js +72 -77
- package/dist/axon-server-event-store.js.map +1 -1
- package/dist/axon-server-snapshotting-event-store.d.ts +3 -3
- package/dist/axon-server-snapshotting-event-store.d.ts.map +1 -1
- package/dist/axon-server-snapshotting-event-store.js +3 -2
- package/dist/axon-server-snapshotting-event-store.js.map +1 -1
- package/dist/axon-server.d.ts +15 -7
- package/dist/axon-server.d.ts.map +1 -1
- package/dist/axon-server.js +447 -275
- package/dist/axon-server.js.map +1 -1
- package/dist/bounded-read.d.ts +19 -0
- package/dist/bounded-read.d.ts.map +1 -0
- package/dist/bounded-read.js +39 -0
- package/dist/bounded-read.js.map +1 -0
- package/dist/connection.d.ts +13 -0
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +78 -40
- package/dist/connection.js.map +1 -1
- package/dist/control-plane.d.ts +9 -19
- package/dist/control-plane.d.ts.map +1 -1
- package/dist/control-plane.js +6 -37
- package/dist/control-plane.js.map +1 -1
- package/dist/event-processor-info.d.ts +5 -23
- package/dist/event-processor-info.d.ts.map +1 -1
- package/dist/event-processor-info.js +16 -20
- package/dist/event-processor-info.js.map +1 -1
- package/dist/flow-controlled-sender.d.ts.map +1 -1
- package/dist/flow-controlled-sender.js +37 -17
- package/dist/flow-controlled-sender.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/outbound-stream.d.ts +5 -9
- package/dist/outbound-stream.d.ts.map +1 -1
- package/dist/outbound-stream.js +65 -16
- package/dist/outbound-stream.js.map +1 -1
- package/dist/platform-service.d.ts +10 -1
- package/dist/platform-service.d.ts.map +1 -1
- package/dist/platform-service.js +70 -10
- package/dist/platform-service.js.map +1 -1
- package/dist/resilience.d.ts +2 -0
- package/dist/resilience.d.ts.map +1 -1
- package/dist/resilience.js +2 -1
- package/dist/resilience.js.map +1 -1
- package/dist/shutdown-latch.d.ts +1 -0
- package/dist/shutdown-latch.d.ts.map +1 -1
- package/dist/shutdown-latch.js +20 -1
- package/dist/shutdown-latch.js.map +1 -1
- package/dist/stream-recovery.d.ts +9 -0
- package/dist/stream-recovery.d.ts.map +1 -0
- package/dist/stream-recovery.js +70 -0
- package/dist/stream-recovery.js.map +1 -0
- package/package.json +2 -2
- package/src/axon-server-event-store.ts +77 -79
- package/src/axon-server-snapshotting-event-store.ts +10 -7
- package/src/axon-server.ts +417 -316
- package/src/bounded-read.ts +43 -0
- package/src/connection.ts +77 -45
- package/src/control-plane.ts +19 -63
- package/src/event-processor-info.ts +22 -43
- package/src/flow-controlled-sender.ts +33 -14
- package/src/index.ts +0 -1
- package/src/outbound-stream.ts +57 -27
- package/src/platform-service.ts +75 -11
- package/src/resilience.ts +6 -2
- package/src/shutdown-latch.ts +14 -1
- package/src/stream-recovery.ts +72 -0
package/dist/resilience.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resilience.js","sourceRoot":"","sources":["../src/resilience.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;
|
|
1
|
+
{"version":3,"file":"resilience.js","sourceRoot":"","sources":["../src/resilience.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AA4BH,MAAM,QAAQ,GAA4D;IACxE,cAAc,EAAE,GAAG;IACnB,UAAU,EAAE,MAAM;IAClB,WAAW,EAAE,EAAE;IACf,UAAU,EAAE,CAAC;IACb,sBAAsB,EAAE,KAAK;CAC9B,CAAA;AAED,MAAM,iBAAiB,GAA+B;IACpD,iBAAiB,EAAE,EAAE;IACrB,WAAW,EAAE,EAAE;IACf,eAAe,EAAE,CAAC;IAClB,cAAc,EAAE,CAAC;CAClB,CAAA;AAED,SAAS,aAAa,CACpB,KAAiB,EACjB,OAAmC;IAEnC,OAAO;QACL,GAAG,QAAQ;QACX,WAAW,EAAE,iBAAiB,CAAC,KAAK,CAAC;QACrC,GAAG,OAAO;KACX,CAAA;AACH,CAAC;AAED,SAAS,eAAe,CAAC,OAAe,EAAE,GAAqB;IAC7D,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAC1B,GAAG,CAAC,cAAc,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,UAAU,EAAE,OAAO,CAAC,EACtD,GAAG,CAAC,UAAU,CACf,CAAA;IACD,OAAO,IAAI,CAAC,MAAM,EAAE,GAAG,WAAW,CAAA;AACpC,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,EAAoB,EACpB,IAAuD;IAEvD,MAAM,GAAG,GAAG,aAAa,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,CAAA;IAC3C,IAAI,OAAgB,CAAA;IACpB,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,GAAG,GAAG,CAAC,WAAW,EAAE,OAAO,EAAE,EAAE,CAAC;QAC3D,IAAI,CAAC;YACH,OAAO,MAAM,EAAE,EAAE,CAAA;QACnB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,GAAG,GAAG,CAAA;YACb,IAAI,GAAG,CAAC,WAAW,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC;gBAAE,MAAM,GAAG,CAAA;YACvD,IAAI,OAAO,KAAK,GAAG,CAAC,WAAW,GAAG,CAAC;gBAAE,MAAK;YAC1C,MAAM,KAAK,GAAG,eAAe,CAAC,OAAO,EAAE,GAAG,CAAC,CAAA;YAC3C,GAAG,CAAC,GAAG,EAAE,CACP,uBAAuB,IAAI,CAAC,KAAK,YAAY,OAAO,GAAG,CAAC,IAAI,GAAG,CAAC,WAAW,GAAG;gBAC5E,uBAAuB,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,QACrC,GAAa,EAAE,OAAO,IAAI,MAAM,CAAC,GAAG,CACvC,EAAE,CACL,CAAA;YACD,MAAM,CAAC,GAAG,CAAC,KAAK,IAAI,YAAY,CAAC,CAAC,KAAK,CAAC,CAAA;QAC1C,CAAC;IACH,CAAC;IACD,MAAM,OAAO,CAAA;AACf,CAAC;AAED,MAAM,YAAY,GAAG,CAAC,EAAU,EAAiB,EAAE,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAA;AAEzF;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,EAAuB,EACvB,IAAI,GAA0D,EAAE;IAEhE,MAAM,SAAS,GAAG,IAAI,CAAC,WAAW,IAAI,QAAQ,CAAC,sBAAsB,CAAA;IACrE,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAA;IACxD,IAAI,KAAgD,CAAA;IACpD,IAAI,CAAC;QACH,MAAM,OAAO,CAAC,IAAI,CAAC;YACjB,EAAE,EAAE;YACJ,IAAI,OAAO,CAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE;gBAC/B,KAAK,GAAG,UAAU,CAChB,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC,EAC1D,SAAS,CACV,CAAA;YACH,CAAC,CAAC;SACH,CAAC,CAAA;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,GAAG,CACD,iDACG,GAAa,EAAE,OAAO,IAAI,MAAM,CAAC,GAAG,CACvC,eAAe,CAChB,CAAA;QACD,sCAAsC;IACxC,CAAC;YAAS,CAAC;QACT,IAAI,KAAK;YAAE,YAAY,CAAC,KAAK,CAAC,CAAA;IAChC,CAAC;AACH,CAAC"}
|
package/dist/shutdown-latch.d.ts
CHANGED
|
@@ -15,6 +15,7 @@ export type ShutdownLatch = {
|
|
|
15
15
|
* Register an in-flight activity. Throws if shutdown is in progress.
|
|
16
16
|
* Call `end()` on the returned handle when the activity completes.
|
|
17
17
|
*/
|
|
18
|
+
onShutdown(callback: () => void): () => void;
|
|
18
19
|
registerActivity(): ActivityHandle;
|
|
19
20
|
/**
|
|
20
21
|
* Begin graceful shutdown. Returns a promise that resolves when
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shutdown-latch.d.ts","sourceRoot":"","sources":["../src/shutdown-latch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B;;;OAGG;IACH,gBAAgB,IAAI,cAAc,CAAA;IAElC;;;;OAIG;IACH,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IAEjC,2CAA2C;IAC3C,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAA;IAE9B,gDAAgD;IAChD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;CAC7B,CAAA;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,sCAAsC;IACtC,GAAG,IAAI,IAAI,CAAA;CACZ,CAAA;AAED,qBAAa,uBAAwB,SAAQ,KAAK;IAChD,YAAY,OAAO,GAAE,MAA+B,EAGnD;CACF;AAED,wBAAgB,aAAa,IAAI,aAAa,
|
|
1
|
+
{"version":3,"file":"shutdown-latch.d.ts","sourceRoot":"","sources":["../src/shutdown-latch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B;;;OAGG;IACH,UAAU,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAA;IAC5C,gBAAgB,IAAI,cAAc,CAAA;IAElC;;;;OAIG;IACH,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IAEjC,2CAA2C;IAC3C,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAA;IAE9B,gDAAgD;IAChD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;CAC7B,CAAA;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,sCAAsC;IACtC,GAAG,IAAI,IAAI,CAAA;CACZ,CAAA;AAED,qBAAa,uBAAwB,SAAQ,KAAK;IAChD,YAAY,OAAO,GAAE,MAA+B,EAGnD;CACF;AAED,wBAAgB,aAAa,IAAI,aAAa,CA+D7C"}
|
package/dist/shutdown-latch.js
CHANGED
|
@@ -5,8 +5,10 @@ export class ShutdownInProgressError extends Error {
|
|
|
5
5
|
}
|
|
6
6
|
}
|
|
7
7
|
export function shutdownLatch() {
|
|
8
|
+
const callbacks = new Set();
|
|
8
9
|
let activeCount = 0;
|
|
9
10
|
let shuttingDown = false;
|
|
11
|
+
let drainPromise;
|
|
10
12
|
let drainResolve = null;
|
|
11
13
|
function checkDrained() {
|
|
12
14
|
if (shuttingDown && activeCount === 0 && drainResolve) {
|
|
@@ -15,6 +17,13 @@ export function shutdownLatch() {
|
|
|
15
17
|
}
|
|
16
18
|
}
|
|
17
19
|
return {
|
|
20
|
+
onShutdown(callback) {
|
|
21
|
+
if (shuttingDown)
|
|
22
|
+
callback();
|
|
23
|
+
else
|
|
24
|
+
callbacks.add(callback);
|
|
25
|
+
return () => { callbacks.delete(callback); };
|
|
26
|
+
},
|
|
18
27
|
registerActivity() {
|
|
19
28
|
if (shuttingDown) {
|
|
20
29
|
throw new ShutdownInProgressError();
|
|
@@ -33,12 +42,22 @@ export function shutdownLatch() {
|
|
|
33
42
|
},
|
|
34
43
|
initiateShutdown() {
|
|
35
44
|
shuttingDown = true;
|
|
45
|
+
for (const callback of callbacks) {
|
|
46
|
+
try {
|
|
47
|
+
callback();
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
console.error("Messaging shutdown callback failed", error);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
callbacks.clear();
|
|
36
54
|
if (activeCount === 0) {
|
|
37
55
|
return Promise.resolve();
|
|
38
56
|
}
|
|
39
|
-
|
|
57
|
+
drainPromise ??= new Promise((resolve) => {
|
|
40
58
|
drainResolve = resolve;
|
|
41
59
|
});
|
|
60
|
+
return drainPromise;
|
|
42
61
|
},
|
|
43
62
|
get shuttingDown() {
|
|
44
63
|
return shuttingDown;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shutdown-latch.js","sourceRoot":"","sources":["../src/shutdown-latch.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"shutdown-latch.js","sourceRoot":"","sources":["../src/shutdown-latch.ts"],"names":[],"mappings":"AAuCA,MAAM,OAAO,uBAAwB,SAAQ,KAAK;IAChD,YAAY,OAAO,GAAW,sBAAsB;QAClD,KAAK,CAAC,OAAO,CAAC,CAAA;QACd,IAAI,CAAC,IAAI,GAAG,yBAAyB,CAAA;IACvC,CAAC;CACF;AAED,MAAM,UAAU,aAAa;IAC3B,MAAM,SAAS,GAAG,IAAI,GAAG,EAAc,CAAA;IACvC,IAAI,WAAW,GAAG,CAAC,CAAA;IACnB,IAAI,YAAY,GAAG,KAAK,CAAA;IACxB,IAAI,YAAuC,CAAA;IAC3C,IAAI,YAAY,GAAwB,IAAI,CAAA;IAE5C,SAAS,YAAY;QACnB,IAAI,YAAY,IAAI,WAAW,KAAK,CAAC,IAAI,YAAY,EAAE,CAAC;YACtD,YAAY,EAAE,CAAA;YACd,YAAY,GAAG,IAAI,CAAA;QACrB,CAAC;IACH,CAAC;IAED,OAAO;QACL,UAAU,CAAC,QAAQ;YACjB,IAAI,YAAY;gBAAE,QAAQ,EAAE,CAAA;;gBACvB,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;YAC5B,OAAO,GAAG,EAAE,GAAG,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA,CAAC,CAAC,CAAA;QAC7C,CAAC;QACD,gBAAgB;YACd,IAAI,YAAY,EAAE,CAAC;gBACjB,MAAM,IAAI,uBAAuB,EAAE,CAAA;YACrC,CAAC;YAED,WAAW,EAAE,CAAA;YACb,IAAI,KAAK,GAAG,KAAK,CAAA;YAEjB,OAAO;gBACL,GAAG;oBACD,IAAI,KAAK;wBAAE,OAAM;oBACjB,KAAK,GAAG,IAAI,CAAA;oBACZ,WAAW,EAAE,CAAA;oBACb,YAAY,EAAE,CAAA;gBAChB,CAAC;aACF,CAAA;QACH,CAAC;QAED,gBAAgB;YACd,YAAY,GAAG,IAAI,CAAA;YACnB,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;gBACjC,IAAI,CAAC;oBAAC,QAAQ,EAAE,CAAA;gBAAC,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBAAC,OAAO,CAAC,KAAK,CAAC,oCAAoC,EAAE,KAAK,CAAC,CAAA;gBAAC,CAAC;YACjG,CAAC;YACD,SAAS,CAAC,KAAK,EAAE,CAAA;YAEjB,IAAI,WAAW,KAAK,CAAC,EAAE,CAAC;gBACtB,OAAO,OAAO,CAAC,OAAO,EAAE,CAAA;YAC1B,CAAC;YAED,YAAY,KAAK,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;gBACvC,YAAY,GAAG,OAAO,CAAA;YACxB,CAAC,CAAC,CAAA;YACF,OAAO,YAAY,CAAA;QACrB,CAAC;QAED,IAAI,YAAY;YACd,OAAO,YAAY,CAAA;QACrB,CAAC;QAED,IAAI,WAAW;YACb,OAAO,WAAW,CAAA;QACpB,CAAC;KACF,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ResilienceConfig } from "./resilience.js";
|
|
2
|
+
/** Retry failures of the async stream, not just synchronous stream creation. */
|
|
3
|
+
export declare function streamRecovery(reopen: () => void, canReconnect: () => boolean, config?: Partial<ResilienceConfig>): {
|
|
4
|
+
failed: (error: unknown) => void;
|
|
5
|
+
received(): void;
|
|
6
|
+
restart(): void;
|
|
7
|
+
stop(): void;
|
|
8
|
+
};
|
|
9
|
+
//# sourceMappingURL=stream-recovery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream-recovery.d.ts","sourceRoot":"","sources":["../src/stream-recovery.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AAEvD,gFAAgF;AAChF,wBAAgB,cAAc,CAC5B,MAAM,EAAE,MAAM,IAAI,EAClB,YAAY,EAAE,MAAM,OAAO,EAC3B,MAAM,GAAE,OAAO,CAAC,gBAAgB,CAAM;oBAcf,OAAO;;;;EAmD/B"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/** Retry failures of the async stream, not just synchronous stream creation. */
|
|
2
|
+
export function streamRecovery(reopen, canReconnect, config = {}) {
|
|
3
|
+
const log = (message) => {
|
|
4
|
+
try {
|
|
5
|
+
;
|
|
6
|
+
(config.log ?? console.warn)(message);
|
|
7
|
+
}
|
|
8
|
+
catch {
|
|
9
|
+
/* Diagnostics cannot prevent recovery. */
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
let timer;
|
|
13
|
+
let stopped = false;
|
|
14
|
+
let attempts = 0;
|
|
15
|
+
let openedAt = Date.now();
|
|
16
|
+
function failed(error) {
|
|
17
|
+
if (stopped || timer || !canReconnect())
|
|
18
|
+
return;
|
|
19
|
+
if (config.isRetryable?.(error) === false || attempts >= (config.maxAttempts ?? 30)) {
|
|
20
|
+
log(`Provider stream recovery exhausted: ${String(error)}`);
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
// Keep a positive floor even when jitter is zero. An immediately-ended
|
|
24
|
+
// async generator must never create a microtask reconnect loop.
|
|
25
|
+
const cap = Math.min((config.initialDelayMs ?? 100) * (config.multiplier ?? 2) ** attempts++, config.maxDelayMs ?? 30000);
|
|
26
|
+
const delay = Math.max(1, cap * (0.5 + Math.random() * 0.5));
|
|
27
|
+
log(`Provider stream failed; reconnecting in ${Math.round(delay)}ms: ${String(error)}`);
|
|
28
|
+
timer = setTimeout(() => {
|
|
29
|
+
timer = undefined;
|
|
30
|
+
if (stopped || !canReconnect())
|
|
31
|
+
return;
|
|
32
|
+
openedAt = Date.now();
|
|
33
|
+
try {
|
|
34
|
+
reopen();
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
failed(error);
|
|
38
|
+
}
|
|
39
|
+
}, delay);
|
|
40
|
+
timer.unref?.();
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
failed,
|
|
44
|
+
received() {
|
|
45
|
+
// Subscription acknowledgements alone do not prove a stable connection.
|
|
46
|
+
if (Date.now() - openedAt >= 30000)
|
|
47
|
+
attempts = 0;
|
|
48
|
+
},
|
|
49
|
+
restart() {
|
|
50
|
+
if (stopped || !canReconnect())
|
|
51
|
+
return;
|
|
52
|
+
clearTimeout(timer);
|
|
53
|
+
timer = undefined;
|
|
54
|
+
attempts = 0;
|
|
55
|
+
openedAt = Date.now();
|
|
56
|
+
try {
|
|
57
|
+
reopen();
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
failed(error);
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
stop() {
|
|
64
|
+
stopped = true;
|
|
65
|
+
clearTimeout(timer);
|
|
66
|
+
timer = undefined;
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=stream-recovery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream-recovery.js","sourceRoot":"","sources":["../src/stream-recovery.ts"],"names":[],"mappings":"AAEA,gFAAgF;AAChF,MAAM,UAAU,cAAc,CAC5B,MAAkB,EAClB,YAA2B,EAC3B,MAAM,GAA8B,EAAE;IAEtC,MAAM,GAAG,GAAG,CAAC,OAAe,EAAE,EAAE;QAC9B,IAAI,CAAC;YACH,CAAC;YAAA,CAAC,MAAM,CAAC,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,CAAA;QACxC,CAAC;QAAC,MAAM,CAAC;YACP,0CAA0C;QAC5C,CAAC;IACH,CAAC,CAAA;IACD,IAAI,KAAgD,CAAA;IACpD,IAAI,OAAO,GAAG,KAAK,CAAA;IACnB,IAAI,QAAQ,GAAG,CAAC,CAAA;IAChB,IAAI,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;IAEzB,SAAS,MAAM,CAAC,KAAc;QAC5B,IAAI,OAAO,IAAI,KAAK,IAAI,CAAC,YAAY,EAAE;YAAE,OAAM;QAC/C,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,KAAK,KAAK,IAAI,QAAQ,IAAI,CAAC,MAAM,CAAC,WAAW,IAAI,EAAE,CAAC,EAAE,CAAC;YACpF,GAAG,CAAC,uCAAuC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;YAC3D,OAAM;QACR,CAAC;QACD,uEAAuE;QACvE,gEAAgE;QAChE,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAClB,CAAC,MAAM,CAAC,cAAc,IAAI,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,IAAI,CAAC,CAAC,IAAI,QAAQ,EAAE,EACvE,MAAM,CAAC,UAAU,IAAI,KAAK,CAC3B,CAAA;QACD,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,GAAG,CAAC,GAAG,GAAG,IAAI,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC,CAAA;QAC5D,GAAG,CAAC,2CAA2C,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;QACvF,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YACtB,KAAK,GAAG,SAAS,CAAA;YACjB,IAAI,OAAO,IAAI,CAAC,YAAY,EAAE;gBAAE,OAAM;YACtC,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;YACrB,IAAI,CAAC;gBACH,MAAM,EAAE,CAAA;YACV,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,CAAC,KAAK,CAAC,CAAA;YACf,CAAC;QACH,CAAC,EAAE,KAAK,CAAC,CAAA;QACT,KAAK,CAAC,KAAK,EAAE,EAAE,CAAA;IACjB,CAAC;IAED,OAAO;QACL,MAAM;QACN,QAAQ;YACN,wEAAwE;YACxE,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ,IAAI,KAAK;gBAAE,QAAQ,GAAG,CAAC,CAAA;QAClD,CAAC;QACD,OAAO;YACL,IAAI,OAAO,IAAI,CAAC,YAAY,EAAE;gBAAE,OAAM;YACtC,YAAY,CAAC,KAAK,CAAC,CAAA;YACnB,KAAK,GAAG,SAAS,CAAA;YACjB,QAAQ,GAAG,CAAC,CAAA;YACZ,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;YACrB,IAAI,CAAC;gBACH,MAAM,EAAE,CAAA;YACV,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,CAAC,KAAK,CAAC,CAAA;YACf,CAAC;QACH,CAAC;QACD,IAAI;YACF,OAAO,GAAG,IAAI,CAAA;YACd,YAAY,CAAC,KAAK,CAAC,CAAA;YACnB,KAAK,GAAG,SAAS,CAAA;QACnB,CAAC;KACF,CAAA;AACH,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kronos-ts/axon-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Axon Server extension for Kronos — event store and command bus over Axon Server.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
}
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
|
-
"@kronos-ts/core": "0.
|
|
52
|
+
"@kronos-ts/core": "0.5.0",
|
|
53
53
|
"@bufbuild/protobuf": "^2.3",
|
|
54
54
|
"@grpc/grpc-js": "^1.12",
|
|
55
55
|
"nice-grpc": "^2.1",
|
|
@@ -11,7 +11,7 @@ import type {
|
|
|
11
11
|
SequencedEvent,
|
|
12
12
|
StreamingCondition,
|
|
13
13
|
} from "@kronos-ts/core"
|
|
14
|
-
import {
|
|
14
|
+
import { compileQuery } from "@kronos-ts/core"
|
|
15
15
|
import type {
|
|
16
16
|
EventStore,
|
|
17
17
|
SourcingResult,
|
|
@@ -25,6 +25,7 @@ import { globalSequenceToken, FIRST_TOKEN } from "@kronos-ts/core"
|
|
|
25
25
|
import { markerAt, noMarker } from "@kronos-ts/core"
|
|
26
26
|
import type { AxonServerStoreSource } from "./connection.js"
|
|
27
27
|
import { contextView } from "./context-view.js"
|
|
28
|
+
import { boundedRead } from "./bounded-read.js"
|
|
28
29
|
import type {
|
|
29
30
|
Criterion,
|
|
30
31
|
TagsAndNamesCriterion,
|
|
@@ -178,69 +179,59 @@ export function axonServerEventStore(conn: AxonServerStoreSource, context: strin
|
|
|
178
179
|
const { connection, serializer, metadata: createAxonMetadata } = contextView(conn, context)
|
|
179
180
|
const { eventToProto, eventFromProto } = createEventConverters(serializer)
|
|
180
181
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
const subscribers = new Set<(events: ReadonlyArray<EventMessage>) => Promise<void>>()
|
|
185
|
-
async function notifySubscribers(events: ReadonlyArray<EventMessage>): Promise<void> {
|
|
186
|
-
for (const sub of subscribers) {
|
|
187
|
-
try {
|
|
188
|
-
await sub(events)
|
|
189
|
-
} catch {
|
|
190
|
-
/* ignore subscriber errors */
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
return {
|
|
196
|
-
async source(condition: SourcingCondition): Promise<SourcingResult> {
|
|
197
|
-
const criterions = criteriaToCriterions(compileQuery(condition.query))
|
|
198
|
-
const start = condition.start ?? 0n
|
|
199
|
-
|
|
200
|
-
const request = {
|
|
201
|
-
fromSequence: start,
|
|
202
|
-
criterion: criterions,
|
|
203
|
-
}
|
|
182
|
+
async function sourceOnce(condition: SourcingCondition, signal: AbortSignal): Promise<SourcingResult> {
|
|
183
|
+
const criterions = criteriaToCriterions(compileQuery(condition.query))
|
|
184
|
+
const start = condition.start ?? 0n
|
|
204
185
|
|
|
205
|
-
|
|
206
|
-
|
|
186
|
+
const request = {
|
|
187
|
+
fromSequence: start,
|
|
188
|
+
criterion: criterions,
|
|
189
|
+
}
|
|
207
190
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
marker = markerAt(response.consistencyMarker)
|
|
191
|
+
const events: EventMessage[] = []
|
|
192
|
+
let marker: ConsistencyMarker = noMarker()
|
|
193
|
+
|
|
194
|
+
try {
|
|
195
|
+
const stream = connection.eventStore.source(request, { metadata: createAxonMetadata(), signal })
|
|
196
|
+
for await (const response of stream) {
|
|
197
|
+
if (response.event) {
|
|
198
|
+
const taggedEvent = response.event
|
|
199
|
+
const protoEvent = taggedEvent.event
|
|
200
|
+
if (protoEvent) {
|
|
201
|
+
// DCB source/stream responses carry no tags — the server indexes
|
|
202
|
+
// them write-side but does not echo them back (SequencedEvent has
|
|
203
|
+
// only sequence + event). Reconstructed events get empty tags.
|
|
204
|
+
events.push(eventFromProto(protoEvent, []))
|
|
223
205
|
}
|
|
224
206
|
}
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
// reconciled. `Source` fails OUT_OF_RANGE with "Start sequence cannot
|
|
229
|
-
// be larger than end sequence" whenever `fromSequence` is beyond the
|
|
230
|
-
// global head, which is the ORDINARY STEADY STATE of a snapshotted
|
|
231
|
-
// load: an entry written at the head means the very next read resumes
|
|
232
|
-
// at head + 1 and legitimately finds nothing.
|
|
233
|
-
//
|
|
234
|
-
// The marker is `start - 1`, and it is exact rather than conservative:
|
|
235
|
-
// the caller has already accounted for everything up to there (that is
|
|
236
|
-
// what asking to start later MEANS), and nothing can exist after it or
|
|
237
|
-
// the server would not have refused. So an append conditioned on this
|
|
238
|
-
// read is checked against precisely the range that was read.
|
|
239
|
-
if (!isStartPastHead(err)) throw err
|
|
240
|
-
return { events: [], marker: start > 0n ? markerAt(start - 1n) : noMarker() }
|
|
207
|
+
if (response.consistencyMarker !== undefined) {
|
|
208
|
+
marker = markerAt(response.consistencyMarker)
|
|
209
|
+
}
|
|
241
210
|
}
|
|
211
|
+
} catch (err) {
|
|
212
|
+
// READING PAST THE HEAD IS AN EMPTY ANSWER, NOT AN ERROR — and Axon
|
|
213
|
+
// Server disagrees, so this is where the two vocabularies are
|
|
214
|
+
// reconciled. `Source` fails OUT_OF_RANGE with "Start sequence cannot
|
|
215
|
+
// be larger than end sequence" whenever `fromSequence` is beyond the
|
|
216
|
+
// global head, which is the ORDINARY STEADY STATE of a snapshotted
|
|
217
|
+
// load: an entry written at the head means the very next read resumes
|
|
218
|
+
// at head + 1 and legitimately finds nothing.
|
|
219
|
+
//
|
|
220
|
+
// The marker is `start - 1`, and it is exact rather than conservative:
|
|
221
|
+
// the caller has already accounted for everything up to there (that is
|
|
222
|
+
// what asking to start later MEANS), and nothing can exist after it or
|
|
223
|
+
// the server would not have refused. So an append conditioned on this
|
|
224
|
+
// read is checked against precisely the range that was read.
|
|
225
|
+
if (!isStartPastHead(err)) throw err
|
|
226
|
+
return { events: [], marker: start > 0n ? markerAt(start - 1n) : noMarker() }
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return { events, marker }
|
|
230
|
+
}
|
|
242
231
|
|
|
243
|
-
|
|
232
|
+
return {
|
|
233
|
+
source(condition: SourcingCondition): Promise<SourcingResult> {
|
|
234
|
+
return boundedRead(connection.config.readTimeoutMs, (signal) => sourceOnce(condition, signal))
|
|
244
235
|
},
|
|
245
236
|
|
|
246
237
|
async appendEvents(
|
|
@@ -271,7 +262,6 @@ export function axonServerEventStore(conn: AxonServerStoreSource, context: strin
|
|
|
271
262
|
metadata: createAxonMetadata(),
|
|
272
263
|
})
|
|
273
264
|
responseMarker = response.consistencyMarker
|
|
274
|
-
await notifySubscribers(newEvents)
|
|
275
265
|
},
|
|
276
266
|
async afterCommit() {
|
|
277
267
|
return markerAt(responseMarker ?? 0n)
|
|
@@ -299,7 +289,20 @@ export function axonServerEventStore(conn: AxonServerStoreSource, context: strin
|
|
|
299
289
|
criterion: criterions,
|
|
300
290
|
}
|
|
301
291
|
|
|
302
|
-
|
|
292
|
+
// `close()` must reach the server. This is a server-streaming call: the
|
|
293
|
+
// client sends nothing after the request, so the only way to end it from
|
|
294
|
+
// this side is to cancel it. Abandoning the async iterator does not do
|
|
295
|
+
// that — the `for await` below stays parked on the call until the next
|
|
296
|
+
// event arrives, which at the head of a quiet stream is never, and every
|
|
297
|
+
// closed stream leaves a live call behind on the server. With enough of
|
|
298
|
+
// them open, Axon Server stops answering later `source` reads (a 2-CPU
|
|
299
|
+
// container gets there after a handful): handlers never reply, and
|
|
300
|
+
// commands die at the server's own timeout.
|
|
301
|
+
const cancel = new AbortController()
|
|
302
|
+
const grpcStream = connection.eventStore.stream(request, {
|
|
303
|
+
metadata: createAxonMetadata(),
|
|
304
|
+
signal: cancel.signal,
|
|
305
|
+
})
|
|
303
306
|
|
|
304
307
|
// Internal buffer for events pulled from the gRPC stream
|
|
305
308
|
const buffer: SequencedEvent[] = []
|
|
@@ -328,7 +331,11 @@ export function axonServerEventStore(conn: AxonServerStoreSource, context: strin
|
|
|
328
331
|
completed = true
|
|
329
332
|
availableCallback?.()
|
|
330
333
|
} catch (err) {
|
|
331
|
-
|
|
334
|
+
// Our own cancellation surfaces here as an AbortError; that is the
|
|
335
|
+
// stream closing as asked, not a failure to report.
|
|
336
|
+
if (!completed) {
|
|
337
|
+
streamError = err instanceof Error ? err : new Error(String(err))
|
|
338
|
+
}
|
|
332
339
|
completed = true
|
|
333
340
|
availableCallback?.()
|
|
334
341
|
}
|
|
@@ -336,7 +343,7 @@ export function axonServerEventStore(conn: AxonServerStoreSource, context: strin
|
|
|
336
343
|
|
|
337
344
|
startReading()
|
|
338
345
|
|
|
339
|
-
return
|
|
346
|
+
return {
|
|
340
347
|
next() {
|
|
341
348
|
return buffer.shift()
|
|
342
349
|
},
|
|
@@ -364,13 +371,15 @@ export function axonServerEventStore(conn: AxonServerStoreSource, context: strin
|
|
|
364
371
|
close() {
|
|
365
372
|
completed = true
|
|
366
373
|
availableCallback = null
|
|
367
|
-
|
|
374
|
+
cancel.abort()
|
|
368
375
|
},
|
|
369
|
-
}
|
|
376
|
+
}
|
|
370
377
|
},
|
|
371
378
|
|
|
372
379
|
async getHeadPosition(): Promise<bigint> {
|
|
373
|
-
const response = await connection.
|
|
380
|
+
const response = await boundedRead(connection.config.readTimeoutMs, (signal) =>
|
|
381
|
+
connection.eventStore.getHead({}, { metadata: createAxonMetadata(), signal }),
|
|
382
|
+
)
|
|
374
383
|
return response.sequence
|
|
375
384
|
},
|
|
376
385
|
|
|
@@ -379,21 +388,10 @@ export function axonServerEventStore(conn: AxonServerStoreSource, context: strin
|
|
|
379
388
|
},
|
|
380
389
|
|
|
381
390
|
async latestToken(): Promise<TrackingToken> {
|
|
382
|
-
const response = await connection.
|
|
391
|
+
const response = await boundedRead(connection.config.readTimeoutMs, (signal) =>
|
|
392
|
+
connection.eventStore.getHead({}, { metadata: createAxonMetadata(), signal }),
|
|
393
|
+
)
|
|
383
394
|
return globalSequenceToken(response.sequence)
|
|
384
395
|
},
|
|
385
|
-
|
|
386
|
-
// EventBus contract — publish = append without condition, then notify
|
|
387
|
-
// in-process subscribers.
|
|
388
|
-
async publish(events: ReadonlyArray<EventMessage>): Promise<void> {
|
|
389
|
-
await this.append(events)
|
|
390
|
-
},
|
|
391
|
-
|
|
392
|
-
subscribe(handler: (events: ReadonlyArray<EventMessage>) => Promise<void>): () => void {
|
|
393
|
-
subscribers.add(handler)
|
|
394
|
-
return () => {
|
|
395
|
-
subscribers.delete(handler)
|
|
396
|
-
}
|
|
397
|
-
},
|
|
398
396
|
}
|
|
399
397
|
}
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
import type {
|
|
26
26
|
EventStore,
|
|
27
27
|
Snapshot,
|
|
28
|
-
|
|
28
|
+
SnapshotStoreCapability,
|
|
29
29
|
SourcingCondition,
|
|
30
30
|
SourcingResult,
|
|
31
31
|
} from "@kronos-ts/core"
|
|
@@ -33,6 +33,7 @@ import type { Serializer } from "@kronos-ts/core"
|
|
|
33
33
|
import { withoutSnapshotKey } from "@kronos-ts/core"
|
|
34
34
|
import type { AxonServerStoreSource } from "./connection.js"
|
|
35
35
|
import { contextView } from "./context-view.js"
|
|
36
|
+
import { boundedRead } from "./bounded-read.js"
|
|
36
37
|
import type { Snapshot as ProtoSnapshot } from "./generated/dcb.js"
|
|
37
38
|
|
|
38
39
|
// ---------------------------------------------------------------------------
|
|
@@ -92,7 +93,7 @@ function encodeKey(key: string): Uint8Array {
|
|
|
92
93
|
* )
|
|
93
94
|
* ```
|
|
94
95
|
*
|
|
95
|
-
* ADDITIVE, NOT COLLAPSING. It returns `E &
|
|
96
|
+
* ADDITIVE, NOT COLLAPSING. It returns `E & SnapshotStoreCapability` — the store you
|
|
96
97
|
* passed in, plus the write — so capabilities stack in either order and nothing
|
|
97
98
|
* the inner store carried is laundered on the way through.
|
|
98
99
|
*
|
|
@@ -111,16 +112,18 @@ export function axonServerSnapshottingEventStore<E extends EventStore>(
|
|
|
111
112
|
next: E,
|
|
112
113
|
conn: AxonServerStoreSource,
|
|
113
114
|
context: string,
|
|
114
|
-
): E &
|
|
115
|
+
): E & SnapshotStoreCapability {
|
|
115
116
|
const { connection, serializer, metadata: createAxonMetadata } = contextView(conn, context)
|
|
116
117
|
const { snapshotToProto, snapshotFromProto } = createSnapshotConverters(serializer)
|
|
117
118
|
|
|
118
119
|
/** The cached fold filed under `key`, or nothing — the first of the two calls. */
|
|
119
120
|
async function loadSnapshot(key: string): Promise<Snapshot | undefined> {
|
|
120
121
|
try {
|
|
121
|
-
const response = await connection.
|
|
122
|
-
|
|
123
|
-
|
|
122
|
+
const response = await boundedRead(connection.config.readTimeoutMs, (signal) =>
|
|
123
|
+
connection.snapshotStore.getLast(
|
|
124
|
+
{ key: encodeKey(key) },
|
|
125
|
+
{ metadata: createAxonMetadata(), signal },
|
|
126
|
+
),
|
|
124
127
|
)
|
|
125
128
|
|
|
126
129
|
if (!response.snapshot) {
|
|
@@ -187,5 +190,5 @@ export function axonServerSnapshottingEventStore<E extends EventStore>(
|
|
|
187
190
|
const result = await next.source({ ...plain, start })
|
|
188
191
|
return { ...result, snapshot }
|
|
189
192
|
},
|
|
190
|
-
} as E &
|
|
193
|
+
} as E & SnapshotStoreCapability
|
|
191
194
|
}
|