perf(scenario-scheduler): parallelize stale-run cleanup on startup with effect

- use Effect.forEach with concurrency:10 to fail up to 10 runs in parallel
- isolate each failRun with catchAll so one failure doesn't abort the rest
- log unexpected failRun errors per run without halting the batch
This commit is contained in:
2026-04-17 14:32:25 +03:00
parent 2a46b355db
commit a199700878
3 changed files with 73 additions and 5 deletions
+49 -1
View File
@@ -3979,7 +3979,6 @@
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz",
"integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==",
"dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/@storybook/addon-a11y": { "node_modules/@storybook/addon-a11y": {
@@ -7219,6 +7218,16 @@
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/effect": {
"version": "3.21.0",
"resolved": "https://registry.npmjs.org/effect/-/effect-3.21.0.tgz",
"integrity": "sha512-PPN80qRokCd1f015IANNhrwOnLO7GrrMQfk4/lnZRE/8j7UPWrNNjPV0uBrZutI/nHzernbW+J0hdqQysHiSnQ==",
"license": "MIT",
"dependencies": {
"@standard-schema/spec": "^1.0.0",
"fast-check": "^3.23.1"
}
},
"node_modules/electron-to-chromium": { "node_modules/electron-to-chromium": {
"version": "1.5.332", "version": "1.5.332",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.332.tgz", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.332.tgz",
@@ -8118,6 +8127,44 @@
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/fast-check": {
"version": "3.23.2",
"resolved": "https://registry.npmjs.org/fast-check/-/fast-check-3.23.2.tgz",
"integrity": "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/dubzzz"
},
{
"type": "opencollective",
"url": "https://opencollective.com/fast-check"
}
],
"license": "MIT",
"dependencies": {
"pure-rand": "^6.1.0"
},
"engines": {
"node": ">=8.0.0"
}
},
"node_modules/fast-check/node_modules/pure-rand": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz",
"integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/dubzzz"
},
{
"type": "opencollective",
"url": "https://opencollective.com/fast-check"
}
],
"license": "MIT"
},
"node_modules/fast-deep-equal": { "node_modules/fast-deep-equal": {
"version": "3.1.3", "version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@@ -15987,6 +16034,7 @@
"better-sqlite3": "^12.8.0", "better-sqlite3": "^12.8.0",
"class-transformer": "^0.5.1", "class-transformer": "^0.5.1",
"class-validator": "^0.14.4", "class-validator": "^0.14.4",
"effect": "^3.21.0",
"jsdom": "^29.0.2", "jsdom": "^29.0.2",
"playwright": "^1.59.1", "playwright": "^1.59.1",
"reflect-metadata": "^0.2.2", "reflect-metadata": "^0.2.2",
+1
View File
@@ -32,6 +32,7 @@
"better-sqlite3": "^12.8.0", "better-sqlite3": "^12.8.0",
"class-transformer": "^0.5.1", "class-transformer": "^0.5.1",
"class-validator": "^0.14.4", "class-validator": "^0.14.4",
"effect": "^3.21.0",
"jsdom": "^29.0.2", "jsdom": "^29.0.2",
"playwright": "^1.59.1", "playwright": "^1.59.1",
"reflect-metadata": "^0.2.2", "reflect-metadata": "^0.2.2",
@@ -2,6 +2,7 @@ import { Injectable, OnModuleInit } from "@nestjs/common";
import { Interval } from "@nestjs/schedule"; import { Interval } from "@nestjs/schedule";
import { InjectRepository } from "@nestjs/typeorm"; import { InjectRepository } from "@nestjs/typeorm";
import * as crypto from "crypto"; import * as crypto from "crypto";
import { Effect } from "effect";
import type { Browser, BrowserContext, Page } from "playwright"; import type { Browser, BrowserContext, Page } from "playwright";
import { chromium } from "playwright"; import { chromium } from "playwright";
import { Repository } from "typeorm"; import { Repository } from "typeorm";
@@ -76,11 +77,29 @@ export class ScenarioSchedulerService implements OnModuleInit {
this.logger.error( this.logger.error(
`Run #${run.id}: marked fail due to server restart (playwright session lost)`, `Run #${run.id}: marked fail due to server restart (playwright session lost)`,
); );
await this.failRun(
run.id,
"Run aborted: server restarted and playwright session could not be recovered",
);
} }
await Effect.runPromise(
Effect.forEach(
staleRuns,
(run) =>
Effect.tryPromise(() =>
this.failRun(
run.id,
"Run aborted: server restarted and playwright session could not be recovered",
),
).pipe(
Effect.catchAll((err) =>
Effect.sync(() =>
this.logger.error(
`Run #${run.id}: failRun threw unexpectedly — ${String(err)}`,
),
),
),
),
{ concurrency: 10 },
),
);
} }
/** /**