Skip to content

Commit 5851a7d

Browse files
feat: ensure max state memory size (#1458)
* feat: ensure max state memory * tests: add tests for state size limit * tests: fix flaky test on dataclip loading * chore: remove log * chore: update changeset * versions * reduce size of state objects Down from 20% of allow memory to 15% * log state sizes * typo --------- Co-authored-by: Joe Clark <jclark@openfn.org>
1 parent 216614a commit 5851a7d

22 files changed

Lines changed: 204 additions & 11 deletions

File tree

integration-tests/worker/CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,18 @@
11
# @openfn/integration-tests-worker
22

3+
## 1.1.0
4+
5+
### Minor Changes
6+
7+
- 8ebc086: Add support for state memory limit via worker
8+
9+
### Patch Changes
10+
11+
- Updated dependencies [8ebc086]
12+
- @openfn/engine-multi@1.12.0
13+
- @openfn/ws-worker@1.27.0
14+
- @openfn/lightning-mock@2.4.20
15+
316
## 1.0.96
417

518
### Patch Changes

integration-tests/worker/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@openfn/integration-tests-worker",
33
"private": true,
4-
"version": "1.0.96",
4+
"version": "1.1.0",
55
"description": "Lightning WOrker integration tests",
66
"author": "Open Function Group <admin@openfn.org>",
77
"license": "ISC",

integration-tests/worker/test/exit-reasons.test.ts

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,92 @@ test.serial('kill: oom (large, kill vm)', async (t) => {
226226
t.is(error_message, 'Run exceeded maximum memory usage');
227227
});
228228

229+
test.serial('kill: state exceeds the configured state limit', async (t) => {
230+
const attempt = {
231+
id: crypto.randomUUID(),
232+
jobs: [
233+
{
234+
adaptor: '@openfn/language-common@latest',
235+
// ~2mb string for a 1mb limit
236+
body: `fn((s) => {
237+
s.data = new Array(2 * 1024 * 1024).fill('a').join('');
238+
return s;
239+
})`,
240+
},
241+
],
242+
options: {
243+
state_limit_mb: 1,
244+
},
245+
};
246+
247+
const result = await run(attempt);
248+
249+
const { reason, error_type, error_message } = result;
250+
t.is(reason, 'kill');
251+
t.is(error_type, 'StateTooLargeError');
252+
t.regex(error_message, /State exceeds the limit of 1mb/);
253+
});
254+
255+
test.serial(
256+
'kill: state limit is enforced between jobs (downstream job does not run)',
257+
async (t) => {
258+
const jobOne = {
259+
id: crypto.randomUUID(),
260+
adaptor: '@openfn/language-common@latest',
261+
// ~2mb state, over the 1mb limit set below
262+
body: `fn((s) => {
263+
s.data = new Array(2 * 1024 * 1024).fill('a').join('');
264+
return s;
265+
})`,
266+
};
267+
268+
// not expected to run because the first job is expected to trigger state size crash
269+
const jobTwo = {
270+
id: crypto.randomUUID(),
271+
adaptor: '@openfn/language-common@latest',
272+
body: `fn(() => ({ data: 'ok' }))`,
273+
};
274+
275+
const attempt = {
276+
id: crypto.randomUUID(),
277+
jobs: [jobOne, jobTwo],
278+
edges: [
279+
{
280+
id: crypto.randomUUID(),
281+
source_job_id: jobOne.id,
282+
target_job_id: jobTwo.id,
283+
condition: 'always',
284+
},
285+
],
286+
options: {
287+
state_limit_mb: 1,
288+
},
289+
};
290+
291+
const startedJobs: string[] = [];
292+
const unsubscribe = lightning.onSocketEvent(
293+
'step:start',
294+
attempt.id,
295+
(evt) => {
296+
if (evt.runId === attempt.id) {
297+
startedJobs.push(evt.payload.job_id);
298+
}
299+
},
300+
false
301+
);
302+
303+
const result = await run(attempt);
304+
unsubscribe();
305+
306+
const { reason, error_type, error_message } = result;
307+
t.is(reason, 'kill');
308+
t.is(error_type, 'StateTooLargeError');
309+
t.regex(error_message, /State exceeds the limit of 1mb/);
310+
311+
t.deepEqual(startedJobs, [jobOne.id]);
312+
}
313+
);
314+
229315
test.serial('crash: process.exit() triggered by postgres', async (t) => {
230316
const attempt = {
231317
id: crypto.randomUUID(),

packages/engine-multi/CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
# engine-multi
22

3+
## 1.12.0
4+
5+
### Minor Changes
6+
7+
- 8ebc086: Add support for state memory limit via worker
8+
- reduce default size of state objects
9+
10+
### Patch Changes
11+
12+
- Updated dependencies [8ebc086]
13+
- @openfn/lexicon@2.3.0
14+
315
## 1.11.4
416

517
### Patch Changes

packages/engine-multi/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@openfn/engine-multi",
3-
"version": "1.11.4",
3+
"version": "1.12.0",
44
"description": "Multi-process runtime engine",
55
"main": "dist/index.js",
66
"type": "module",

packages/engine-multi/src/api/execute.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ const execute = async (context: ExecutionContext) => {
5050
// This must be fairly high to prevent crashes
5151
stateLimitMb:
5252
options.stateLimitMb ??
53-
Math.max((options.memoryLimitMb ?? 1000) * 0.25),
53+
Math.max(50, options.memoryLimitMb ?? 1000 * 0.15),
5454
} as RunOptions;
5555

5656
logger.debug(

packages/engine-multi/src/engine.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ export type EngineOptions = {
7474
logger: Logger;
7575
maxWorkers?: number;
7676
memoryLimitMb?: number;
77+
stateLimitMb?: number;
7778
payloadLimitMb?: number;
7879
logPayloadLimitMb?: number;
7980
repoDir: string;
@@ -169,7 +170,7 @@ const createEngine = async (
169170
callWorker,
170171
options: {
171172
...options,
172-
stateLimitMb: opts.stateLimitMb,
173+
stateLimitMb: opts.stateLimitMb ?? options.stateLimitMb,
173174
sanitize: opts.sanitize,
174175
resolvers: opts.resolvers,
175176
runTimeoutMs: opts.runTimeoutMs ?? defaultTimeout,

packages/lexicon/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# lexicon
22

3+
## 2.3.0
4+
5+
### Minor Changes
6+
7+
- 8ebc086: Add support for state memory limit via worker
8+
39
## 2.2.1
410

511
### Patch Changes

packages/lexicon/lightning.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ export type LightningPlanOptions = {
5959
output_dataclips?: boolean;
6060

6161
run_memory_limit_mb?: number;
62+
state_limit_mb?: number;
6263
payload_limit_mb?: number;
6364
log_payload_limit_mb?: number;
6465
job_log_level?: LogLevel;

packages/lexicon/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@openfn/lexicon",
3-
"version": "2.2.1",
3+
"version": "2.3.0",
44
"description": "Central repo of names and type definitions",
55
"author": "Open Function Group <admin@openfn.org>",
66
"license": "ISC",

0 commit comments

Comments
 (0)