@webiny/background-tasks-standalone 0.0.0-unstable.33e4fd9647

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.
Files changed (71) hide show
  1. package/BackgroundTasksStandaloneFeature.d.ts +8 -0
  2. package/BackgroundTasksStandaloneFeature.js +27 -0
  3. package/BackgroundTasksStandaloneFeature.js.map +1 -0
  4. package/LICENSE +21 -0
  5. package/README.md +11 -0
  6. package/claim/ClaimRenewal.d.ts +22 -0
  7. package/claim/ClaimRenewal.js +36 -0
  8. package/claim/ClaimRenewal.js.map +1 -0
  9. package/claim/KeyValueTaskClaim.d.ts +19 -0
  10. package/claim/KeyValueTaskClaim.js +64 -0
  11. package/claim/KeyValueTaskClaim.js.map +1 -0
  12. package/claim/KeyValueTaskRecoveries.d.ts +16 -0
  13. package/claim/KeyValueTaskRecoveries.js +40 -0
  14. package/claim/KeyValueTaskRecoveries.js.map +1 -0
  15. package/claim/constants.d.ts +5 -0
  16. package/claim/constants.js +8 -0
  17. package/claim/constants.js.map +1 -0
  18. package/domain/BackgroundTasksLoop.d.ts +31 -0
  19. package/domain/BackgroundTasksLoop.js +7 -0
  20. package/domain/BackgroundTasksLoop.js.map +1 -0
  21. package/domain/ShutdownSignal.d.ts +16 -0
  22. package/domain/ShutdownSignal.js +5 -0
  23. package/domain/ShutdownSignal.js.map +1 -0
  24. package/domain/TaskClaim.d.ts +25 -0
  25. package/domain/TaskClaim.js +5 -0
  26. package/domain/TaskClaim.js.map +1 -0
  27. package/domain/TaskRecoveries.d.ts +18 -0
  28. package/domain/TaskRecoveries.js +5 -0
  29. package/domain/TaskRecoveries.js.map +1 -0
  30. package/handlers/InProcessBackgroundTaskHandler.d.ts +37 -0
  31. package/handlers/InProcessBackgroundTaskHandler.js +120 -0
  32. package/handlers/InProcessBackgroundTaskHandler.js.map +1 -0
  33. package/index.d.ts +3 -0
  34. package/index.js +3 -0
  35. package/loop/InProcessBackgroundTasksLoop.d.ts +22 -0
  36. package/loop/InProcessBackgroundTasksLoop.js +58 -0
  37. package/loop/InProcessBackgroundTasksLoop.js.map +1 -0
  38. package/loop/IterationSlots.d.ts +21 -0
  39. package/loop/IterationSlots.js +37 -0
  40. package/loop/IterationSlots.js.map +1 -0
  41. package/loop/TaskExecution.d.ts +31 -0
  42. package/loop/TaskExecution.js +61 -0
  43. package/loop/TaskExecution.js.map +1 -0
  44. package/loop/nextStep.d.ts +20 -0
  45. package/loop/nextStep.js +26 -0
  46. package/loop/nextStep.js.map +1 -0
  47. package/package.json +37 -0
  48. package/recovery/RecoverTasksHandler.d.ts +47 -0
  49. package/recovery/RecoverTasksHandler.js +151 -0
  50. package/recovery/RecoverTasksHandler.js.map +1 -0
  51. package/recovery/TaskRecoverEventHandler.d.ts +15 -0
  52. package/recovery/TaskRecoverEventHandler.js +5 -0
  53. package/recovery/TaskRecoverEventHandler.js.map +1 -0
  54. package/recovery/TaskRecoverEventType.d.ts +17 -0
  55. package/recovery/TaskRecoverEventType.js +18 -0
  56. package/recovery/TaskRecoverEventType.js.map +1 -0
  57. package/recovery/startTaskRecovery.d.ts +11 -0
  58. package/recovery/startTaskRecovery.js +31 -0
  59. package/recovery/startTaskRecovery.js.map +1 -0
  60. package/service/InProcessTaskService.d.ts +28 -0
  61. package/service/InProcessTaskService.js +62 -0
  62. package/service/InProcessTaskService.js.map +1 -0
  63. package/shutdown/ProcessShutdownSignal.d.ts +12 -0
  64. package/shutdown/ProcessShutdownSignal.js +29 -0
  65. package/shutdown/ProcessShutdownSignal.js.map +1 -0
  66. package/shutdown/stopBackgroundTasks.d.ts +7 -0
  67. package/shutdown/stopBackgroundTasks.js +8 -0
  68. package/shutdown/stopBackgroundTasks.js.map +1 -0
  69. package/timer/ProcessTimer.d.ts +14 -0
  70. package/timer/ProcessTimer.js +24 -0
  71. package/timer/ProcessTimer.js.map +1 -0
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Background tasks for the standalone server. Register at the root container: the loop is one
3
+ * long-lived object for the whole process, and it needs the root's `EventDispatcher`.
4
+ */
5
+ export declare const BackgroundTasksStandaloneFeature: {
6
+ name: string;
7
+ register(container: import("@webiny/di").Container): void;
8
+ };
@@ -0,0 +1,27 @@
1
+ import { createFeature } from "@webiny/feature/api";
2
+ import { BackgroundTaskEventType } from "@webiny/event-handler-core";
3
+ import { InProcessBackgroundTasksLoop } from "./loop/InProcessBackgroundTasksLoop.js";
4
+ import { InProcessTaskService } from "./service/InProcessTaskService.js";
5
+ import { InProcessBackgroundTaskHandler } from "./handlers/InProcessBackgroundTaskHandler.js";
6
+ import { KeyValueTaskClaim } from "./claim/KeyValueTaskClaim.js";
7
+ import { KeyValueTaskRecoveries } from "./claim/KeyValueTaskRecoveries.js";
8
+ import { TaskRecoverEventType } from "./recovery/TaskRecoverEventType.js";
9
+ import { RecoverTasksHandler } from "./recovery/RecoverTasksHandler.js";
10
+ import { ProcessShutdownSignal } from "./shutdown/ProcessShutdownSignal.js";
11
+ const BackgroundTasksStandaloneFeature = createFeature({
12
+ name: "BackgroundTasksServer",
13
+ register (container) {
14
+ container.register(BackgroundTaskEventType);
15
+ container.register(InProcessBackgroundTaskHandler);
16
+ container.register(InProcessBackgroundTasksLoop).inSingletonScope();
17
+ container.register(InProcessTaskService);
18
+ container.register(ProcessShutdownSignal).inSingletonScope();
19
+ container.register(KeyValueTaskClaim);
20
+ container.register(KeyValueTaskRecoveries);
21
+ container.register(TaskRecoverEventType);
22
+ container.register(RecoverTasksHandler);
23
+ }
24
+ });
25
+ export { BackgroundTasksStandaloneFeature };
26
+
27
+ //# sourceMappingURL=BackgroundTasksStandaloneFeature.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BackgroundTasksStandaloneFeature.js","sources":["../src/BackgroundTasksStandaloneFeature.ts"],"sourcesContent":["import { createFeature } from \"@webiny/feature/api\";\nimport { BackgroundTaskEventType } from \"@webiny/event-handler-core\";\nimport { InProcessBackgroundTasksLoop } from \"~/loop/InProcessBackgroundTasksLoop.js\";\nimport { InProcessTaskService } from \"~/service/InProcessTaskService.js\";\nimport { InProcessBackgroundTaskHandler } from \"~/handlers/InProcessBackgroundTaskHandler.js\";\nimport { KeyValueTaskClaim } from \"~/claim/KeyValueTaskClaim.js\";\nimport { KeyValueTaskRecoveries } from \"~/claim/KeyValueTaskRecoveries.js\";\nimport { TaskRecoverEventType } from \"~/recovery/TaskRecoverEventType.js\";\nimport { RecoverTasksHandler } from \"~/recovery/RecoverTasksHandler.js\";\nimport { ProcessShutdownSignal } from \"~/shutdown/ProcessShutdownSignal.js\";\n\n/**\n * Background tasks for the standalone server. Register at the root container: the loop is one\n * long-lived object for the whole process, and it needs the root's `EventDispatcher`.\n */\nexport const BackgroundTasksStandaloneFeature = createFeature({\n name: \"BackgroundTasksServer\",\n register(container) {\n container.register(BackgroundTaskEventType);\n container.register(InProcessBackgroundTaskHandler);\n container.register(InProcessBackgroundTasksLoop).inSingletonScope();\n container.register(InProcessTaskService);\n\n /*\n * One shutdown signal for the life of the process, shared by every request. The claims also\n * need the process's `ProcessOwner`, which the server registers once for every feature.\n */\n container.register(ProcessShutdownSignal).inSingletonScope();\n container.register(KeyValueTaskClaim);\n container.register(KeyValueTaskRecoveries);\n\n container.register(TaskRecoverEventType);\n container.register(RecoverTasksHandler);\n }\n});\n"],"names":["BackgroundTasksStandaloneFeature","createFeature","container","BackgroundTaskEventType","InProcessBackgroundTaskHandler","InProcessBackgroundTasksLoop","InProcessTaskService","ProcessShutdownSignal","KeyValueTaskClaim","KeyValueTaskRecoveries","TaskRecoverEventType","RecoverTasksHandler"],"mappings":";;;;;;;;;;AAeO,MAAMA,mCAAmCC,cAAc;IAC1D,MAAM;IACN,UAASC,SAAS;QACdA,UAAU,QAAQ,CAACC;QACnBD,UAAU,QAAQ,CAACE;QACnBF,UAAU,QAAQ,CAACG,8BAA8B,gBAAgB;QACjEH,UAAU,QAAQ,CAACI;QAMnBJ,UAAU,QAAQ,CAACK,uBAAuB,gBAAgB;QAC1DL,UAAU,QAAQ,CAACM;QACnBN,UAAU,QAAQ,CAACO;QAEnBP,UAAU,QAAQ,CAACQ;QACnBR,UAAU,QAAQ,CAACS;IACvB;AACJ"}
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) Webiny
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,11 @@
1
+ # @webiny/background-tasks-standalone
2
+
3
+ > [!NOTE]
4
+ > This package is part of the [Webiny](https://www.webiny.com) monorepo.
5
+ > It’s **included in every Webiny project by default** and is not meant to be used as a standalone package.
6
+
7
+ 📘 **Documentation:** [https://www.webiny.com/docs](https://www.webiny.com/docs)
8
+
9
+ ---
10
+
11
+ _This README file is automatically generated during the publish process._
@@ -0,0 +1,22 @@
1
+ import type { TaskClaim } from "../domain/TaskClaim.js";
2
+ interface IClaimRenewalParams {
3
+ claim: TaskClaim.Interface;
4
+ target: TaskClaim.Target;
5
+ onLost: () => void;
6
+ onError: (error: unknown) => void;
7
+ }
8
+ /**
9
+ * Keeps extending a claim while one iteration of its task runs, since an iteration can run far longer
10
+ * than the claim lasts. Started before the iteration and stopped after it.
11
+ */
12
+ export declare class ClaimRenewal {
13
+ private readonly params;
14
+ private interval;
15
+ private lost;
16
+ constructor(params: IClaimRenewalParams);
17
+ isLost(): boolean;
18
+ start(): void;
19
+ stop(): void;
20
+ private renew;
21
+ }
22
+ export {};
@@ -0,0 +1,36 @@
1
+ import { CLAIM_RENEW_EVERY_MS, CLAIM_TTL_MS } from "./constants.js";
2
+ class ClaimRenewal {
3
+ constructor(params){
4
+ this.lost = false;
5
+ this.params = params;
6
+ }
7
+ isLost() {
8
+ return this.lost;
9
+ }
10
+ start() {
11
+ this.interval = setInterval(()=>{
12
+ this.renew();
13
+ }, CLAIM_RENEW_EVERY_MS);
14
+ }
15
+ stop() {
16
+ clearInterval(this.interval);
17
+ this.interval = void 0;
18
+ }
19
+ async renew() {
20
+ if (this.lost) return;
21
+ let held;
22
+ try {
23
+ held = await this.params.claim.keep(this.params.target, CLAIM_TTL_MS);
24
+ } catch (error) {
25
+ this.params.onError(error);
26
+ return;
27
+ }
28
+ if (held) return;
29
+ this.lost = true;
30
+ this.stop();
31
+ this.params.onLost();
32
+ }
33
+ }
34
+ export { ClaimRenewal };
35
+
36
+ //# sourceMappingURL=ClaimRenewal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claim/ClaimRenewal.js","sources":["../../src/claim/ClaimRenewal.ts"],"sourcesContent":["import type { TaskClaim } from \"~/domain/TaskClaim.js\";\nimport { CLAIM_RENEW_EVERY_MS } from \"./constants.js\";\nimport { CLAIM_TTL_MS } from \"./constants.js\";\n\ninterface IClaimRenewalParams {\n claim: TaskClaim.Interface;\n target: TaskClaim.Target;\n // Called once, when an extension finds that this process no longer holds the claim.\n onLost: () => void;\n // Called for an extension that could not be written. The next one tries again.\n onError: (error: unknown) => void;\n}\n\n/**\n * Keeps extending a claim while one iteration of its task runs, since an iteration can run far longer\n * than the claim lasts. Started before the iteration and stopped after it.\n */\nexport class ClaimRenewal {\n private readonly params: IClaimRenewalParams;\n private interval: ReturnType<typeof setInterval> | undefined;\n private lost = false;\n\n public constructor(params: IClaimRenewalParams) {\n this.params = params;\n }\n\n public isLost(): boolean {\n return this.lost;\n }\n\n public start(): void {\n this.interval = setInterval(() => {\n void this.renew();\n }, CLAIM_RENEW_EVERY_MS);\n }\n\n public stop(): void {\n clearInterval(this.interval);\n this.interval = undefined;\n }\n\n /*\n * Only an answer of `false` loses the claim. A write that fails is retried on the next tick: one\n * slow or failed write leaves the claim with most of its time, and if they keep failing until it\n * runs out, the next write that gets through answers `false`.\n */\n private async renew(): Promise<void> {\n if (this.lost) {\n return;\n }\n let held: boolean;\n try {\n held = await this.params.claim.keep(this.params.target, CLAIM_TTL_MS);\n } catch (error) {\n this.params.onError(error);\n return;\n }\n if (held) {\n return;\n }\n this.lost = true;\n this.stop();\n this.params.onLost();\n }\n}\n"],"names":["ClaimRenewal","params","setInterval","CLAIM_RENEW_EVERY_MS","clearInterval","undefined","held","CLAIM_TTL_MS","error"],"mappings":";AAiBO,MAAMA;IAKT,YAAmBC,MAA2B,CAAE;aAFxC,IAAI,GAAG;QAGX,IAAI,CAAC,MAAM,GAAGA;IAClB;IAEO,SAAkB;QACrB,OAAO,IAAI,CAAC,IAAI;IACpB;IAEO,QAAc;QACjB,IAAI,CAAC,QAAQ,GAAGC,YAAY;YACnB,IAAI,CAAC,KAAK;QACnB,GAAGC;IACP;IAEO,OAAa;QAChBC,cAAc,IAAI,CAAC,QAAQ;QAC3B,IAAI,CAAC,QAAQ,GAAGC;IACpB;IAOA,MAAc,QAAuB;QACjC,IAAI,IAAI,CAAC,IAAI,EACT;QAEJ,IAAIC;QACJ,IAAI;YACAA,OAAO,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,EAAEC;QAC5D,EAAE,OAAOC,OAAO;YACZ,IAAI,CAAC,MAAM,CAAC,OAAO,CAACA;YACpB;QACJ;QACA,IAAIF,MACA;QAEJ,IAAI,CAAC,IAAI,GAAG;QACZ,IAAI,CAAC,IAAI;QACT,IAAI,CAAC,MAAM,CAAC,MAAM;IACtB;AACJ"}
@@ -0,0 +1,19 @@
1
+ import { GlobalKeyValueStore } from "@webiny/api-core/features/keyValueStore/index.js";
2
+ import { TaskClaim } from "../domain/TaskClaim.js";
3
+ import { ProcessOwner } from "@webiny/event-handler-standalone";
4
+ /**
5
+ * A claim is a global key-value record whose value is the owning process. The store's conditional
6
+ * writes do the checking, so two processes racing for a task cannot both get it.
7
+ */
8
+ declare class KeyValueTaskClaimImpl implements TaskClaim.Interface {
9
+ private readonly store;
10
+ private readonly owner;
11
+ constructor(store: GlobalKeyValueStore.Interface, owner: ProcessOwner.Interface);
12
+ take(target: TaskClaim.Target, ttlMs: number): Promise<boolean>;
13
+ keep(target: TaskClaim.Target, ttlMs: number): Promise<boolean>;
14
+ release(target: TaskClaim.Target): Promise<void>;
15
+ }
16
+ export declare const KeyValueTaskClaim: typeof KeyValueTaskClaimImpl & {
17
+ __abstraction: import("@webiny/di").Abstraction<import("~/domain/TaskClaim.js").ITaskClaim>;
18
+ };
19
+ export {};
@@ -0,0 +1,64 @@
1
+ import { GlobalKeyValueStore, KeyValueConditionFailedError } from "@webiny/api-core/features/keyValueStore/index.js";
2
+ import { TaskClaim } from "../domain/TaskClaim.js";
3
+ import { ProcessOwner } from "@webiny/event-handler-standalone";
4
+ const CLAIM_SCOPE = "background-tasks/claims";
5
+ const toKey = (target)=>`${target.tenant}/${target.taskId}`;
6
+ const expiresIn = (ttlMs)=>{
7
+ const expiresAtMs = Date.now() + ttlMs;
8
+ return new Date(expiresAtMs);
9
+ };
10
+ const toHeld = (result)=>{
11
+ if (result.isOk()) return true;
12
+ if (result.error instanceof KeyValueConditionFailedError) return false;
13
+ throw result.error;
14
+ };
15
+ class KeyValueTaskClaimImpl {
16
+ constructor(store, owner){
17
+ this.store = store;
18
+ this.owner = owner;
19
+ }
20
+ async take(target, ttlMs) {
21
+ const key = toKey(target);
22
+ const expiresAt = expiresIn(ttlMs);
23
+ const result = await this.store.set(key, this.owner.id, {
24
+ scope: CLAIM_SCOPE,
25
+ expiresAt,
26
+ condition: {
27
+ exists: false
28
+ }
29
+ });
30
+ return toHeld(result);
31
+ }
32
+ async keep(target, ttlMs) {
33
+ const key = toKey(target);
34
+ const expiresAt = expiresIn(ttlMs);
35
+ const result = await this.store.set(key, this.owner.id, {
36
+ scope: CLAIM_SCOPE,
37
+ expiresAt,
38
+ condition: {
39
+ equals: this.owner.id
40
+ }
41
+ });
42
+ return toHeld(result);
43
+ }
44
+ async release(target) {
45
+ const key = toKey(target);
46
+ const result = await this.store.delete(key, {
47
+ scope: CLAIM_SCOPE,
48
+ condition: {
49
+ equals: this.owner.id
50
+ }
51
+ });
52
+ toHeld(result);
53
+ }
54
+ }
55
+ const KeyValueTaskClaim = TaskClaim.createImplementation({
56
+ implementation: KeyValueTaskClaimImpl,
57
+ dependencies: [
58
+ GlobalKeyValueStore,
59
+ ProcessOwner
60
+ ]
61
+ });
62
+ export { KeyValueTaskClaim };
63
+
64
+ //# sourceMappingURL=KeyValueTaskClaim.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claim/KeyValueTaskClaim.js","sources":["../../src/claim/KeyValueTaskClaim.ts"],"sourcesContent":["import type { Result } from \"@webiny/feature/api\";\nimport { GlobalKeyValueStore } from \"@webiny/api-core/features/keyValueStore/index.js\";\nimport { KeyValueConditionFailedError } from \"@webiny/api-core/features/keyValueStore/index.js\";\nimport { TaskClaim } from \"~/domain/TaskClaim.js\";\nimport { ProcessOwner } from \"@webiny/event-handler-standalone\";\n\n// Claims live apart from everyone's records, in their own scope of the global store.\nconst CLAIM_SCOPE = \"background-tasks/claims\";\n\nconst toKey = (target: TaskClaim.Target): string => {\n return `${target.tenant}/${target.taskId}`;\n};\n\nconst expiresIn = (ttlMs: number): Date => {\n const expiresAtMs = Date.now() + ttlMs;\n return new Date(expiresAtMs);\n};\n\n/*\n * A write whose condition does not hold means someone else holds the claim. That is an answer, not\n * a failure, so it becomes `false`.\n */\nconst toHeld = (result: Result<void, GlobalKeyValueStore.Error>): boolean => {\n if (result.isOk()) {\n return true;\n }\n if (result.error instanceof KeyValueConditionFailedError) {\n return false;\n }\n throw result.error;\n};\n\n/**\n * A claim is a global key-value record whose value is the owning process. The store's conditional\n * writes do the checking, so two processes racing for a task cannot both get it.\n */\nclass KeyValueTaskClaimImpl implements TaskClaim.Interface {\n public constructor(\n private readonly store: GlobalKeyValueStore.Interface,\n private readonly owner: ProcessOwner.Interface\n ) {}\n\n public async take(target: TaskClaim.Target, ttlMs: number): Promise<boolean> {\n const key = toKey(target);\n const expiresAt = expiresIn(ttlMs);\n const result = await this.store.set(key, this.owner.id, {\n scope: CLAIM_SCOPE,\n expiresAt,\n condition: { exists: false }\n });\n return toHeld(result);\n }\n\n public async keep(target: TaskClaim.Target, ttlMs: number): Promise<boolean> {\n const key = toKey(target);\n const expiresAt = expiresIn(ttlMs);\n const result = await this.store.set(key, this.owner.id, {\n scope: CLAIM_SCOPE,\n expiresAt,\n condition: { equals: this.owner.id }\n });\n return toHeld(result);\n }\n\n public async release(target: TaskClaim.Target): Promise<void> {\n const key = toKey(target);\n const result = await this.store.delete(key, {\n scope: CLAIM_SCOPE,\n condition: { equals: this.owner.id }\n });\n // Already gone or held by someone else: either way, nothing of ours is left to release.\n toHeld(result);\n }\n}\n\nexport const KeyValueTaskClaim = TaskClaim.createImplementation({\n implementation: KeyValueTaskClaimImpl,\n dependencies: [GlobalKeyValueStore, ProcessOwner]\n});\n"],"names":["CLAIM_SCOPE","toKey","target","expiresIn","ttlMs","expiresAtMs","Date","toHeld","result","KeyValueConditionFailedError","KeyValueTaskClaimImpl","store","owner","key","expiresAt","KeyValueTaskClaim","TaskClaim","GlobalKeyValueStore","ProcessOwner"],"mappings":";;;AAOA,MAAMA,cAAc;AAEpB,MAAMC,QAAQ,CAACC,SACJ,GAAGA,OAAO,MAAM,CAAC,CAAC,EAAEA,OAAO,MAAM,EAAE;AAG9C,MAAMC,YAAY,CAACC;IACf,MAAMC,cAAcC,KAAK,GAAG,KAAKF;IACjC,OAAO,IAAIE,KAAKD;AACpB;AAMA,MAAME,SAAS,CAACC;IACZ,IAAIA,OAAO,IAAI,IACX,OAAO;IAEX,IAAIA,OAAO,KAAK,YAAYC,8BACxB,OAAO;IAEX,MAAMD,OAAO,KAAK;AACtB;AAMA,MAAME;IACF,YACqBC,KAAoC,EACpCC,KAA6B,CAChD;aAFmBD,KAAK,GAALA;aACAC,KAAK,GAALA;IAClB;IAEH,MAAa,KAAKV,MAAwB,EAAEE,KAAa,EAAoB;QACzE,MAAMS,MAAMZ,MAAMC;QAClB,MAAMY,YAAYX,UAAUC;QAC5B,MAAMI,SAAS,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAACK,KAAK,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE;YACpD,OAAOb;YACPc;YACA,WAAW;gBAAE,QAAQ;YAAM;QAC/B;QACA,OAAOP,OAAOC;IAClB;IAEA,MAAa,KAAKN,MAAwB,EAAEE,KAAa,EAAoB;QACzE,MAAMS,MAAMZ,MAAMC;QAClB,MAAMY,YAAYX,UAAUC;QAC5B,MAAMI,SAAS,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAACK,KAAK,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE;YACpD,OAAOb;YACPc;YACA,WAAW;gBAAE,QAAQ,IAAI,CAAC,KAAK,CAAC,EAAE;YAAC;QACvC;QACA,OAAOP,OAAOC;IAClB;IAEA,MAAa,QAAQN,MAAwB,EAAiB;QAC1D,MAAMW,MAAMZ,MAAMC;QAClB,MAAMM,SAAS,MAAM,IAAI,CAAC,KAAK,CAAC,MAAM,CAACK,KAAK;YACxC,OAAOb;YACP,WAAW;gBAAE,QAAQ,IAAI,CAAC,KAAK,CAAC,EAAE;YAAC;QACvC;QAEAO,OAAOC;IACX;AACJ;AAEO,MAAMO,oBAAoBC,UAAU,oBAAoB,CAAC;IAC5D,gBAAgBN;IAChB,cAAc;QAACO;QAAqBC;KAAa;AACrD"}
@@ -0,0 +1,16 @@
1
+ import { GlobalKeyValueStore } from "@webiny/api-core/features/keyValueStore/index.js";
2
+ import { TaskRecoveries } from "../domain/TaskRecoveries.js";
3
+ import type { TaskClaim } from "../domain/TaskClaim.js";
4
+ /**
5
+ * The count is read and written in two steps. That is safe only because the caller holds the
6
+ * task's claim, so no other process records a takeover of the same task in between.
7
+ */
8
+ declare class KeyValueTaskRecoveriesImpl implements TaskRecoveries.Interface {
9
+ private readonly store;
10
+ constructor(store: GlobalKeyValueStore.Interface);
11
+ record(target: TaskClaim.Target, savedOn: string): Promise<number>;
12
+ }
13
+ export declare const KeyValueTaskRecoveries: typeof KeyValueTaskRecoveriesImpl & {
14
+ __abstraction: import("@webiny/di").Abstraction<import("~/domain/TaskRecoveries.js").ITaskRecoveries>;
15
+ };
16
+ export {};
@@ -0,0 +1,40 @@
1
+ import { GlobalKeyValueStore, KeyNotFoundError } from "@webiny/api-core/features/keyValueStore/index.js";
2
+ import { TaskRecoveries } from "../domain/TaskRecoveries.js";
3
+ const RECOVERIES_SCOPE = "background-tasks/recoveries";
4
+ const RECOVERIES_TTL_MS = 86400000;
5
+ const toKey = (target)=>`${target.tenant}/${target.taskId}`;
6
+ class KeyValueTaskRecoveriesImpl {
7
+ constructor(store){
8
+ this.store = store;
9
+ }
10
+ async record(target, savedOn) {
11
+ const key = toKey(target);
12
+ const current = await this.store.get(key, {
13
+ scope: RECOVERIES_SCOPE
14
+ });
15
+ if (current.isFail() && !(current.error instanceof KeyNotFoundError)) throw current.error;
16
+ let count = 1;
17
+ if (current.isOk() && current.value.savedOn === savedOn) count = current.value.count + 1;
18
+ const record = {
19
+ count,
20
+ savedOn
21
+ };
22
+ const expiresAtMs = Date.now() + RECOVERIES_TTL_MS;
23
+ const expiresAt = new Date(expiresAtMs);
24
+ const written = await this.store.set(key, record, {
25
+ scope: RECOVERIES_SCOPE,
26
+ expiresAt
27
+ });
28
+ if (written.isFail()) throw written.error;
29
+ return count;
30
+ }
31
+ }
32
+ const KeyValueTaskRecoveries = TaskRecoveries.createImplementation({
33
+ implementation: KeyValueTaskRecoveriesImpl,
34
+ dependencies: [
35
+ GlobalKeyValueStore
36
+ ]
37
+ });
38
+ export { KeyValueTaskRecoveries };
39
+
40
+ //# sourceMappingURL=KeyValueTaskRecoveries.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claim/KeyValueTaskRecoveries.js","sources":["../../src/claim/KeyValueTaskRecoveries.ts"],"sourcesContent":["import { GlobalKeyValueStore } from \"@webiny/api-core/features/keyValueStore/index.js\";\nimport { KeyNotFoundError } from \"@webiny/api-core/features/keyValueStore/index.js\";\nimport { TaskRecoveries } from \"~/domain/TaskRecoveries.js\";\nimport type { TaskClaim } from \"~/domain/TaskClaim.js\";\n\nconst RECOVERIES_SCOPE = \"background-tasks/recoveries\";\n\n/*\n * Only clean-up: a crash loop repeats within minutes, and a task that saves starts the count over\n * anyway. A record nobody updates for a day is not worth keeping.\n */\nconst RECOVERIES_TTL_MS = 86_400_000;\n\ninterface IRecoveriesRecord {\n count: number;\n // When the task had last saved at the previous takeover.\n savedOn: string;\n}\n\nconst toKey = (target: TaskClaim.Target): string => {\n return `${target.tenant}/${target.taskId}`;\n};\n\n/**\n * The count is read and written in two steps. That is safe only because the caller holds the\n * task's claim, so no other process records a takeover of the same task in between.\n */\nclass KeyValueTaskRecoveriesImpl implements TaskRecoveries.Interface {\n public constructor(private readonly store: GlobalKeyValueStore.Interface) {}\n\n public async record(target: TaskClaim.Target, savedOn: string): Promise<number> {\n const key = toKey(target);\n const current = await this.store.get<IRecoveriesRecord>(key, { scope: RECOVERIES_SCOPE });\n if (current.isFail() && !(current.error instanceof KeyNotFoundError)) {\n throw current.error;\n }\n\n let count = 1;\n if (current.isOk() && current.value.savedOn === savedOn) {\n count = current.value.count + 1;\n }\n\n const record: IRecoveriesRecord = { count, savedOn };\n const expiresAtMs = Date.now() + RECOVERIES_TTL_MS;\n const expiresAt = new Date(expiresAtMs);\n const written = await this.store.set(key, record, { scope: RECOVERIES_SCOPE, expiresAt });\n if (written.isFail()) {\n throw written.error;\n }\n return count;\n }\n}\n\nexport const KeyValueTaskRecoveries = TaskRecoveries.createImplementation({\n implementation: KeyValueTaskRecoveriesImpl,\n dependencies: [GlobalKeyValueStore]\n});\n"],"names":["RECOVERIES_SCOPE","RECOVERIES_TTL_MS","toKey","target","KeyValueTaskRecoveriesImpl","store","savedOn","key","current","KeyNotFoundError","count","record","expiresAtMs","Date","expiresAt","written","KeyValueTaskRecoveries","TaskRecoveries","GlobalKeyValueStore"],"mappings":";;AAKA,MAAMA,mBAAmB;AAMzB,MAAMC,oBAAoB;AAQ1B,MAAMC,QAAQ,CAACC,SACJ,GAAGA,OAAO,MAAM,CAAC,CAAC,EAAEA,OAAO,MAAM,EAAE;AAO9C,MAAMC;IACF,YAAoCC,KAAoC,CAAE;aAAtCA,KAAK,GAALA;IAAuC;IAE3E,MAAa,OAAOF,MAAwB,EAAEG,OAAe,EAAmB;QAC5E,MAAMC,MAAML,MAAMC;QAClB,MAAMK,UAAU,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAoBD,KAAK;YAAE,OAAOP;QAAiB;QACvF,IAAIQ,QAAQ,MAAM,MAAM,CAAEA,CAAAA,QAAQ,KAAK,YAAYC,gBAAe,GAC9D,MAAMD,QAAQ,KAAK;QAGvB,IAAIE,QAAQ;QACZ,IAAIF,QAAQ,IAAI,MAAMA,QAAQ,KAAK,CAAC,OAAO,KAAKF,SAC5CI,QAAQF,QAAQ,KAAK,CAAC,KAAK,GAAG;QAGlC,MAAMG,SAA4B;YAAED;YAAOJ;QAAQ;QACnD,MAAMM,cAAcC,KAAK,GAAG,KAAKZ;QACjC,MAAMa,YAAY,IAAID,KAAKD;QAC3B,MAAMG,UAAU,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAACR,KAAKI,QAAQ;YAAE,OAAOX;YAAkBc;QAAU;QACvF,IAAIC,QAAQ,MAAM,IACd,MAAMA,QAAQ,KAAK;QAEvB,OAAOL;IACX;AACJ;AAEO,MAAMM,yBAAyBC,eAAe,oBAAoB,CAAC;IACtE,gBAAgBb;IAChB,cAAc;QAACc;KAAoB;AACvC"}
@@ -0,0 +1,5 @@
1
+ export declare const CLAIM_TTL_MS = 60000;
2
+ export declare const CLAIM_RENEW_EVERY_MS = 20000;
3
+ export declare const RECOVER_EVERY_MS = 60000;
4
+ export declare const RECOVER_GRACE_MS = 60000;
5
+ export declare const MAX_RECOVERIES = 3;
@@ -0,0 +1,8 @@
1
+ const CLAIM_TTL_MS = 60000;
2
+ const CLAIM_RENEW_EVERY_MS = 20000;
3
+ const RECOVER_EVERY_MS = 60000;
4
+ const RECOVER_GRACE_MS = CLAIM_TTL_MS;
5
+ const MAX_RECOVERIES = 3;
6
+ export { CLAIM_RENEW_EVERY_MS, CLAIM_TTL_MS, MAX_RECOVERIES, RECOVER_EVERY_MS, RECOVER_GRACE_MS };
7
+
8
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claim/constants.js","sources":["../../src/claim/constants.ts"],"sourcesContent":["/*\n * How long a claim lasts unless its owner extends it. A process that dies stops extending, so its\n * tasks can be taken over this long after its last extension. Shorter recovers sooner, but a process\n * that is alive and only slow (a busy event loop, a slow database) could lose a task it is running.\n */\nexport const CLAIM_TTL_MS = 60_000;\n\n// A third of the TTL, so one or two missed extensions do not lose the claim.\nexport const CLAIM_RENEW_EVERY_MS = 20_000;\n\n// How often each process looks for tasks that nobody holds.\nexport const RECOVER_EVERY_MS = 60_000;\n\n/*\n * A pending task saved this recently is left alone by recovery. It covers the moment between a\n * trigger creating a task and claiming it, when nobody holds it yet, and a claim the trigger could\n * not write, which the first iteration then takes.\n */\nexport const RECOVER_GRACE_MS = CLAIM_TTL_MS;\n\n/*\n * Takeovers of one task, with no save in between, before recovery fails it instead. A task that\n * crashes the process it runs in would otherwise be resumed on every start and take the server down\n * each time.\n */\nexport const MAX_RECOVERIES = 3;\n"],"names":["CLAIM_TTL_MS","CLAIM_RENEW_EVERY_MS","RECOVER_EVERY_MS","RECOVER_GRACE_MS","MAX_RECOVERIES"],"mappings":"AAKO,MAAMA,eAAe;AAGrB,MAAMC,uBAAuB;AAG7B,MAAMC,mBAAmB;AAOzB,MAAMC,mBAAmBH;AAOzB,MAAMI,iBAAiB"}
@@ -0,0 +1,31 @@
1
+ import type { IBackgroundTaskEvent } from "@webiny/event-handler-core";
2
+ /**
3
+ * Drives the background tasks of this process to completion. For each task it runs one iteration,
4
+ * waits as long as the iteration asks, and runs the next until the task stops asking to continue.
5
+ * The standalone counterpart of the Step Functions state machine that drives a task on AWS.
6
+ *
7
+ * `start` returns at once. The task runs after the request that triggered it has finished.
8
+ */
9
+ export interface IBackgroundTasksLoop {
10
+ /** Ignored for a task already being driven, and once the process is shutting down. */
11
+ start(event: IBackgroundTaskEvent): void;
12
+ /**
13
+ * Stops driving tasks: running iterations are asked to save and hand their task back, and nothing
14
+ * new starts. Resolves once they have, or after `timeoutMs`, whichever comes first.
15
+ */
16
+ stop(timeoutMs: number): Promise<void>;
17
+ }
18
+ /**
19
+ * The status an iteration answers when another process holds the task's claim. That process drives
20
+ * the task, so driving it here stops without it being an error.
21
+ */
22
+ export declare const CLAIMED_ELSEWHERE_STATUS = "claimed-elsewhere";
23
+ /**
24
+ * The status an iteration answers when it stopped early because the process is shutting down. Its claim
25
+ * is released, so another process takes the task over at its next recovery sweep.
26
+ */
27
+ export declare const HANDED_BACK_STATUS = "handed-back";
28
+ export declare const BackgroundTasksLoop: import("@webiny/di").Abstraction<IBackgroundTasksLoop>;
29
+ export declare namespace BackgroundTasksLoop {
30
+ type Interface = IBackgroundTasksLoop;
31
+ }
@@ -0,0 +1,7 @@
1
+ import { createAbstraction } from "@webiny/feature/api";
2
+ const CLAIMED_ELSEWHERE_STATUS = "claimed-elsewhere";
3
+ const HANDED_BACK_STATUS = "handed-back";
4
+ const BackgroundTasksLoop = createAbstraction("BackgroundTasks/BackgroundTasksLoop");
5
+ export { BackgroundTasksLoop, CLAIMED_ELSEWHERE_STATUS, HANDED_BACK_STATUS };
6
+
7
+ //# sourceMappingURL=BackgroundTasksLoop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"domain/BackgroundTasksLoop.js","sources":["../../src/domain/BackgroundTasksLoop.ts"],"sourcesContent":["import { createAbstraction } from \"@webiny/feature/api\";\nimport type { IBackgroundTaskEvent } from \"@webiny/event-handler-core\";\n\n/**\n * Drives the background tasks of this process to completion. For each task it runs one iteration,\n * waits as long as the iteration asks, and runs the next until the task stops asking to continue.\n * The standalone counterpart of the Step Functions state machine that drives a task on AWS.\n *\n * `start` returns at once. The task runs after the request that triggered it has finished.\n */\nexport interface IBackgroundTasksLoop {\n /** Ignored for a task already being driven, and once the process is shutting down. */\n start(event: IBackgroundTaskEvent): void;\n /**\n * Stops driving tasks: running iterations are asked to save and hand their task back, and nothing\n * new starts. Resolves once they have, or after `timeoutMs`, whichever comes first.\n */\n stop(timeoutMs: number): Promise<void>;\n}\n\n/**\n * The status an iteration answers when another process holds the task's claim. That process drives\n * the task, so driving it here stops without it being an error.\n */\nexport const CLAIMED_ELSEWHERE_STATUS = \"claimed-elsewhere\";\n\n/**\n * The status an iteration answers when it stopped early because the process is shutting down. Its claim\n * is released, so another process takes the task over at its next recovery sweep.\n */\nexport const HANDED_BACK_STATUS = \"handed-back\";\n\nexport const BackgroundTasksLoop = createAbstraction<IBackgroundTasksLoop>(\n \"BackgroundTasks/BackgroundTasksLoop\"\n);\n\nexport namespace BackgroundTasksLoop {\n export type Interface = IBackgroundTasksLoop;\n}\n"],"names":["CLAIMED_ELSEWHERE_STATUS","HANDED_BACK_STATUS","BackgroundTasksLoop","createAbstraction"],"mappings":";AAwBO,MAAMA,2BAA2B;AAMjC,MAAMC,qBAAqB;AAE3B,MAAMC,sBAAsBC,kBAC/B"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Tells the task code that this process is shutting down, so it hands its work back instead of
3
+ * starting more. One for the whole process: the loop, the iterations and recovery all listen to the
4
+ * same one.
5
+ */
6
+ export interface IShutdownSignal {
7
+ isShuttingDown(): boolean;
8
+ /** Calls `listener` once the shutdown begins. Returns a function that unsubscribes. */
9
+ onShutdown(listener: () => void): () => void;
10
+ /** Begins the shutdown. Calling it again does nothing. */
11
+ begin(): void;
12
+ }
13
+ export declare const ShutdownSignal: import("@webiny/di").Abstraction<IShutdownSignal>;
14
+ export declare namespace ShutdownSignal {
15
+ type Interface = IShutdownSignal;
16
+ }
@@ -0,0 +1,5 @@
1
+ import { createAbstraction } from "@webiny/feature/api";
2
+ const ShutdownSignal = createAbstraction("BackgroundTasks/ShutdownSignal");
3
+ export { ShutdownSignal };
4
+
5
+ //# sourceMappingURL=ShutdownSignal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"domain/ShutdownSignal.js","sources":["../../src/domain/ShutdownSignal.ts"],"sourcesContent":["import { createAbstraction } from \"@webiny/feature/api\";\n\n/**\n * Tells the task code that this process is shutting down, so it hands its work back instead of\n * starting more. One for the whole process: the loop, the iterations and recovery all listen to the\n * same one.\n */\nexport interface IShutdownSignal {\n isShuttingDown(): boolean;\n /** Calls `listener` once the shutdown begins. Returns a function that unsubscribes. */\n onShutdown(listener: () => void): () => void;\n /** Begins the shutdown. Calling it again does nothing. */\n begin(): void;\n}\n\nexport const ShutdownSignal = createAbstraction<IShutdownSignal>(\"BackgroundTasks/ShutdownSignal\");\n\nexport namespace ShutdownSignal {\n export type Interface = IShutdownSignal;\n}\n"],"names":["ShutdownSignal","createAbstraction"],"mappings":";AAeO,MAAMA,iBAAiBC,kBAAmC"}
@@ -0,0 +1,25 @@
1
+ export interface ITaskClaimTarget {
2
+ tenant: string;
3
+ taskId: string;
4
+ }
5
+ /**
6
+ * Which process runs a task. Every process can see every task, so before one runs a task it claims
7
+ * it, and only one claim can be held at a time. A claim expires unless its owner keeps extending it,
8
+ * which is how the tasks of a process that died become free again.
9
+ *
10
+ * Each method resolves to whether this process holds the claim afterwards. A storage failure
11
+ * rejects: the caller cannot tell whether it holds the claim, so it must not run the task.
12
+ */
13
+ export interface ITaskClaim {
14
+ /** Claims the task when nobody holds it, an expired claim counting as nobody. */
15
+ take(target: ITaskClaimTarget, ttlMs: number): Promise<boolean>;
16
+ /** Extends a claim this process holds; `false` when it no longer does. */
17
+ keep(target: ITaskClaimTarget, ttlMs: number): Promise<boolean>;
18
+ /** Drops a claim this process holds, and leaves anyone else's alone. */
19
+ release(target: ITaskClaimTarget): Promise<void>;
20
+ }
21
+ export declare const TaskClaim: import("@webiny/di").Abstraction<ITaskClaim>;
22
+ export declare namespace TaskClaim {
23
+ type Interface = ITaskClaim;
24
+ type Target = ITaskClaimTarget;
25
+ }
@@ -0,0 +1,5 @@
1
+ import { createAbstraction } from "@webiny/feature/api";
2
+ const TaskClaim = createAbstraction("BackgroundTasks/TaskClaim");
3
+ export { TaskClaim };
4
+
5
+ //# sourceMappingURL=TaskClaim.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"domain/TaskClaim.js","sources":["../../src/domain/TaskClaim.ts"],"sourcesContent":["import { createAbstraction } from \"@webiny/feature/api\";\n\nexport interface ITaskClaimTarget {\n tenant: string;\n taskId: string;\n}\n\n/**\n * Which process runs a task. Every process can see every task, so before one runs a task it claims\n * it, and only one claim can be held at a time. A claim expires unless its owner keeps extending it,\n * which is how the tasks of a process that died become free again.\n *\n * Each method resolves to whether this process holds the claim afterwards. A storage failure\n * rejects: the caller cannot tell whether it holds the claim, so it must not run the task.\n */\nexport interface ITaskClaim {\n /** Claims the task when nobody holds it, an expired claim counting as nobody. */\n take(target: ITaskClaimTarget, ttlMs: number): Promise<boolean>;\n /** Extends a claim this process holds; `false` when it no longer does. */\n keep(target: ITaskClaimTarget, ttlMs: number): Promise<boolean>;\n /** Drops a claim this process holds, and leaves anyone else's alone. */\n release(target: ITaskClaimTarget): Promise<void>;\n}\n\nexport const TaskClaim = createAbstraction<ITaskClaim>(\"BackgroundTasks/TaskClaim\");\n\nexport namespace TaskClaim {\n export type Interface = ITaskClaim;\n export type Target = ITaskClaimTarget;\n}\n"],"names":["TaskClaim","createAbstraction"],"mappings":";AAwBO,MAAMA,YAAYC,kBAA8B"}
@@ -0,0 +1,18 @@
1
+ import type { TaskClaim } from "./TaskClaim.js";
2
+ /**
3
+ * Counts how often recovery has taken over a task without the task saving in between, so one that
4
+ * keeps dying with its process is failed instead of resumed forever. Only the holder of the task's
5
+ * claim records, so two processes never count the same takeover.
6
+ */
7
+ export interface ITaskRecoveries {
8
+ /**
9
+ * Records one more takeover and resolves to the count, this one included. `savedOn` is when the
10
+ * task last saved: when it differs from the previous takeover's, the task made progress and the
11
+ * count starts over at 1.
12
+ */
13
+ record(target: TaskClaim.Target, savedOn: string): Promise<number>;
14
+ }
15
+ export declare const TaskRecoveries: import("@webiny/di").Abstraction<ITaskRecoveries>;
16
+ export declare namespace TaskRecoveries {
17
+ type Interface = ITaskRecoveries;
18
+ }
@@ -0,0 +1,5 @@
1
+ import { createAbstraction } from "@webiny/feature/api";
2
+ const TaskRecoveries = createAbstraction("BackgroundTasks/TaskRecoveries");
3
+ export { TaskRecoveries };
4
+
5
+ //# sourceMappingURL=TaskRecoveries.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"domain/TaskRecoveries.js","sources":["../../src/domain/TaskRecoveries.ts"],"sourcesContent":["import { createAbstraction } from \"@webiny/feature/api\";\nimport type { TaskClaim } from \"./TaskClaim.js\";\n\n/**\n * Counts how often recovery has taken over a task without the task saving in between, so one that\n * keeps dying with its process is failed instead of resumed forever. Only the holder of the task's\n * claim records, so two processes never count the same takeover.\n */\nexport interface ITaskRecoveries {\n /**\n * Records one more takeover and resolves to the count, this one included. `savedOn` is when the\n * task last saved: when it differs from the previous takeover's, the task made progress and the\n * count starts over at 1.\n */\n record(target: TaskClaim.Target, savedOn: string): Promise<number>;\n}\n\nexport const TaskRecoveries = createAbstraction<ITaskRecoveries>(\"BackgroundTasks/TaskRecoveries\");\n\nexport namespace TaskRecoveries {\n export type Interface = ITaskRecoveries;\n}\n"],"names":["TaskRecoveries","createAbstraction"],"mappings":";AAiBO,MAAMA,iBAAiBC,kBAAmC"}
@@ -0,0 +1,37 @@
1
+ import type { Container } from "@webiny/feature/api";
2
+ import { BackgroundTaskEventHandler } from "@webiny/event-handler-core";
3
+ import type { EventContext } from "@webiny/event-handler-core";
4
+ import type { IBackgroundTaskEvent } from "@webiny/event-handler-core";
5
+ import { RawTenantId } from "@webiny/api-core/features/requestContext/index.js";
6
+ import { RequestTenantLoader } from "@webiny/api-core/features/requestContext/index.js";
7
+ import { Logger } from "@webiny/api-core/features/logger/index.js";
8
+ import { TaskClaim } from "../domain/TaskClaim.js";
9
+ import { ShutdownSignal } from "../domain/ShutdownSignal.js";
10
+ /**
11
+ * Runs one iteration of a background task in the request container the dispatcher built for it.
12
+ * The standalone counterpart of `BackgroundTaskLambdaHandler`: same tenant set-up and runner, with
13
+ * a process timer in place of the Lambda countdown.
14
+ *
15
+ * Every iteration runs under the task's claim, so only one process drives a task at a time. The
16
+ * first iteration takes the claim, the later ones find it still theirs.
17
+ *
18
+ * When the process starts shutting down, the running iteration's time runs out at once, so the task saves
19
+ * its progress and hands back control. The task is then left to another process.
20
+ */
21
+ declare class InProcessBackgroundTaskHandlerImpl implements BackgroundTaskEventHandler.Interface {
22
+ private readonly container;
23
+ private readonly rawTenantId;
24
+ private readonly tenantLoader;
25
+ private readonly claim;
26
+ private readonly shutdown;
27
+ private readonly logger;
28
+ constructor(container: Container, rawTenantId: RawTenantId.Interface, tenantLoader: RequestTenantLoader.Interface, claim: TaskClaim.Interface, shutdown: ShutdownSignal.Interface, logger: Logger.Interface);
29
+ execute(eventCtx: EventContext<IBackgroundTaskEvent>): Promise<unknown>;
30
+ private hold;
31
+ private afterIteration;
32
+ private release;
33
+ }
34
+ export declare const InProcessBackgroundTaskHandler: typeof InProcessBackgroundTaskHandlerImpl & {
35
+ __abstraction: import("@webiny/di").Abstraction<import("@webiny/event-handler-core/features/backgroundTask/BackgroundTaskEventHandler").IBackgroundTaskEventHandler>;
36
+ };
37
+ export {};