@ddd-ts/event-sourcing-firestore 0.0.36 → 0.0.38
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/LICENSE +21 -0
- package/dist/_virtual/_rolldown/runtime.js +29 -0
- package/dist/firestore.event-lake.aggregate-store.d.ts +4 -8
- package/dist/firestore.event-lake.aggregate-store.d.ts.map +1 -1
- package/dist/firestore.event-lake.aggregate-store.js +35 -33
- package/dist/firestore.event-lake.aggregate-store.mjs +36 -0
- package/dist/firestore.event-lake.storage-layer.d.ts +2 -2
- package/dist/firestore.event-lake.storage-layer.d.ts.map +1 -1
- package/dist/firestore.event-lake.storage-layer.js +67 -117
- package/dist/firestore.event-lake.storage-layer.mjs +65 -0
- package/dist/firestore.event-lake.store.d.ts +1 -1
- package/dist/firestore.event-lake.store.d.ts.map +1 -1
- package/dist/firestore.event-lake.store.js +14 -12
- package/dist/firestore.event-lake.store.mjs +13 -0
- package/dist/firestore.event-stream.aggregate-store.d.ts +2 -2
- package/dist/firestore.event-stream.aggregate-store.d.ts.map +1 -1
- package/dist/firestore.event-stream.aggregate-store.js +35 -37
- package/dist/firestore.event-stream.aggregate-store.mjs +36 -0
- package/dist/firestore.event-stream.storage-layer.d.ts +2 -2
- package/dist/firestore.event-stream.storage-layer.d.ts.map +1 -1
- package/dist/firestore.event-stream.storage-layer.js +67 -110
- package/dist/firestore.event-stream.storage-layer.mjs +65 -0
- package/dist/firestore.event-stream.store.d.ts +1 -1
- package/dist/firestore.event-stream.store.d.ts.map +1 -1
- package/dist/firestore.event-stream.store.js +14 -12
- package/dist/firestore.event-stream.store.mjs +13 -0
- package/dist/firestore.projected-stream.reader.d.ts +1 -1
- package/dist/firestore.projected-stream.reader.d.ts.map +1 -1
- package/dist/firestore.projected-stream.reader.js +35 -37
- package/dist/firestore.projected-stream.reader.mjs +34 -0
- package/dist/firestore.projected-stream.storage-layer.d.ts +1 -1
- package/dist/firestore.projected-stream.storage-layer.d.ts.map +1 -1
- package/dist/firestore.projected-stream.storage-layer.js +119 -140
- package/dist/firestore.projected-stream.storage-layer.mjs +118 -0
- package/dist/firestore.snapshotter.js +36 -40
- package/dist/firestore.snapshotter.mjs +35 -0
- package/dist/index.js +30 -32
- package/dist/index.mjs +12 -0
- package/dist/projection/firestore.projector.d.ts +1 -1
- package/dist/projection/firestore.projector.d.ts.map +1 -1
- package/dist/projection/firestore.projector.js +477 -630
- package/dist/projection/firestore.projector.mjs +479 -0
- package/dist/projection/testkit/case-fixture.d.ts.map +1 -1
- package/dist/projection/testkit.d.ts +1 -1
- package/dist/projection/testkit.d.ts.map +1 -1
- package/package.json +43 -32
- package/dist/firestore.event-lake.aggregate-store.js.map +0 -1
- package/dist/firestore.event-lake.aggregate-store.spec.js +0 -58
- package/dist/firestore.event-lake.aggregate-store.spec.js.map +0 -1
- package/dist/firestore.event-lake.storage-layer.js.map +0 -1
- package/dist/firestore.event-lake.store.js.map +0 -1
- package/dist/firestore.event-lake.store.spec.js +0 -50
- package/dist/firestore.event-lake.store.spec.js.map +0 -1
- package/dist/firestore.event-stream-store.spec.js +0 -50
- package/dist/firestore.event-stream-store.spec.js.map +0 -1
- package/dist/firestore.event-stream.aggregate-store.js.map +0 -1
- package/dist/firestore.event-stream.aggregate-store.spec.js +0 -54
- package/dist/firestore.event-stream.aggregate-store.spec.js.map +0 -1
- package/dist/firestore.event-stream.storage-layer.js.map +0 -1
- package/dist/firestore.event-stream.store.js.map +0 -1
- package/dist/firestore.projected-stream.reader.js.map +0 -1
- package/dist/firestore.projected-stream.reader.spec.js +0 -54
- package/dist/firestore.projected-stream.reader.spec.js.map +0 -1
- package/dist/firestore.projected-stream.storage-layer.js.map +0 -1
- package/dist/firestore.snapshotter.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/projection/cases/attempts.spec.js +0 -42
- package/dist/projection/cases/attempts.spec.js.map +0 -1
- package/dist/projection/cases/batchlast.spec.js +0 -51
- package/dist/projection/cases/batchlast.spec.js.map +0 -1
- package/dist/projection/cases/bigshuffle.spec.js +0 -59
- package/dist/projection/cases/bigshuffle.spec.js.map +0 -1
- package/dist/projection/cases/burst.spec.js +0 -38
- package/dist/projection/cases/burst.spec.js.map +0 -1
- package/dist/projection/cases/claimtimeout.spec.js +0 -40
- package/dist/projection/cases/claimtimeout.spec.js.map +0 -1
- package/dist/projection/cases/concurrency.spec.js +0 -49
- package/dist/projection/cases/concurrency.spec.js.map +0 -1
- package/dist/projection/cases/deduplicate.spec.js +0 -22
- package/dist/projection/cases/deduplicate.spec.js.map +0 -1
- package/dist/projection/cases/defer.spec.js +0 -44
- package/dist/projection/cases/defer.spec.js.map +0 -1
- package/dist/projection/cases/lock.spec.js +0 -91
- package/dist/projection/cases/lock.spec.js.map +0 -1
- package/dist/projection/cases/skip.spec.js +0 -86
- package/dist/projection/cases/skip.spec.js.map +0 -1
- package/dist/projection/cases/stress.spec.js +0 -73
- package/dist/projection/cases/stress.spec.js.map +0 -1
- package/dist/projection/firestore.projector.js.map +0 -1
- package/dist/projection/testkit/case-fixture.js +0 -341
- package/dist/projection/testkit/case-fixture.js.map +0 -1
- package/dist/projection/testkit.js +0 -77
- package/dist/projection/testkit.js.map +0 -1
- package/dist/projection/trace.decorator.js +0 -35
- package/dist/projection/trace.decorator.js.map +0 -1
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const case_fixture_1 = require("../testkit/case-fixture");
|
|
4
|
-
const test = (0, case_fixture_1.caseFixture)("ClaimTimeout", {
|
|
5
|
-
handlers: {
|
|
6
|
-
AccountOpened: {
|
|
7
|
-
chain: [
|
|
8
|
-
case_fixture_1.traits.Context(),
|
|
9
|
-
case_fixture_1.traits.Transaction(),
|
|
10
|
-
case_fixture_1.traits.ClaimTimeout(500),
|
|
11
|
-
case_fixture_1.traits.Parallel(),
|
|
12
|
-
case_fixture_1.traits.Suspense(),
|
|
13
|
-
],
|
|
14
|
-
lock: case_fixture_1.locks.wholeAccount,
|
|
15
|
-
},
|
|
16
|
-
},
|
|
17
|
-
projector: {
|
|
18
|
-
retry: { attempts: 2, minDelay: 500, maxDelay: 500 },
|
|
19
|
-
unclaimOnFailure: false,
|
|
20
|
-
},
|
|
21
|
-
});
|
|
22
|
-
test.describe(() => {
|
|
23
|
-
it("will process any previous event that disappeared for too long", async () => {
|
|
24
|
-
const { events, act, control, assert } = await test.setup();
|
|
25
|
-
const [account, opened] = events.open(test.name);
|
|
26
|
-
await act.save(account);
|
|
27
|
-
const reallow = await control.markFailing(opened);
|
|
28
|
-
// this one will retry 3 times from breathing, but will still fail
|
|
29
|
-
await act.handle(opened).catch(() => { });
|
|
30
|
-
// after 3 attempts, the claim timeout should have been exceeded (1s vs 500ms * 3)
|
|
31
|
-
// so we can re-allow the handler to complete.
|
|
32
|
-
reallow();
|
|
33
|
-
// now the next event will make the first one succeed
|
|
34
|
-
const deposited = account.deposit(100);
|
|
35
|
-
await act.save(account);
|
|
36
|
-
await act.handle(deposited);
|
|
37
|
-
await assert.cashflow(account.id).toHave({ id: account.id, flow: 100 });
|
|
38
|
-
});
|
|
39
|
-
});
|
|
40
|
-
//# sourceMappingURL=claimtimeout.spec.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"claimtimeout.spec.js","sourceRoot":"","sources":["../../../src/projection/cases/claimtimeout.spec.ts"],"names":[],"mappings":";;AAAA,0DAAqE;AAErE,MAAM,IAAI,GAAG,IAAA,0BAAW,EAAC,cAAc,EAAE;IACvC,QAAQ,EAAE;QACR,aAAa,EAAE;YACb,KAAK,EAAE;gBACL,qBAAM,CAAC,OAAO,EAAE;gBAChB,qBAAM,CAAC,WAAW,EAAE;gBACpB,qBAAM,CAAC,YAAY,CAAC,GAAG,CAAC;gBACxB,qBAAM,CAAC,QAAQ,EAAE;gBACjB,qBAAM,CAAC,QAAQ,EAAE;aAClB;YACD,IAAI,EAAE,oBAAK,CAAC,YAAY;SACzB;KACF;IACD,SAAS,EAAE;QACT,KAAK,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,EAAE;QACpD,gBAAgB,EAAE,KAAK;KACxB;CACF,CAAC,CAAC;AAEH,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE;IACjB,EAAE,CAAC,+DAA+D,EAAE,KAAK,IAAI,EAAE;QAC7E,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;QAE5D,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjD,MAAM,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAExB,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;QAElD,kEAAkE;QAClE,MAAM,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QAEzC,kFAAkF;QAClF,8CAA8C;QAC9C,OAAO,EAAE,CAAC;QAEV,qDAAqD;QACrD,MAAM,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACvC,MAAM,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACxB,MAAM,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAE5B,MAAM,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;IAC1E,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import { caseFixture, locks, traits } from \"../testkit/case-fixture\";\n\nconst test = caseFixture(\"ClaimTimeout\", {\n handlers: {\n AccountOpened: {\n chain: [\n traits.Context(),\n traits.Transaction(),\n traits.ClaimTimeout(500),\n traits.Parallel(),\n traits.Suspense(),\n ],\n lock: locks.wholeAccount,\n },\n },\n projector: {\n retry: { attempts: 2, minDelay: 500, maxDelay: 500 },\n unclaimOnFailure: false,\n },\n});\n\ntest.describe(() => {\n it(\"will process any previous event that disappeared for too long\", async () => {\n const { events, act, control, assert } = await test.setup();\n\n const [account, opened] = events.open(test.name);\n await act.save(account);\n\n const reallow = await control.markFailing(opened);\n\n // this one will retry 3 times from breathing, but will still fail\n await act.handle(opened).catch(() => {});\n\n // after 3 attempts, the claim timeout should have been exceeded (1s vs 500ms * 3)\n // so we can re-allow the handler to complete.\n reallow();\n\n // now the next event will make the first one succeed\n const deposited = account.deposit(100);\n await act.save(account);\n await act.handle(deposited);\n\n await assert.cashflow(account.id).toHave({ id: account.id, flow: 100 });\n });\n});\n"]}
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const case_fixture_1 = require("../testkit/case-fixture");
|
|
4
|
-
const test = (0, case_fixture_1.caseFixture)("Concurrency", {
|
|
5
|
-
handlers: {
|
|
6
|
-
Deposited: {
|
|
7
|
-
chain: [
|
|
8
|
-
case_fixture_1.traits.Context(),
|
|
9
|
-
case_fixture_1.traits.Transaction(),
|
|
10
|
-
case_fixture_1.traits.Suspense(),
|
|
11
|
-
case_fixture_1.traits.Delay(1000),
|
|
12
|
-
case_fixture_1.traits.Parallel(),
|
|
13
|
-
],
|
|
14
|
-
lock: case_fixture_1.locks.accountAndEventId,
|
|
15
|
-
},
|
|
16
|
-
Withdrawn: {
|
|
17
|
-
chain: [
|
|
18
|
-
case_fixture_1.traits.Context(),
|
|
19
|
-
case_fixture_1.traits.Transaction(),
|
|
20
|
-
case_fixture_1.traits.Suspense(),
|
|
21
|
-
case_fixture_1.traits.Parallel(),
|
|
22
|
-
],
|
|
23
|
-
lock: case_fixture_1.locks.accountAndEventId,
|
|
24
|
-
},
|
|
25
|
-
},
|
|
26
|
-
projector: {
|
|
27
|
-
unclaimOnFailure: false,
|
|
28
|
-
retry: { attempts: 5, minDelay: 100, maxDelay: 100 },
|
|
29
|
-
},
|
|
30
|
-
});
|
|
31
|
-
test.describe(() => {
|
|
32
|
-
it("handles two events of the same account in parallel (time-based)", async () => {
|
|
33
|
-
const { events, act, assert } = await test.setup();
|
|
34
|
-
const [account, opened] = events.open(test.name);
|
|
35
|
-
await act.save(account);
|
|
36
|
-
await act.handle(opened);
|
|
37
|
-
// Deposit before withdraw
|
|
38
|
-
const deposit = account.deposit(100);
|
|
39
|
-
const withdraw = account.withdraw(50);
|
|
40
|
-
await act.save(account);
|
|
41
|
-
await Promise.all([act.handle(withdraw), act.handle(deposit)]);
|
|
42
|
-
await assert.cashflow(account.id).toHave({
|
|
43
|
-
// But withdraw is first in ops_trace, because it has no delay
|
|
44
|
-
ops_trace: [opened.id, withdraw.id, deposit.id],
|
|
45
|
-
flow: 150,
|
|
46
|
-
});
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
//# sourceMappingURL=concurrency.spec.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"concurrency.spec.js","sourceRoot":"","sources":["../../../src/projection/cases/concurrency.spec.ts"],"names":[],"mappings":";;AAAA,0DAAqE;AAErE,MAAM,IAAI,GAAG,IAAA,0BAAW,EAAC,aAAa,EAAE;IACtC,QAAQ,EAAE;QACR,SAAS,EAAE;YACT,KAAK,EAAE;gBACL,qBAAM,CAAC,OAAO,EAAE;gBAChB,qBAAM,CAAC,WAAW,EAAE;gBACpB,qBAAM,CAAC,QAAQ,EAAE;gBACjB,qBAAM,CAAC,KAAK,CAAC,IAAI,CAAC;gBAClB,qBAAM,CAAC,QAAQ,EAAE;aAClB;YACD,IAAI,EAAE,oBAAK,CAAC,iBAAiB;SAC9B;QACD,SAAS,EAAE;YACT,KAAK,EAAE;gBACL,qBAAM,CAAC,OAAO,EAAE;gBAChB,qBAAM,CAAC,WAAW,EAAE;gBACpB,qBAAM,CAAC,QAAQ,EAAE;gBACjB,qBAAM,CAAC,QAAQ,EAAE;aAClB;YACD,IAAI,EAAE,oBAAK,CAAC,iBAAiB;SAC9B;KACF;IACD,SAAS,EAAE;QACT,gBAAgB,EAAE,KAAK;QACvB,KAAK,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,EAAE;KACrD;CACF,CAAC,CAAC;AAEH,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE;IACjB,EAAE,CAAC,iEAAiE,EAAE,KAAK,IAAI,EAAE;QAC/E,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;QAEnD,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjD,MAAM,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACxB,MAAM,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAEzB,0BAA0B;QAC1B,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACrC,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QACtC,MAAM,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAExB,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAE/D,MAAM,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC;YACvC,8DAA8D;YAC9D,SAAS,EAAE,CAAC,MAAM,CAAC,EAAE,EAAE,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC,EAAE,CAAC;YAC/C,IAAI,EAAE,GAAG;SACV,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import { caseFixture, locks, traits } from \"../testkit/case-fixture\";\n\nconst test = caseFixture(\"Concurrency\", {\n handlers: {\n Deposited: {\n chain: [\n traits.Context(),\n traits.Transaction(),\n traits.Suspense(),\n traits.Delay(1000),\n traits.Parallel(),\n ],\n lock: locks.accountAndEventId,\n },\n Withdrawn: {\n chain: [\n traits.Context(),\n traits.Transaction(),\n traits.Suspense(),\n traits.Parallel(),\n ],\n lock: locks.accountAndEventId,\n },\n },\n projector: {\n unclaimOnFailure: false,\n retry: { attempts: 5, minDelay: 100, maxDelay: 100 },\n },\n});\n\ntest.describe(() => {\n it(\"handles two events of the same account in parallel (time-based)\", async () => {\n const { events, act, assert } = await test.setup();\n\n const [account, opened] = events.open(test.name);\n await act.save(account);\n await act.handle(opened);\n\n // Deposit before withdraw\n const deposit = account.deposit(100);\n const withdraw = account.withdraw(50);\n await act.save(account);\n\n await Promise.all([act.handle(withdraw), act.handle(deposit)]);\n\n await assert.cashflow(account.id).toHave({\n // But withdraw is first in ops_trace, because it has no delay\n ops_trace: [opened.id, withdraw.id, deposit.id],\n flow: 150,\n });\n });\n});\n"]}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const case_fixture_1 = require("../testkit/case-fixture");
|
|
4
|
-
const test = (0, case_fixture_1.caseFixture)("Deduplicate");
|
|
5
|
-
test.describe(() => {
|
|
6
|
-
it("ignores duplicate withdraw events and applies once", async () => {
|
|
7
|
-
const { events, act, assert } = await test.setup();
|
|
8
|
-
const [account, opened] = events.open(test.name);
|
|
9
|
-
const deposit = account.deposit(100);
|
|
10
|
-
const withdraw = account.withdraw(50);
|
|
11
|
-
await act.save(account);
|
|
12
|
-
await act.handle(opened);
|
|
13
|
-
await Promise.all([
|
|
14
|
-
// Those two withdraws are duplicates and only one should be applied
|
|
15
|
-
act.handle(withdraw),
|
|
16
|
-
act.handle(withdraw),
|
|
17
|
-
act.handle(deposit),
|
|
18
|
-
]);
|
|
19
|
-
await assert.cashflow(account.id).toHave({ id: account.id, flow: 150 });
|
|
20
|
-
});
|
|
21
|
-
});
|
|
22
|
-
//# sourceMappingURL=deduplicate.spec.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"deduplicate.spec.js","sourceRoot":"","sources":["../../../src/projection/cases/deduplicate.spec.ts"],"names":[],"mappings":";;AAAA,0DAAsD;AAEtD,MAAM,IAAI,GAAG,IAAA,0BAAW,EAAC,aAAa,CAAC,CAAC;AAExC,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE;IACjB,EAAE,CAAC,oDAAoD,EAAE,KAAK,IAAI,EAAE;QAClE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;QAEnD,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAEjD,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACrC,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QAEtC,MAAM,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAExB,MAAM,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAEzB,MAAM,OAAO,CAAC,GAAG,CAAC;YAChB,oEAAoE;YACpE,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC;YACpB,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC;YACpB,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC;SACpB,CAAC,CAAC;QAEH,MAAM,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;IAC1E,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import { caseFixture } from \"../testkit/case-fixture\";\n\nconst test = caseFixture(\"Deduplicate\");\n\ntest.describe(() => {\n it(\"ignores duplicate withdraw events and applies once\", async () => {\n const { events, act, assert } = await test.setup();\n\n const [account, opened] = events.open(test.name);\n\n const deposit = account.deposit(100);\n const withdraw = account.withdraw(50);\n\n await act.save(account);\n\n await act.handle(opened);\n\n await Promise.all([\n // Those two withdraws are duplicates and only one should be applied\n act.handle(withdraw),\n act.handle(withdraw),\n act.handle(deposit),\n ]);\n\n await assert.cashflow(account.id).toHave({ id: account.id, flow: 150 });\n });\n});\n"]}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const case_fixture_1 = require("../testkit/case-fixture");
|
|
4
|
-
jest.setTimeout(180_000);
|
|
5
|
-
const test = (0, case_fixture_1.caseFixture)("Defer", {
|
|
6
|
-
handlers: {
|
|
7
|
-
Deposited: {
|
|
8
|
-
chain: [
|
|
9
|
-
case_fixture_1.traits.Context(),
|
|
10
|
-
case_fixture_1.traits.Transaction(),
|
|
11
|
-
case_fixture_1.traits.Suspense(),
|
|
12
|
-
case_fixture_1.traits.ClaimTimeout(2_000),
|
|
13
|
-
case_fixture_1.traits.IsolateAfter(1),
|
|
14
|
-
case_fixture_1.traits.Sequential(),
|
|
15
|
-
],
|
|
16
|
-
lock: case_fixture_1.locks.wholeAccount,
|
|
17
|
-
},
|
|
18
|
-
},
|
|
19
|
-
projector: {
|
|
20
|
-
retry: { attempts: 10, minDelay: 100, maxDelay: 100, backoff: 1 },
|
|
21
|
-
},
|
|
22
|
-
});
|
|
23
|
-
test.describe(() => {
|
|
24
|
-
it("will allow more breathing time if a previous locking event is being processed", async () => {
|
|
25
|
-
const { events, act, control, assert } = await test.setup();
|
|
26
|
-
const [account, opened] = events.open(test.name);
|
|
27
|
-
await act.save(account);
|
|
28
|
-
await act.handle(opened);
|
|
29
|
-
const failing = account.deposit(1000);
|
|
30
|
-
const next = account.deposit(100);
|
|
31
|
-
await act.save(account);
|
|
32
|
-
const unmark = control.markFailing(failing);
|
|
33
|
-
// const h1 = act.handle(failing);
|
|
34
|
-
const h2 = act.handle(next);
|
|
35
|
-
await control.wait(100);
|
|
36
|
-
// await control.wait(2_000);
|
|
37
|
-
await assert.cashflow(account.id).toHave({ flow: 0 });
|
|
38
|
-
unmark();
|
|
39
|
-
// await h1;
|
|
40
|
-
await h2;
|
|
41
|
-
await assert.cashflow(account.id).toHave({ flow: 1100 });
|
|
42
|
-
});
|
|
43
|
-
});
|
|
44
|
-
//# sourceMappingURL=defer.spec.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"defer.spec.js","sourceRoot":"","sources":["../../../src/projection/cases/defer.spec.ts"],"names":[],"mappings":";;AAAA,0DAAqE;AAErE,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;AAEzB,MAAM,IAAI,GAAG,IAAA,0BAAW,EAAC,OAAO,EAAE;IAChC,QAAQ,EAAE;QACR,SAAS,EAAE;YACT,KAAK,EAAE;gBACL,qBAAM,CAAC,OAAO,EAAE;gBAChB,qBAAM,CAAC,WAAW,EAAE;gBACpB,qBAAM,CAAC,QAAQ,EAAE;gBACjB,qBAAM,CAAC,YAAY,CAAC,KAAK,CAAC;gBAC1B,qBAAM,CAAC,YAAY,CAAC,CAAC,CAAC;gBACtB,qBAAM,CAAC,UAAU,EAAE;aACpB;YACD,IAAI,EAAE,oBAAK,CAAC,YAAY;SACzB;KACF;IACD,SAAS,EAAE;QACT,KAAK,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,EAAE;KAClE;CACF,CAAC,CAAC;AAEH,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE;IACjB,EAAE,CAAC,+EAA+E,EAAE,KAAK,IAAI,EAAE;QAC7F,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;QAE5D,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjD,MAAM,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACxB,MAAM,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAEzB,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACtC,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAClC,MAAM,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAExB,MAAM,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;QAC5C,kCAAkC;QAClC,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAE5B,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAExB,6BAA6B;QAE7B,MAAM,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC;QAEtD,MAAM,EAAE,CAAC;QACT,YAAY;QACZ,MAAM,EAAE,CAAC;QAET,MAAM,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import { caseFixture, traits, locks } from \"../testkit/case-fixture\";\n\njest.setTimeout(180_000);\n\nconst test = caseFixture(\"Defer\", {\n handlers: {\n Deposited: {\n chain: [\n traits.Context(),\n traits.Transaction(),\n traits.Suspense(),\n traits.ClaimTimeout(2_000),\n traits.IsolateAfter(1),\n traits.Sequential(),\n ],\n lock: locks.wholeAccount,\n },\n },\n projector: {\n retry: { attempts: 10, minDelay: 100, maxDelay: 100, backoff: 1 },\n },\n});\n\ntest.describe(() => {\n it(\"will allow more breathing time if a previous locking event is being processed\", async () => {\n const { events, act, control, assert } = await test.setup();\n\n const [account, opened] = events.open(test.name);\n await act.save(account);\n await act.handle(opened);\n\n const failing = account.deposit(1000);\n const next = account.deposit(100);\n await act.save(account);\n\n const unmark = control.markFailing(failing);\n // const h1 = act.handle(failing);\n const h2 = act.handle(next);\n\n await control.wait(100);\n\n // await control.wait(2_000);\n\n await assert.cashflow(account.id).toHave({ flow: 0 });\n\n unmark();\n // await h1;\n await h2;\n\n await assert.cashflow(account.id).toHave({ flow: 1100 });\n });\n});\n"]}
|
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const case_fixture_1 = require("../testkit/case-fixture");
|
|
4
|
-
jest.setTimeout(60_000);
|
|
5
|
-
const test = (0, case_fixture_1.caseFixture)("Lock", {
|
|
6
|
-
handlers: {
|
|
7
|
-
AccountOpened: {
|
|
8
|
-
chain: [
|
|
9
|
-
case_fixture_1.traits.Context(),
|
|
10
|
-
case_fixture_1.traits.Transaction(),
|
|
11
|
-
case_fixture_1.traits.Suspense(),
|
|
12
|
-
case_fixture_1.traits.Delay(1000), // to ensure that deposit waits for it
|
|
13
|
-
case_fixture_1.traits.Parallel(),
|
|
14
|
-
],
|
|
15
|
-
lock: case_fixture_1.locks.wholeAccount,
|
|
16
|
-
},
|
|
17
|
-
Deposited: {
|
|
18
|
-
chain: [
|
|
19
|
-
case_fixture_1.traits.Context(),
|
|
20
|
-
case_fixture_1.traits.Transaction(),
|
|
21
|
-
case_fixture_1.traits.Suspense(),
|
|
22
|
-
case_fixture_1.traits.Parallel(),
|
|
23
|
-
],
|
|
24
|
-
lock: case_fixture_1.locks.accountAndEventId, // allows parallel deposits
|
|
25
|
-
},
|
|
26
|
-
Withdrawn: {
|
|
27
|
-
chain: [
|
|
28
|
-
case_fixture_1.traits.Context(),
|
|
29
|
-
case_fixture_1.traits.Transaction(),
|
|
30
|
-
case_fixture_1.traits.Suspense(),
|
|
31
|
-
case_fixture_1.traits.Sequential(),
|
|
32
|
-
],
|
|
33
|
-
lock: case_fixture_1.locks.wholeAccount, // forces sequential withdrawals
|
|
34
|
-
},
|
|
35
|
-
},
|
|
36
|
-
});
|
|
37
|
-
test.describe(() => {
|
|
38
|
-
it("processes deposit after opened resumes", async () => {
|
|
39
|
-
const { events, act, assert } = await test.setup();
|
|
40
|
-
const [account] = events.open(test.name);
|
|
41
|
-
const deposit = account.deposit(100);
|
|
42
|
-
await act.save(account);
|
|
43
|
-
await act.handle(deposit);
|
|
44
|
-
await assert.cashflow(account.id).toHave({ id: account.id, flow: 100 });
|
|
45
|
-
});
|
|
46
|
-
it("processes deposits in parallel, but withdrawals sequentially", async () => {
|
|
47
|
-
const { events, act, assert } = await test.setup();
|
|
48
|
-
const [account, opened] = events.open(test.name);
|
|
49
|
-
const deposits = new Map();
|
|
50
|
-
while (deposits.size < 100) {
|
|
51
|
-
const deposit = account.deposit(deposits.size);
|
|
52
|
-
deposits.set(deposit.id.serialize(), deposit);
|
|
53
|
-
}
|
|
54
|
-
// await act.save(account);
|
|
55
|
-
const withdrawals = new Map();
|
|
56
|
-
while (withdrawals.size < 50) {
|
|
57
|
-
const withdrawal = account.withdraw(withdrawals.size);
|
|
58
|
-
withdrawals.set(withdrawal.id.serialize(), withdrawal);
|
|
59
|
-
}
|
|
60
|
-
const last = account.deposit(50);
|
|
61
|
-
await act.save(account);
|
|
62
|
-
await act.handle(last);
|
|
63
|
-
const cashflow = await assert.fetch(account.id);
|
|
64
|
-
const trace = cashflow?.ops_trace.map((id) => id.serialize());
|
|
65
|
-
const report = trace?.map((id) => id === opened.id.serialize()
|
|
66
|
-
? "O"
|
|
67
|
-
: deposits.has(id)
|
|
68
|
-
? "D"
|
|
69
|
-
: withdrawals.has(id)
|
|
70
|
-
? "W"
|
|
71
|
-
: "L");
|
|
72
|
-
// For debugging:
|
|
73
|
-
// const report = trace?.map((id) =>
|
|
74
|
-
// id === opened.id.serialize()
|
|
75
|
-
// ? "O"
|
|
76
|
-
// : deposits.has(id)
|
|
77
|
-
// ? `D ${deposits.get(id).payload.amount} ${id}`
|
|
78
|
-
// : withdrawals.has(id)
|
|
79
|
-
// ? `W ${withdrawals.get(id).payload.amount} ${id}`
|
|
80
|
-
// : "L",
|
|
81
|
-
// );
|
|
82
|
-
expect(report).toEqual([
|
|
83
|
-
"O",
|
|
84
|
-
// 0-99 deposits in any order
|
|
85
|
-
...Array.from({ length: 100 }, () => "D"),
|
|
86
|
-
...Array.from({ length: 50 }, () => "W"),
|
|
87
|
-
"L", // last deposit
|
|
88
|
-
]);
|
|
89
|
-
});
|
|
90
|
-
});
|
|
91
|
-
//# sourceMappingURL=lock.spec.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"lock.spec.js","sourceRoot":"","sources":["../../../src/projection/cases/lock.spec.ts"],"names":[],"mappings":"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","sourcesContent":["import { caseFixture, locks, traits } from \"../testkit/case-fixture\";\n\njest.setTimeout(60_000);\n\nconst test = caseFixture(\"Lock\", {\n handlers: {\n AccountOpened: {\n chain: [\n traits.Context(),\n traits.Transaction(),\n traits.Suspense(),\n traits.Delay(1000), // to ensure that deposit waits for it\n traits.Parallel(),\n ],\n lock: locks.wholeAccount,\n },\n Deposited: {\n chain: [\n traits.Context(),\n traits.Transaction(),\n traits.Suspense(),\n traits.Parallel(),\n ],\n lock: locks.accountAndEventId, // allows parallel deposits\n },\n Withdrawn: {\n chain: [\n traits.Context(),\n traits.Transaction(),\n traits.Suspense(),\n traits.Sequential(),\n ],\n lock: locks.wholeAccount, // forces sequential withdrawals\n },\n },\n});\n\ntest.describe(() => {\n it(\"processes deposit after opened resumes\", async () => {\n const { events, act, assert } = await test.setup();\n\n const [account] = events.open(test.name);\n const deposit = account.deposit(100);\n await act.save(account);\n\n await act.handle(deposit);\n\n await assert.cashflow(account.id).toHave({ id: account.id, flow: 100 });\n });\n\n it(\"processes deposits in parallel, but withdrawals sequentially\", async () => {\n const { events, act, assert } = await test.setup();\n\n const [account, opened] = events.open(test.name);\n\n const deposits = new Map();\n while (deposits.size < 100) {\n const deposit = account.deposit(deposits.size);\n deposits.set(deposit.id.serialize(), deposit);\n }\n\n // await act.save(account);\n\n const withdrawals = new Map();\n while (withdrawals.size < 50) {\n const withdrawal = account.withdraw(withdrawals.size);\n withdrawals.set(withdrawal.id.serialize(), withdrawal);\n }\n\n const last = account.deposit(50);\n\n await act.save(account);\n await act.handle(last);\n\n const cashflow = await assert.fetch(account.id);\n\n const trace = cashflow?.ops_trace.map((id) => id.serialize());\n\n const report = trace?.map((id) =>\n id === opened.id.serialize()\n ? \"O\"\n : deposits.has(id)\n ? \"D\"\n : withdrawals.has(id)\n ? \"W\"\n : \"L\",\n );\n\n // For debugging:\n // const report = trace?.map((id) =>\n // id === opened.id.serialize()\n // ? \"O\"\n // : deposits.has(id)\n // ? `D ${deposits.get(id).payload.amount} ${id}`\n // : withdrawals.has(id)\n // ? `W ${withdrawals.get(id).payload.amount} ${id}`\n // : \"L\",\n // );\n\n expect(report).toEqual([\n \"O\",\n // 0-99 deposits in any order\n ...Array.from({ length: 100 }, () => \"D\"),\n ...Array.from({ length: 50 }, () => \"W\"),\n \"L\", // last deposit\n ]);\n });\n});\n"]}
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const case_fixture_1 = require("../testkit/case-fixture");
|
|
4
|
-
jest.setTimeout(20_000);
|
|
5
|
-
const test = (0, case_fixture_1.caseFixture)("Skip", {
|
|
6
|
-
projector: {
|
|
7
|
-
// unclaimOnFailure: false,
|
|
8
|
-
retry: { attempts: 10, minDelay: 100, maxDelay: 100 },
|
|
9
|
-
},
|
|
10
|
-
handlers: {
|
|
11
|
-
Deposited: {
|
|
12
|
-
chain: [
|
|
13
|
-
case_fixture_1.traits.Context(),
|
|
14
|
-
case_fixture_1.traits.Transaction(),
|
|
15
|
-
case_fixture_1.traits.SkipAfter(5),
|
|
16
|
-
case_fixture_1.traits.Suspense(),
|
|
17
|
-
case_fixture_1.traits.Sequential(),
|
|
18
|
-
],
|
|
19
|
-
// Ensures that a failing event will block all next ones
|
|
20
|
-
lock: case_fixture_1.locks.wholeAccount,
|
|
21
|
-
},
|
|
22
|
-
},
|
|
23
|
-
});
|
|
24
|
-
test.describe(() => {
|
|
25
|
-
it("skips failing event and processes others", async () => {
|
|
26
|
-
const { events, act, control, assert } = await test.setup();
|
|
27
|
-
const [account, opened] = events.open(test.name);
|
|
28
|
-
await act.save(account);
|
|
29
|
-
await act.handle(opened);
|
|
30
|
-
const failing = account.deposit(1000);
|
|
31
|
-
await act.save(account);
|
|
32
|
-
const reallow = control.markFailing(failing);
|
|
33
|
-
await act.handle(failing).catch(() => { });
|
|
34
|
-
await assert.cashflow(account.id).toHave({ flow: 0 });
|
|
35
|
-
const d1 = account.deposit(100);
|
|
36
|
-
await act.save(account);
|
|
37
|
-
await act.handle(d1);
|
|
38
|
-
const w1 = account.withdraw(50);
|
|
39
|
-
await act.save(account);
|
|
40
|
-
await act.handle(w1);
|
|
41
|
-
await assert.cashflow(account.id).toHave({ flow: 150 });
|
|
42
|
-
reallow();
|
|
43
|
-
});
|
|
44
|
-
});
|
|
45
|
-
const testunclaim = (0, case_fixture_1.caseFixture)("SkipClaim", {
|
|
46
|
-
projector: {
|
|
47
|
-
unclaimOnFailure: false,
|
|
48
|
-
// unclaimOnFailure: false,
|
|
49
|
-
retry: { attempts: 10, minDelay: 100, maxDelay: 100 },
|
|
50
|
-
},
|
|
51
|
-
handlers: {
|
|
52
|
-
Deposited: {
|
|
53
|
-
chain: [
|
|
54
|
-
case_fixture_1.traits.Context(),
|
|
55
|
-
case_fixture_1.traits.Transaction(),
|
|
56
|
-
case_fixture_1.traits.SkipAfter(5),
|
|
57
|
-
case_fixture_1.traits.Suspense(),
|
|
58
|
-
case_fixture_1.traits.Sequential(),
|
|
59
|
-
],
|
|
60
|
-
// Ensures that a failing event will block all next ones
|
|
61
|
-
lock: case_fixture_1.locks.wholeAccount,
|
|
62
|
-
},
|
|
63
|
-
},
|
|
64
|
-
});
|
|
65
|
-
testunclaim.describe(() => {
|
|
66
|
-
it("skips failing event and processes others", async () => {
|
|
67
|
-
const { events, act, control, assert } = await test.setup();
|
|
68
|
-
const [account, opened] = events.open(test.name);
|
|
69
|
-
await act.save(account);
|
|
70
|
-
await act.handle(opened);
|
|
71
|
-
const failing = account.deposit(1000);
|
|
72
|
-
await act.save(account);
|
|
73
|
-
const reallow = control.markFailing(failing);
|
|
74
|
-
await act.handle(failing).catch(() => { });
|
|
75
|
-
await assert.cashflow(account.id).toHave({ flow: 0 });
|
|
76
|
-
const d1 = account.deposit(100);
|
|
77
|
-
await act.save(account);
|
|
78
|
-
await act.handle(d1);
|
|
79
|
-
const w1 = account.withdraw(50);
|
|
80
|
-
await act.save(account);
|
|
81
|
-
await act.handle(w1);
|
|
82
|
-
await assert.cashflow(account.id).toHave({ flow: 150 });
|
|
83
|
-
reallow();
|
|
84
|
-
});
|
|
85
|
-
});
|
|
86
|
-
//# sourceMappingURL=skip.spec.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"skip.spec.js","sourceRoot":"","sources":["../../../src/projection/cases/skip.spec.ts"],"names":[],"mappings":";;AAAA,0DAAqE;AAErE,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;AAExB,MAAM,IAAI,GAAG,IAAA,0BAAW,EAAC,MAAM,EAAE;IAC/B,SAAS,EAAE;QACT,2BAA2B;QAC3B,KAAK,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,EAAE;KACtD;IACD,QAAQ,EAAE;QACR,SAAS,EAAE;YACT,KAAK,EAAE;gBACL,qBAAM,CAAC,OAAO,EAAE;gBAChB,qBAAM,CAAC,WAAW,EAAE;gBACpB,qBAAM,CAAC,SAAS,CAAC,CAAC,CAAC;gBACnB,qBAAM,CAAC,QAAQ,EAAE;gBACjB,qBAAM,CAAC,UAAU,EAAE;aACpB;YACD,wDAAwD;YACxD,IAAI,EAAE,oBAAK,CAAC,YAAY;SACzB;KACF;CACF,CAAC,CAAC;AAEH,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE;IACjB,EAAE,CAAC,0CAA0C,EAAE,KAAK,IAAI,EAAE;QACxD,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;QAE5D,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjD,MAAM,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACxB,MAAM,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAEzB,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACtC,MAAM,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAExB,MAAM,OAAO,GAAG,OAAO,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;QAE7C,MAAM,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QAE1C,MAAM,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC;QAEtD,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAChC,MAAM,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACxB,MAAM,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAErB,MAAM,EAAE,GAAG,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QAChC,MAAM,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACxB,MAAM,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAErB,MAAM,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;QAExD,OAAO,EAAE,CAAC;IACZ,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,MAAM,WAAW,GAAG,IAAA,0BAAW,EAAC,WAAW,EAAE;IAC3C,SAAS,EAAE;QACT,gBAAgB,EAAE,KAAK;QACvB,2BAA2B;QAC3B,KAAK,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,EAAE;KACtD;IACD,QAAQ,EAAE;QACR,SAAS,EAAE;YACT,KAAK,EAAE;gBACL,qBAAM,CAAC,OAAO,EAAE;gBAChB,qBAAM,CAAC,WAAW,EAAE;gBACpB,qBAAM,CAAC,SAAS,CAAC,CAAC,CAAC;gBACnB,qBAAM,CAAC,QAAQ,EAAE;gBACjB,qBAAM,CAAC,UAAU,EAAE;aACpB;YACD,wDAAwD;YACxD,IAAI,EAAE,oBAAK,CAAC,YAAY;SACzB;KACF;CACF,CAAC,CAAC;AAEH,WAAW,CAAC,QAAQ,CAAC,GAAG,EAAE;IACxB,EAAE,CAAC,0CAA0C,EAAE,KAAK,IAAI,EAAE;QACxD,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;QAE5D,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjD,MAAM,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACxB,MAAM,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAEzB,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACtC,MAAM,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAExB,MAAM,OAAO,GAAG,OAAO,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;QAE7C,MAAM,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QAE1C,MAAM,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC;QAEtD,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAChC,MAAM,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACxB,MAAM,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAErB,MAAM,EAAE,GAAG,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QAChC,MAAM,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACxB,MAAM,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAErB,MAAM,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;QAExD,OAAO,EAAE,CAAC;IACZ,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import { caseFixture, locks, traits } from \"../testkit/case-fixture\";\n\njest.setTimeout(20_000);\n\nconst test = caseFixture(\"Skip\", {\n projector: {\n // unclaimOnFailure: false,\n retry: { attempts: 10, minDelay: 100, maxDelay: 100 },\n },\n handlers: {\n Deposited: {\n chain: [\n traits.Context(),\n traits.Transaction(),\n traits.SkipAfter(5),\n traits.Suspense(),\n traits.Sequential(),\n ],\n // Ensures that a failing event will block all next ones\n lock: locks.wholeAccount,\n },\n },\n});\n\ntest.describe(() => {\n it(\"skips failing event and processes others\", async () => {\n const { events, act, control, assert } = await test.setup();\n\n const [account, opened] = events.open(test.name);\n await act.save(account);\n await act.handle(opened);\n\n const failing = account.deposit(1000);\n await act.save(account);\n\n const reallow = control.markFailing(failing);\n\n await act.handle(failing).catch(() => {});\n\n await assert.cashflow(account.id).toHave({ flow: 0 });\n\n const d1 = account.deposit(100);\n await act.save(account);\n await act.handle(d1);\n\n const w1 = account.withdraw(50);\n await act.save(account);\n await act.handle(w1);\n\n await assert.cashflow(account.id).toHave({ flow: 150 });\n\n reallow();\n });\n});\n\nconst testunclaim = caseFixture(\"SkipClaim\", {\n projector: {\n unclaimOnFailure: false,\n // unclaimOnFailure: false,\n retry: { attempts: 10, minDelay: 100, maxDelay: 100 },\n },\n handlers: {\n Deposited: {\n chain: [\n traits.Context(),\n traits.Transaction(),\n traits.SkipAfter(5),\n traits.Suspense(),\n traits.Sequential(),\n ],\n // Ensures that a failing event will block all next ones\n lock: locks.wholeAccount,\n },\n },\n});\n\ntestunclaim.describe(() => {\n it(\"skips failing event and processes others\", async () => {\n const { events, act, control, assert } = await test.setup();\n\n const [account, opened] = events.open(test.name);\n await act.save(account);\n await act.handle(opened);\n\n const failing = account.deposit(1000);\n await act.save(account);\n\n const reallow = control.markFailing(failing);\n\n await act.handle(failing).catch(() => {});\n\n await assert.cashflow(account.id).toHave({ flow: 0 });\n\n const d1 = account.deposit(100);\n await act.save(account);\n await act.handle(d1);\n\n const w1 = account.withdraw(50);\n await act.save(account);\n await act.handle(w1);\n\n await assert.cashflow(account.id).toHave({ flow: 150 });\n\n reallow();\n });\n});\n"]}
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const case_fixture_1 = require("../testkit/case-fixture");
|
|
4
|
-
jest.setTimeout(180_000);
|
|
5
|
-
const test = (0, case_fixture_1.caseFixture)("Stress", {
|
|
6
|
-
handlers: {
|
|
7
|
-
AccountOpened: {
|
|
8
|
-
chain: [case_fixture_1.traits.Context(), case_fixture_1.traits.Transaction(), case_fixture_1.traits.Parallel()],
|
|
9
|
-
lock: case_fixture_1.locks.wholeAccount,
|
|
10
|
-
},
|
|
11
|
-
Deposited: {
|
|
12
|
-
chain: [case_fixture_1.traits.Context(), case_fixture_1.traits.Parallel(), case_fixture_1.traits.Transaction()],
|
|
13
|
-
lock: case_fixture_1.locks.accountAndEventId, // allows parallel deposits
|
|
14
|
-
},
|
|
15
|
-
Withdrawn: {
|
|
16
|
-
chain: [case_fixture_1.traits.Context(), case_fixture_1.traits.Parallel(), case_fixture_1.traits.Transaction()],
|
|
17
|
-
lock: case_fixture_1.locks.accountAndEventId, // forces sequential withdrawals
|
|
18
|
-
},
|
|
19
|
-
},
|
|
20
|
-
});
|
|
21
|
-
class SeededRandom {
|
|
22
|
-
seed;
|
|
23
|
-
constructor(seed) {
|
|
24
|
-
this.seed = seed;
|
|
25
|
-
}
|
|
26
|
-
next() {
|
|
27
|
-
this.seed = (this.seed * 9301 + 49297) % 233280;
|
|
28
|
-
return this.seed / 233280;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
test.describe(() => {
|
|
32
|
-
it("processes a realistic mix of operations deterministically", async () => {
|
|
33
|
-
const { events, act, cashflowStore } = await test.setup();
|
|
34
|
-
const [account, opened] = events.open(test.name);
|
|
35
|
-
await act.save(account);
|
|
36
|
-
await act.handle(opened);
|
|
37
|
-
const rng = new SeededRandom(12345);
|
|
38
|
-
const ops = 100;
|
|
39
|
-
let expectedFlow = 0;
|
|
40
|
-
let renameCount = 0;
|
|
41
|
-
const toHandle = [];
|
|
42
|
-
for (let i = 0; i < ops; i++) {
|
|
43
|
-
const r = rng.next();
|
|
44
|
-
if (r < 0.6) {
|
|
45
|
-
const amount = Math.floor(rng.next() * 500) + 10;
|
|
46
|
-
expectedFlow += amount;
|
|
47
|
-
toHandle.push(account.deposit(amount));
|
|
48
|
-
}
|
|
49
|
-
else if (r < 0.85 && expectedFlow > 50) {
|
|
50
|
-
const maxWithdraw = Math.min(expectedFlow - 10, 200);
|
|
51
|
-
const amount = Math.floor(rng.next() * maxWithdraw) + 1;
|
|
52
|
-
expectedFlow += amount;
|
|
53
|
-
toHandle.push(account.withdraw(amount));
|
|
54
|
-
}
|
|
55
|
-
else {
|
|
56
|
-
const nameId = renameCount.toString().padStart(3, "0");
|
|
57
|
-
toHandle.push(account.rename(`WorkflowName_${nameId}`));
|
|
58
|
-
renameCount++;
|
|
59
|
-
}
|
|
60
|
-
// if (i % 25 === 24) {
|
|
61
|
-
// await act.save(account);
|
|
62
|
-
// }
|
|
63
|
-
}
|
|
64
|
-
await act.save(account);
|
|
65
|
-
await Promise.all(toHandle.map((e) => act.handle(e)));
|
|
66
|
-
// await new Promise((r) => setTimeout(r, 5000)); // Wait for eventual consistency
|
|
67
|
-
const cashflow = await cashflowStore.load(account.id);
|
|
68
|
-
expect(cashflow).toBeDefined();
|
|
69
|
-
expect(cashflow.flow).toBe(expectedFlow);
|
|
70
|
-
expect(cashflow.name).toBe(`WorkflowName_${(renameCount - 1).toString().padStart(3, "0")}`);
|
|
71
|
-
});
|
|
72
|
-
});
|
|
73
|
-
//# sourceMappingURL=stress.spec.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"stress.spec.js","sourceRoot":"","sources":["../../../src/projection/cases/stress.spec.ts"],"names":[],"mappings":"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","sourcesContent":["import { caseFixture, locks, traits } from \"../testkit/case-fixture\";\n\njest.setTimeout(180_000);\n\nconst test = caseFixture(\"Stress\", {\n handlers: {\n AccountOpened: {\n chain: [traits.Context(), traits.Transaction(), traits.Parallel()],\n lock: locks.wholeAccount,\n },\n Deposited: {\n chain: [traits.Context(), traits.Parallel(), traits.Transaction()],\n lock: locks.accountAndEventId, // allows parallel deposits\n },\n Withdrawn: {\n chain: [traits.Context(), traits.Parallel(), traits.Transaction()],\n lock: locks.accountAndEventId, // forces sequential withdrawals\n },\n },\n});\n\nclass SeededRandom {\n private seed: number;\n constructor(seed: number) {\n this.seed = seed;\n }\n next(): number {\n this.seed = (this.seed * 9301 + 49297) % 233280;\n return this.seed / 233280;\n }\n}\n\ntest.describe(() => {\n it(\"processes a realistic mix of operations deterministically\", async () => {\n const { events, act, cashflowStore } = await test.setup();\n\n const [account, opened] = events.open(test.name);\n await act.save(account);\n await act.handle(opened);\n\n const rng = new SeededRandom(12345);\n const ops = 100;\n let expectedFlow = 0;\n let renameCount = 0;\n\n const toHandle: any[] = [];\n for (let i = 0; i < ops; i++) {\n const r = rng.next();\n if (r < 0.6) {\n const amount = Math.floor(rng.next() * 500) + 10;\n expectedFlow += amount;\n toHandle.push(account.deposit(amount));\n } else if (r < 0.85 && expectedFlow > 50) {\n const maxWithdraw = Math.min(expectedFlow - 10, 200);\n const amount = Math.floor(rng.next() * maxWithdraw) + 1;\n expectedFlow += amount;\n toHandle.push(account.withdraw(amount));\n } else {\n const nameId = renameCount.toString().padStart(3, \"0\");\n toHandle.push(account.rename(`WorkflowName_${nameId}`));\n renameCount++;\n }\n // if (i % 25 === 24) {\n // await act.save(account);\n // }\n }\n await act.save(account);\n\n await Promise.all(toHandle.map((e) => act.handle(e)));\n\n // await new Promise((r) => setTimeout(r, 5000)); // Wait for eventual consistency\n\n const cashflow = await cashflowStore.load(account.id);\n expect(cashflow).toBeDefined();\n expect(cashflow!.flow).toBe(expectedFlow);\n expect(cashflow!.name).toBe(\n `WorkflowName_${(renameCount - 1).toString().padStart(3, \"0\")}`,\n );\n });\n});\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"firestore.projector.js","sourceRoot":"","sources":["../../src/projection/firestore.projector.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n FieldValue,\n Firestore,\n Timestamp,\n WriteBatch,\n} from \"firebase-admin/firestore\";\nimport {\n IEsEvent,\n ISavedChange,\n EventId,\n ProjectedStreamReader,\n Cursor,\n CheckpointId,\n ESProjection,\n ProjectedStream,\n IFact,\n Serialized,\n Lock,\n} from \"@ddd-ts/core\";\nimport { MicrosecondTimestamp, Optional, Shape } from \"@ddd-ts/shape\";\nimport {\n DefaultConverter,\n FirestoreTransaction,\n} from \"@ddd-ts/store-firestore\";\n\nconst Status = {\n SUCCESS: \"OK\",\n FAILURE: \"FAILURE\",\n DEFERRED: \"DEFERRED\",\n} as const;\ntype Status = (typeof Status)[keyof typeof Status];\n\nconst TaskState = {\n ENQUEUED: \"ENQUEUED\",\n PROCESSED: \"PROCESSED\",\n MISSING: \"MISSING\",\n} as const;\ntype TaskState = (typeof TaskState)[keyof typeof TaskState];\n\nconst wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));\n\nconst RETENTION = MicrosecondTimestamp.MONTH;\n\nexport class FirestoreProjector {\n _unclaim = true;\n\n constructor(\n public readonly projection: ESProjection<IEsEvent>,\n public readonly reader: ProjectedStreamReader<IEsEvent>,\n public readonly queue: FirestoreQueueStore,\n public config = {\n retry: { attempts: 10, minDelay: 10, maxDelay: 200, backoff: 1.5 },\n enqueue: { batchSize: 100 },\n onProcessError: (error: Error) => {\n console.error(\"Error processing event:\", error);\n },\n onEnqueueError: (error: Error) => {\n console.error(\"Error enqueuing tasks:\", error);\n },\n },\n ) {}\n\n async *breathe() {\n const { attempts, minDelay, maxDelay, backoff } = this.config.retry;\n\n for (let i = 0; i < attempts; i++) {\n const reset = () => {\n i--;\n };\n\n yield [i, reset] as const;\n\n const margin = maxDelay - minDelay;\n const jitter = Math.random() * margin;\n\n const backedoff = (backoff * i + 1) * minDelay;\n const jitteredDelay = backedoff + jitter;\n\n await wait(jitteredDelay);\n }\n }\n\n // @Trace(\"projector.handle\", ($, e) => ({\n // eventId: e.id.serialize(),\n // eventName: e.name,\n // eventRevision: e.revision,\n // eventReference: e.ref,\n // projectionName: $.projection.constructor.name,\n // checkpointId: $.projection.getCheckpointId(e).serialize(),\n // }))\n async handle(savedChange: ISavedChange<IEsEvent>) {\n const checkpointId = this.projection.getCheckpointId(savedChange);\n const target = await this.getCursor(savedChange);\n\n if (!target) {\n throw new Error(\n `Cursor not found for event ${savedChange.id.serialize()}`,\n );\n }\n\n const errors = [];\n\n for await (const [attempt, reset] of this.breathe()) {\n // console.log(`Attempt ${attempt} for event ${savedChange.id.serialize()}`);\n const source = this.projection.getSource(savedChange);\n const [status, message] = await this.attempt(\n source,\n checkpointId,\n target,\n );\n\n if (status === Status.DEFERRED) {\n reset();\n continue;\n }\n\n if (status === Status.SUCCESS) {\n await this.queue.cleanup(checkpointId);\n return;\n }\n\n errors.push(message);\n }\n\n throw new Error(\n `Failed to handle event ${savedChange.id.serialize()}: ${errors.join(\", \")}`,\n );\n }\n\n // @Trace(\"projector.reader.getCursor\")\n private async getCursor(savedChange: ISavedChange<IEsEvent>) {\n return await this.reader.getCursor(savedChange);\n }\n\n // @Trace(\"projector.attempt\")\n private async attempt(\n source: ProjectedStream,\n checkpointId: CheckpointId,\n target: Cursor,\n ) {\n const headCursor = await this.getQueueHead(checkpointId);\n\n const isTargetAfterHead = !headCursor || target.isAfter(headCursor);\n\n if (isTargetAfterHead) {\n const [status, message] = await this.enqueue(\n source,\n headCursor,\n checkpointId,\n target,\n );\n if (status === Status.DEFERRED) {\n return [Status.DEFERRED, message] as const;\n }\n }\n\n if (!isTargetAfterHead) {\n const processed = await this.checkIsProcessed(checkpointId, target);\n if (processed === TaskState.PROCESSED) {\n return [Status.SUCCESS, \"Target event already processed\"] as const;\n }\n\n if (processed === TaskState.MISSING) {\n const [status, message] = await this.enqueueOne(checkpointId, target);\n if (status === Status.DEFERRED) {\n return [Status.FAILURE, message] as const;\n }\n }\n }\n\n const unprocessed = await this.getUnprocessed(checkpointId);\n\n if (!unprocessed.length) {\n return [Status.FAILURE, \"No unprocessed tasks found\"] as const;\n }\n\n const batch = Task.batch(unprocessed);\n\n if (!batch.length) {\n return [\n Status.DEFERRED,\n \"No tasks available to claim, deferring\",\n ] as const;\n }\n\n const claimer = ClaimerId.generate();\n\n const [status, message] = await this.claimTasks(\n checkpointId,\n claimer,\n batch,\n );\n\n if (status === Status.FAILURE) {\n return [Status.DEFERRED, message] as const;\n }\n\n return await this.processEvents(checkpointId, claimer, target.eventId);\n }\n\n // @Trace(\"projector.queue.head\")\n private async getQueueHead(checkpointId: CheckpointId) {\n return await this.queue.head(checkpointId);\n }\n\n // @Trace(\"projector.readSourceStream\")\n private async readSourceStream(\n source: ProjectedStream,\n head: Cursor | undefined,\n checkpointId: CheckpointId,\n target: Cursor,\n ) {\n const shard = checkpointId.shard();\n const headCursor = head;\n const limit = this.config.enqueue.batchSize;\n return this.reader.slice(source, shard, headCursor, target, limit);\n }\n\n // @Trace(\"projector.enqueue\")\n private async enqueue(\n source: ProjectedStream,\n head: Cursor | undefined,\n checkpointId: CheckpointId,\n target: Cursor,\n ) {\n const events = await this.readSourceStream(\n source,\n head,\n checkpointId,\n target,\n );\n\n const tasks = events.map((e) => {\n const settings = this.projection.getTaskSettings(e);\n return Task.new(e, settings);\n });\n\n return await this.queue.enqueue(checkpointId, tasks);\n }\n\n private async enqueueOne(checkpointId: CheckpointId, target: Cursor) {\n const event = await this.reader.get(target);\n if (!event) {\n throw new Error(`Event not found for cursor ${target.ref}`);\n }\n\n const settings = this.projection.getTaskSettings(event);\n const task = Task.new(event, settings);\n return await this.queue.enqueue(checkpointId, [task]);\n }\n\n // @Trace(\"projector.queue.isProcessed\")\n private async checkIsProcessed(checkpointId: CheckpointId, cursor: Cursor) {\n return await this.queue.isProcessed(checkpointId, cursor);\n }\n\n // @Trace(\"projector.queue.unprocessed\")\n private async getUnprocessed(checkpointId: CheckpointId) {\n return await this.queue.unprocessed(checkpointId);\n }\n\n // @Trace(\"projector.queue.claim\")\n private async claimTasks(\n checkpointId: CheckpointId,\n claimer: ClaimerId,\n batch: Task<true>[],\n ) {\n try {\n await this.queue.claim(checkpointId, claimer, batch);\n return [Status.SUCCESS, \"Tasks claimed successfully\"] as const;\n } catch (e) {\n return [Status.FAILURE, e] as const;\n }\n }\n\n // @Trace(\"projector.processEvents\")\n private async processEvents(\n checkpointId: CheckpointId,\n claimer: ClaimerId,\n targetEventId: EventId,\n ) {\n const tasks = await this.queue.claimed(checkpointId, claimer);\n const todo = await Promise.all(tasks.map((t) => this.reader.get(t.cursor)));\n const filtered = todo.filter((t) => !!t) as IEsEvent[];\n\n if (!filtered.length) {\n // Nothing to process, possibly all tasks were for events that no longer exist\n return [\n Status.DEFERRED,\n \"No events to process in claimed tasks\",\n ] as const;\n }\n\n const onProcessed = this.queue.processed.bind(this.queue);\n const context = { onProcessed, checkpointId };\n\n const hasTarget = tasks.some((t) => t.id.equals(targetEventId));\n\n try {\n const processed = await this.projection.process(filtered, context);\n\n if (processed.some((id) => id?.equals(targetEventId))) {\n return [Status.SUCCESS, \"Target event processed successfully\"] as const;\n }\n\n return [Status.DEFERRED, \"Target event not processed yet\"] as const;\n } catch (e) {\n this.config.onProcessError(e as Error);\n if (this._unclaim) {\n await this.queue.unclaim(checkpointId, tasks);\n }\n\n if (!hasTarget) {\n return [\n Status.DEFERRED,\n \"Target event not in claimed batch, deferring\",\n ] as const;\n }\n\n return [Status.FAILURE, e] as const;\n }\n }\n}\n\nexport class AlreadyEnqueuedError extends Error {\n constructor() {\n super(\"Tasks already enqueued\");\n this.name = \"AlreadyEnqueuedError\";\n }\n}\n\nexport class FirestoreQueueStore {\n converter = new DefaultConverter();\n collection: FirebaseFirestore.CollectionReference;\n\n constructor(public db: Firestore) {\n this.collection = db.collection(\n \"checkpoints\",\n ) as FirebaseFirestore.CollectionReference;\n }\n\n private timestampToMicroseconds(timestamp: Timestamp): MicrosecondTimestamp {\n const microseconds =\n BigInt(timestamp.seconds) * BigInt(1_000_000) +\n BigInt(timestamp.nanoseconds) / BigInt(1_000);\n return new MicrosecondTimestamp(microseconds);\n }\n\n private microsecondsToTimestamp(\n microseconds: MicrosecondTimestamp,\n ): Timestamp {\n const seconds = BigInt(microseconds.micros) / 1_000_000n;\n const nanoseconds = (BigInt(microseconds.micros) % 1_000_000n) * 1000n; // Convert to nanoseconds\n return new Timestamp(Number(seconds), Number(nanoseconds));\n }\n\n async enqueue(checkpointId: CheckpointId, tasks: Task<false>[]) {\n // console.log(\n // `Enqueuing ${tasks.length} tasks for checkpoint ${checkpointId.serialize()}`,\n // );\n\n const batch = this.collection.firestore.batch();\n\n for (const task of tasks) {\n const ref = this.queued(checkpointId, task.id);\n batch.create(ref, {\n ref: this.db.doc(task.cursor.ref),\n ...this.converter.toFirestore(task.serialize()),\n });\n }\n\n try {\n await batch.commit();\n return [Status.SUCCESS, \"Tasks enqueued successfully\"] as const;\n } catch (err: any) {\n if (err.code === 6) {\n return [Status.DEFERRED, new AlreadyEnqueuedError()] as const;\n }\n return [Status.DEFERRED, err] as const;\n }\n }\n\n async claim(\n checkpointId: CheckpointId,\n claimer: ClaimerId,\n tasks: Task<true>[],\n ) {\n const batch = this.collection.firestore.batch();\n\n for (const task of tasks) {\n const ref = this.queued(checkpointId, task.id);\n batch.update(\n ref,\n {\n claimer: claimer.serialize(),\n claimedAt: FieldValue.serverTimestamp(),\n attempts: FieldValue.increment(1),\n remaining: FieldValue.increment(-1),\n },\n { lastUpdateTime: this.microsecondsToTimestamp(task.lastUpdateTime) },\n );\n }\n\n await batch.commit();\n }\n\n async head(checkpointId: CheckpointId) {\n const head = this.queue(checkpointId)\n .orderBy(\"occurredAt\", \"desc\")\n .orderBy(\"revision\", \"desc\")\n .limit(1);\n const headDoc = (await head.get()).docs[0];\n if (!headDoc) {\n return undefined;\n }\n\n const headData = this.converter.fromFirestoreSnapshot(headDoc);\n if (!headData) {\n return undefined;\n }\n const headCursor = headDoc\n ? Cursor.deserialize({\n ref: headData.ref,\n occurredAt: headData.occurredAt,\n revision: headData.revision,\n eventId: headData.id,\n })\n : undefined;\n return headCursor;\n }\n\n async unprocessed(checkpointId: CheckpointId) {\n const query = this.queue(checkpointId)\n .where(\"processed\", \"==\", false)\n .where(\"remaining\", \">\", 0)\n .orderBy(\"occurredAt\", \"asc\")\n .orderBy(\"revision\", \"asc\")\n .limit(100);\n\n const snapshot = await query.get();\n const tasks = snapshot.docs.map((doc) => {\n const data = this.converter.fromFirestoreSnapshot(doc);\n const timestamp = doc.updateTime\n ? this.timestampToMicroseconds(doc.updateTime)\n : undefined;\n return Task.deserializeWithLastUpdateTime(data as any, timestamp);\n });\n\n // Check for timeouts and unclaim expired tasks\n const expiredTasks: Task<true>[] = [];\n for (const task of tasks) {\n const originalClaimer = task.claimer;\n task.checkTimeout();\n\n // If timeout cleared the claimer, we need to update the database\n if (originalClaimer && !task.claimer) {\n expiredTasks.push(task);\n }\n }\n\n // Update expired tasks in the database\n if (expiredTasks.length > 0) {\n const batch = this.collection.firestore.batch();\n for (const task of expiredTasks) {\n const ref = this.queued(checkpointId, task.id);\n batch.update(ref, {\n claimer: FieldValue.delete(),\n claimedAt: FieldValue.delete(),\n attempts: task.attempts,\n remaining: task.remaining,\n });\n }\n await batch.commit();\n }\n\n return tasks;\n }\n\n async claimed(\n checkpointId: CheckpointId,\n claimer: ClaimerId,\n ): Promise<Task<true>[]> {\n const query = this.queue(checkpointId)\n .where(\"claimer\", \"==\", claimer.serialize())\n .orderBy(\"occurredAt\", \"asc\")\n .orderBy(\"revision\", \"asc\");\n\n const snapshot = await query.get();\n return snapshot.docs.map((doc) => {\n const data = this.converter.fromFirestoreSnapshot(doc);\n const timestamp = doc.updateTime\n ? this.timestampToMicroseconds(doc.updateTime as Timestamp)\n : undefined;\n return Task.deserializeWithLastUpdateTime(data as any, timestamp);\n });\n }\n\n async unclaim(checkpointId: CheckpointId, tasks: Task<true>[]) {\n const batch = this.collection.firestore.batch();\n\n for (const task of tasks) {\n const ref = this.queued(checkpointId, task.id);\n batch.update(ref, {\n claimer: FieldValue.delete(),\n claimedAt: FieldValue.delete(),\n });\n }\n\n await batch.commit();\n }\n\n /**\n * If the task exists, then looks for a processed flag.\n * If not found, check if the cursor is older than the retention time for processed event.\n * If so, consider it processed.\n * Otherwise, consider it missing.\n */\n async isProcessed(checkpointId: CheckpointId, cursor: Cursor) {\n const doc = await this.queued(checkpointId, cursor.eventId).get();\n if (doc.exists) {\n const data = doc.data();\n if (!data) throw new Error(\"No data in queued document\");\n if (data.processed === true) return TaskState.PROCESSED;\n return TaskState.ENQUEUED;\n }\n\n const lastRetention = MicrosecondTimestamp.now().sub(RETENTION);\n if (cursor.isOlderThan(lastRetention)) {\n return TaskState.PROCESSED;\n }\n return TaskState.MISSING;\n }\n\n checkpoint(id: CheckpointId) {\n return this.collection.doc(id.name).collection(\"shards\");\n }\n\n queue(id: CheckpointId) {\n return this.checkpoint(id).doc(id.shard()).collection(\"queue\");\n }\n\n queued(id: CheckpointId, eventId: EventId) {\n return this.queue(id).doc(eventId.serialize());\n }\n\n async processed(\n id: CheckpointId,\n eventIds: EventId[],\n context: {\n transaction?: FirestoreTransaction;\n batchWriter?: WriteBatch;\n } = {},\n ) {\n const { transaction: trx, batchWriter } = context;\n\n if (trx) {\n for (const eventId of eventIds) {\n const ref = this.queued(id, eventId);\n trx.transaction.update(ref, { processed: true });\n }\n return;\n }\n\n await Promise.all(\n eventIds.map((eventId) =>\n this.queued(id, eventId).update({\n processed: true,\n }),\n ),\n );\n\n return;\n }\n\n async getTailCursor(id: CheckpointId) {\n const tail = this.queue(id)\n .where(\"remaining\", \">\", 0)\n .orderBy(\"occurredAt\", \"asc\")\n .orderBy(\"revision\", \"asc\")\n .limit(1);\n const tailDoc = (await tail.get()).docs[0];\n if (!tailDoc) {\n return undefined;\n }\n\n const tailData = this.converter.fromFirestoreSnapshot(tailDoc);\n if (!tailData) {\n return undefined;\n }\n const tailCursor = tailDoc\n ? Cursor.deserialize({\n ref: tailData.ref,\n occurredAt: tailData.occurredAt,\n revision: tailData.revision,\n eventId: tailData.id,\n })\n : undefined;\n return tailCursor;\n }\n\n async cleanup(id: CheckpointId) {\n const aMonthAgo = MicrosecondTimestamp.now().sub(\n MicrosecondTimestamp.WEEK.mult(4),\n );\n\n const query = this.queue(id)\n .where(\"remaining\", \">\", 0)\n .where(\"occurredAt\", \"<\", aMonthAgo.serialize()) // Only consider events older than 4 weeks\n .orderBy(\"occurredAt\", \"asc\")\n .orderBy(\"revision\", \"asc\");\n\n const MIN_TRAIL = 1; // Keep at least one processed document to maintain the tail cursor\n const TRAIL = MIN_TRAIL + 0; // Extra buffer to optimize isProcessed checks\n\n const snapshot = await query.get();\n\n if (snapshot.size < TRAIL) return;\n\n const stopper = snapshot.docs.findIndex((doc) => !doc.data().processed);\n const cleanable = snapshot.docs.slice(0, stopper);\n\n const cleaning = cleanable.slice(0, cleanable.length - TRAIL);\n if (cleaning.length === 0) return;\n\n const batch = this.collection.firestore.batch();\n for (const queued of cleaning) batch.delete(queued.ref);\n await batch.commit();\n }\n\n async flush(id: CheckpointId) {\n const stream = this.queue(id).stream() as AsyncIterable<any>;\n const writer = this.collection.firestore.bulkWriter();\n for await (const queued of stream) writer.delete(queued.ref);\n await writer.close();\n }\n\n /**\n * This method adds a fake processed event to the queue.\n * It is useful for initializing the tail cursor of a new projection, at the\n * same time as the projection's initial state is created, reset, or updated.\n * By default, it will use the current time as the occurredAt timestamp.\n * You can override this by providing a specific timestamp.\n *\n * This ensures that the projection can start processing new events from the\n * correct point in time, avoiding reprocessing of old events.\n */\n async seed(checkpointId: CheckpointId) {\n const cursor = new Cursor({\n ref: \"seed\",\n occurredAt: MicrosecondTimestamp.now(),\n revision: 0,\n eventId: EventId.generate(),\n });\n\n const task = new Task<false>({\n id: EventId.generate(),\n ref: \"Seed\",\n occurredAt: cursor.occurredAt,\n revision: cursor.revision,\n attempts: 0,\n processed: true,\n claimer: undefined,\n claimedAt: undefined,\n lock: new Lock({}),\n remaining: 1,\n claimTimeout: 0,\n skipAfter: 0,\n isolateAfter: 0,\n lastUpdateTime: undefined,\n });\n\n try {\n const serialized = task.serialize();\n const converted = this.converter.toFirestore(serialized);\n await this.queued(checkpointId, task.cursor.eventId).create(converted);\n } catch (e) {\n // Ignore if already exists\n if (!(e instanceof AlreadyEnqueuedError)) {\n throw e;\n }\n }\n }\n}\n\nexport class ClaimerId extends EventId {}\nexport class Task<Stored extends boolean> extends Shape({\n id: EventId,\n ref: String,\n occurredAt: MicrosecondTimestamp,\n revision: Number,\n attempts: Number,\n processed: Boolean,\n claimer: Optional(String),\n claimedAt: Optional(MicrosecondTimestamp),\n lock: Lock,\n skipAfter: Number,\n remaining: Number,\n isolateAfter: Number,\n claimTimeout: Number,\n lastUpdateTime: Optional(MicrosecondTimestamp),\n}) {\n declare lastUpdateTime: Stored extends true\n ? MicrosecondTimestamp\n : undefined;\n\n get cursor() {\n return new Cursor({\n ref: this.ref,\n occurredAt: this.occurredAt,\n revision: this.revision,\n eventId: this.id,\n });\n }\n\n static new(\n fact: IFact,\n config: {\n lock: Lock;\n claimTimeout: number;\n skipAfter: number;\n isolateAfter: number;\n },\n ): Task<false> {\n return new Task<false>({\n id: fact.id,\n attempts: 0,\n claimer: undefined,\n processed: false,\n claimedAt: undefined,\n lock: config.lock,\n claimTimeout: config.claimTimeout,\n skipAfter: config.skipAfter,\n isolateAfter: config.isolateAfter,\n remaining: config.skipAfter,\n ref: fact.ref,\n revision: fact.revision,\n occurredAt: fact.occurredAt,\n lastUpdateTime: undefined,\n });\n }\n\n get isProcessing() {\n return !!this.claimer;\n }\n\n get isProcessed() {\n return !!this.processed;\n }\n\n get shouldSkip() {\n return this.attempts > this.skipAfter;\n }\n\n get shouldIsolate() {\n return this.attempts > this.isolateAfter;\n }\n\n checkTimeout() {\n if (!this.claimedAt) return;\n const now = MicrosecondTimestamp.now();\n const elapsedMicros = now.micros - this.claimedAt.micros;\n const timeoutMicros = BigInt(this.claimTimeout) * 1000n; // Convert ms to microseconds\n if (elapsedMicros > timeoutMicros) {\n this.claimedAt = undefined;\n this.claimer = undefined;\n this.attempts += 1;\n this.remaining -= 1;\n }\n }\n\n static deserializeWithLastUpdateTime(\n data: Omit<Serialized<Task<true>>, \"lastUpdateTime\">,\n timestamp?: MicrosecondTimestamp,\n ): Task<true> {\n const task = Task.deserialize({\n ...data,\n lastUpdateTime: timestamp as any,\n }) as Task<true>;\n\n return task;\n }\n\n static batch(tasks: Task<true>[]) {\n // console.log(\n // JSON.stringify(\n // tasks.map((t) => t.serialize()),\n // null,\n // 2,\n // ),\n // );\n\n const locks: Lock[] = [];\n const batchLocks: Lock[] = [];\n\n const batch: Task<true>[] = [];\n\n for (const task of tasks) {\n if (task.shouldSkip) {\n // console.log(\n // `Skipping task ${task.id.serialize()} due to skipAfter limit`,\n // );\n continue;\n }\n\n if (task.shouldIsolate) {\n if (batch.length > 0) {\n return batch;\n }\n // console.log(\n // `Isolating task ${task.id.serialize()} due to isolateAfter limit`,\n // );\n batch.push(task);\n return batch;\n }\n\n if (locks.some((l) => l.restrains(task.lock))) {\n locks.push(task.lock);\n continue;\n }\n\n if (batchLocks.some((l) => l.restrains(task.lock, false))) {\n locks.push(task.lock);\n continue;\n }\n\n if (task.isProcessed) {\n continue;\n }\n\n if (task.isProcessing) {\n locks.push(task.lock);\n continue;\n }\n\n batch.push(task);\n batchLocks.push(task.lock);\n }\n\n return batch;\n }\n}\n"]}
|