@devrev/ts-adaas 1.12.3-beta.6 → 1.12.3-beta.7

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 (50) hide show
  1. package/dist/common/constants.d.ts +1 -0
  2. package/dist/common/constants.d.ts.map +1 -1
  3. package/dist/common/constants.js +2 -1
  4. package/dist/common/worker-memory.d.ts +59 -0
  5. package/dist/common/worker-memory.d.ts.map +1 -0
  6. package/dist/common/worker-memory.js +177 -0
  7. package/dist/tests/oom-handling/extraction.d.ts +11 -0
  8. package/dist/tests/oom-handling/extraction.d.ts.map +1 -0
  9. package/dist/tests/oom-handling/extraction.js +29 -0
  10. package/dist/tests/oom-handling/jest.setup.d.ts +7 -0
  11. package/dist/tests/oom-handling/jest.setup.d.ts.map +1 -0
  12. package/dist/tests/oom-handling/jest.setup.js +26 -0
  13. package/dist/tests/oom-handling/oom-after-emit-worker.d.ts +2 -0
  14. package/dist/tests/oom-handling/oom-after-emit-worker.d.ts.map +1 -0
  15. package/dist/tests/oom-handling/oom-after-emit-worker.js +35 -0
  16. package/dist/tests/oom-handling/oom-attachments-worker.d.ts +2 -0
  17. package/dist/tests/oom-handling/oom-attachments-worker.d.ts.map +1 -0
  18. package/dist/tests/oom-handling/oom-attachments-worker.js +29 -0
  19. package/dist/tests/oom-handling/oom-external-sync-units-worker.d.ts +2 -0
  20. package/dist/tests/oom-handling/oom-external-sync-units-worker.d.ts.map +1 -0
  21. package/dist/tests/oom-handling/oom-external-sync-units-worker.js +29 -0
  22. package/dist/tests/oom-handling/oom-gradual-worker.d.ts +2 -0
  23. package/dist/tests/oom-handling/oom-gradual-worker.d.ts.map +1 -0
  24. package/dist/tests/oom-handling/oom-gradual-worker.js +47 -0
  25. package/dist/tests/oom-handling/oom-handling.test.d.ts +2 -0
  26. package/dist/tests/oom-handling/oom-handling.test.d.ts.map +1 -0
  27. package/dist/tests/oom-handling/oom-handling.test.js +407 -0
  28. package/dist/tests/oom-handling/oom-metadata-worker.d.ts +2 -0
  29. package/dist/tests/oom-handling/oom-metadata-worker.d.ts.map +1 -0
  30. package/dist/tests/oom-handling/oom-metadata-worker.js +29 -0
  31. package/dist/tests/oom-handling/oom-worker.d.ts +2 -0
  32. package/dist/tests/oom-handling/oom-worker.d.ts.map +1 -0
  33. package/dist/tests/oom-handling/oom-worker.js +55 -0
  34. package/dist/tests/oom-handling/worker-memory.test.d.ts +2 -0
  35. package/dist/tests/oom-handling/worker-memory.test.d.ts.map +1 -0
  36. package/dist/tests/oom-handling/worker-memory.test.js +306 -0
  37. package/dist/types/common.d.ts +3 -0
  38. package/dist/types/common.d.ts.map +1 -1
  39. package/dist/types/workers.d.ts +58 -0
  40. package/dist/types/workers.d.ts.map +1 -1
  41. package/dist/uploader/uploader.d.ts.map +1 -1
  42. package/dist/uploader/uploader.js +13 -5
  43. package/dist/workers/create-worker.d.ts +20 -2
  44. package/dist/workers/create-worker.d.ts.map +1 -1
  45. package/dist/workers/create-worker.js +38 -3
  46. package/dist/workers/create-worker.test.js +113 -14
  47. package/dist/workers/spawn.d.ts +10 -1
  48. package/dist/workers/spawn.d.ts.map +1 -1
  49. package/dist/workers/spawn.js +77 -3
  50. package/package.json +2 -1
@@ -0,0 +1,407 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const extraction_1 = require("../../types/extraction");
7
+ const mock_server_1 = require("../mock-server");
8
+ const test_helpers_1 = require("../test-helpers");
9
+ const extraction_2 = __importDefault(require("./extraction"));
10
+ describe('OOM handling', () => {
11
+ let mockServer;
12
+ beforeAll(async () => {
13
+ mockServer = new mock_server_1.MockServer(3010);
14
+ await mockServer.start();
15
+ });
16
+ afterAll(async () => {
17
+ if (mockServer) {
18
+ await mockServer.stop();
19
+ }
20
+ });
21
+ beforeEach(() => {
22
+ jest.clearAllMocks();
23
+ mockServer.clearRequests();
24
+ });
25
+ describe('basic OOM detection', () => {
26
+ it('should emit error event with OOM details when worker runs out of memory', async () => {
27
+ const baseUrl = mockServer.getBaseUrl();
28
+ const event = (0, test_helpers_1.createEvent)({
29
+ eventType: extraction_1.EventType.StartExtractingData,
30
+ eventContextOverrides: {
31
+ callback_url: `${baseUrl}/internal/airdrop.external-extractor.message`,
32
+ worker_data_url: `${baseUrl}/internal/airdrop.external-worker`,
33
+ },
34
+ executionMetadataOverrides: {
35
+ devrev_endpoint: `${baseUrl}`,
36
+ },
37
+ });
38
+ // Run the OOM worker - this should trigger OOM and emit an error event
39
+ await (0, extraction_2.default)([event], __dirname + '/oom-worker');
40
+ const requests = mockServer.getRequests();
41
+ const lastRequest = requests[requests.length - 1];
42
+ // Expect last request to be emission of error event
43
+ expect(lastRequest.url).toContain('airdrop.external-extractor.message');
44
+ expect(lastRequest.method).toBe('POST');
45
+ // The event type should be an error event
46
+ expect(lastRequest.body.event_type).toBe(extraction_1.ExtractorEventType.DataExtractionError);
47
+ // The error should contain OOM information
48
+ expect(lastRequest.body.event_data).toBeDefined();
49
+ expect(lastRequest.body.event_data.error).toBeDefined();
50
+ expect(lastRequest.body.event_data.error.message).toContain('out of memory');
51
+ // OOM error info should be present
52
+ const oomErrorInfo = lastRequest.body.event_data.error.oom_error_info;
53
+ expect(oomErrorInfo).toBeDefined();
54
+ expect(oomErrorInfo.type).toBe('OOM_ERROR');
55
+ expect(oomErrorInfo.memoryLimitMb).toBeGreaterThan(0);
56
+ expect(oomErrorInfo.eventType).toBe(extraction_1.EventType.StartExtractingData);
57
+ }, 120000);
58
+ it('should keep parent thread stable when worker dies from OOM', async () => {
59
+ const baseUrl = mockServer.getBaseUrl();
60
+ const event = (0, test_helpers_1.createEvent)({
61
+ eventType: extraction_1.EventType.StartExtractingData,
62
+ eventContextOverrides: {
63
+ callback_url: `${baseUrl}/internal/airdrop.external-extractor.message`,
64
+ worker_data_url: `${baseUrl}/internal/airdrop.external-worker`,
65
+ },
66
+ executionMetadataOverrides: {
67
+ devrev_endpoint: `${baseUrl}`,
68
+ },
69
+ });
70
+ // Run the OOM worker
71
+ await (0, extraction_2.default)([event], __dirname + '/oom-worker');
72
+ // If we get here, the parent thread survived
73
+ expect(true).toBe(true);
74
+ // Verify we can still make requests (parent is functional)
75
+ const requests = mockServer.getRequests();
76
+ expect(requests.length).toBeGreaterThan(0);
77
+ }, 120000);
78
+ });
79
+ describe('OOM with alreadyEmitted', () => {
80
+ it('should handle OOM gracefully when worker has done some work before crashing', async () => {
81
+ var _a, _b, _c, _d;
82
+ const baseUrl = mockServer.getBaseUrl();
83
+ const event = (0, test_helpers_1.createEvent)({
84
+ eventType: extraction_1.EventType.StartExtractingData,
85
+ eventContextOverrides: {
86
+ callback_url: `${baseUrl}/internal/airdrop.external-extractor.message`,
87
+ worker_data_url: `${baseUrl}/internal/airdrop.external-worker`,
88
+ },
89
+ executionMetadataOverrides: {
90
+ devrev_endpoint: `${baseUrl}`,
91
+ },
92
+ });
93
+ // Run the worker that attempts to emit before causing OOM
94
+ // Use a larger memory limit (256MB) to give the worker time to initialize and emit
95
+ await (0, extraction_2.default)([event], __dirname + '/oom-after-emit-worker', {
96
+ testMemoryLimitMb: 256,
97
+ });
98
+ const requests = mockServer.getRequests();
99
+ // Filter for callback requests (event emissions)
100
+ const callbackRequests = requests.filter((r) => r.url.includes('airdrop.external-extractor.message') &&
101
+ r.method === 'POST');
102
+ // Should have at least one event (either progress or error)
103
+ // The key is that the system handles this gracefully without crashing
104
+ expect(callbackRequests.length).toBeGreaterThanOrEqual(1);
105
+ // Check if we got a progress event (worker emitted before OOM)
106
+ const progressRequest = callbackRequests.find((r) => { var _a; return ((_a = r.body) === null || _a === void 0 ? void 0 : _a.event_type) === extraction_1.ExtractorEventType.DataExtractionProgress; });
107
+ // Check if we got an error event (OOM error)
108
+ const errorRequest = callbackRequests.find((r) => { var _a; return ((_a = r.body) === null || _a === void 0 ? void 0 : _a.event_type) === extraction_1.ExtractorEventType.DataExtractionError; });
109
+ // At least one of these should be present
110
+ expect(progressRequest || errorRequest).toBeTruthy();
111
+ // If progress was emitted, verify it's valid
112
+ if (progressRequest) {
113
+ expect((_a = progressRequest.body) === null || _a === void 0 ? void 0 : _a.event_context).toBeDefined();
114
+ }
115
+ // If error was emitted, verify it contains OOM info
116
+ if (errorRequest) {
117
+ expect((_d = (_c = (_b = errorRequest.body) === null || _b === void 0 ? void 0 : _b.event_data) === null || _c === void 0 ? void 0 : _c.error) === null || _d === void 0 ? void 0 : _d.oom_error_info).toBeDefined();
118
+ }
119
+ }, 120000);
120
+ });
121
+ describe('gradual memory leak OOM', () => {
122
+ it('should detect OOM from gradual memory consumption', async () => {
123
+ const baseUrl = mockServer.getBaseUrl();
124
+ const event = (0, test_helpers_1.createEvent)({
125
+ eventType: extraction_1.EventType.StartExtractingData,
126
+ eventContextOverrides: {
127
+ callback_url: `${baseUrl}/internal/airdrop.external-extractor.message`,
128
+ worker_data_url: `${baseUrl}/internal/airdrop.external-worker`,
129
+ },
130
+ executionMetadataOverrides: {
131
+ devrev_endpoint: `${baseUrl}`,
132
+ },
133
+ });
134
+ await (0, extraction_2.default)([event], __dirname + '/oom-gradual-worker');
135
+ const requests = mockServer.getRequests();
136
+ const lastRequest = requests[requests.length - 1];
137
+ expect(lastRequest.url).toContain('airdrop.external-extractor.message');
138
+ expect(lastRequest.body.event_type).toBe(extraction_1.ExtractorEventType.DataExtractionError);
139
+ expect(lastRequest.body.event_data.error.oom_error_info).toBeDefined();
140
+ }, 120000);
141
+ });
142
+ describe('OOM with disabled memory limits', () => {
143
+ it('should still function when memory limits are disabled', async () => {
144
+ const baseUrl = mockServer.getBaseUrl();
145
+ const event = (0, test_helpers_1.createEvent)({
146
+ eventType: extraction_1.EventType.StartExtractingData,
147
+ eventContextOverrides: {
148
+ callback_url: `${baseUrl}/internal/airdrop.external-extractor.message`,
149
+ worker_data_url: `${baseUrl}/internal/airdrop.external-worker`,
150
+ },
151
+ executionMetadataOverrides: {
152
+ devrev_endpoint: `${baseUrl}`,
153
+ },
154
+ });
155
+ // Run with memory limits disabled - worker won't hit OOM limit
156
+ // but will eventually run out of system memory or hit timeout
157
+ // This test verifies the system doesn't crash when limits are disabled
158
+ await (0, extraction_2.default)([event], __dirname + '/oom-worker', {
159
+ enableMemoryLimits: false,
160
+ testMemoryLimitMb: undefined,
161
+ });
162
+ // Parent should still be functional after worker exits
163
+ const requests = mockServer.getRequests();
164
+ expect(requests.length).toBeGreaterThan(0);
165
+ }, 120000);
166
+ });
167
+ describe('OOM for different event types', () => {
168
+ it('should handle OOM during metadata extraction', async () => {
169
+ const baseUrl = mockServer.getBaseUrl();
170
+ const event = (0, test_helpers_1.createEvent)({
171
+ eventType: extraction_1.EventType.StartExtractingMetadata,
172
+ eventContextOverrides: {
173
+ callback_url: `${baseUrl}/internal/airdrop.external-extractor.message`,
174
+ worker_data_url: `${baseUrl}/internal/airdrop.external-worker`,
175
+ },
176
+ executionMetadataOverrides: {
177
+ devrev_endpoint: `${baseUrl}`,
178
+ },
179
+ });
180
+ await (0, extraction_2.default)([event], __dirname + '/oom-metadata-worker');
181
+ const requests = mockServer.getRequests();
182
+ const lastRequest = requests[requests.length - 1];
183
+ expect(lastRequest.body.event_type).toBe(extraction_1.ExtractorEventType.MetadataExtractionError);
184
+ expect(lastRequest.body.event_data.error.oom_error_info).toBeDefined();
185
+ expect(lastRequest.body.event_data.error.oom_error_info.eventType).toBe(extraction_1.EventType.StartExtractingMetadata);
186
+ }, 120000);
187
+ it('should handle OOM during attachments extraction', async () => {
188
+ const baseUrl = mockServer.getBaseUrl();
189
+ const event = (0, test_helpers_1.createEvent)({
190
+ eventType: extraction_1.EventType.StartExtractingAttachments,
191
+ eventContextOverrides: {
192
+ callback_url: `${baseUrl}/internal/airdrop.external-extractor.message`,
193
+ worker_data_url: `${baseUrl}/internal/airdrop.external-worker`,
194
+ },
195
+ executionMetadataOverrides: {
196
+ devrev_endpoint: `${baseUrl}`,
197
+ },
198
+ });
199
+ await (0, extraction_2.default)([event], __dirname + '/oom-attachments-worker');
200
+ const requests = mockServer.getRequests();
201
+ const lastRequest = requests[requests.length - 1];
202
+ expect(lastRequest.body.event_type).toBe(extraction_1.ExtractorEventType.AttachmentExtractionError);
203
+ expect(lastRequest.body.event_data.error.oom_error_info).toBeDefined();
204
+ expect(lastRequest.body.event_data.error.oom_error_info.eventType).toBe(extraction_1.EventType.StartExtractingAttachments);
205
+ }, 120000);
206
+ it('should handle OOM during external sync units extraction', async () => {
207
+ const baseUrl = mockServer.getBaseUrl();
208
+ const event = (0, test_helpers_1.createEvent)({
209
+ eventType: extraction_1.EventType.StartExtractingExternalSyncUnits,
210
+ eventContextOverrides: {
211
+ callback_url: `${baseUrl}/internal/airdrop.external-extractor.message`,
212
+ worker_data_url: `${baseUrl}/internal/airdrop.external-worker`,
213
+ },
214
+ executionMetadataOverrides: {
215
+ devrev_endpoint: `${baseUrl}`,
216
+ },
217
+ });
218
+ await (0, extraction_2.default)([event], __dirname + '/oom-external-sync-units-worker');
219
+ const requests = mockServer.getRequests();
220
+ const lastRequest = requests[requests.length - 1];
221
+ expect(lastRequest.body.event_type).toBe(extraction_1.ExtractorEventType.ExternalSyncUnitExtractionError);
222
+ expect(lastRequest.body.event_data.error.oom_error_info).toBeDefined();
223
+ expect(lastRequest.body.event_data.error.oom_error_info.eventType).toBe(extraction_1.EventType.StartExtractingExternalSyncUnits);
224
+ }, 120000);
225
+ });
226
+ describe('OOM error info completeness', () => {
227
+ it('should include all required fields in OOM error info', async () => {
228
+ const baseUrl = mockServer.getBaseUrl();
229
+ const event = (0, test_helpers_1.createEvent)({
230
+ eventType: extraction_1.EventType.StartExtractingData,
231
+ eventContextOverrides: {
232
+ callback_url: `${baseUrl}/internal/airdrop.external-extractor.message`,
233
+ worker_data_url: `${baseUrl}/internal/airdrop.external-worker`,
234
+ },
235
+ executionMetadataOverrides: {
236
+ devrev_endpoint: `${baseUrl}`,
237
+ },
238
+ });
239
+ await (0, extraction_2.default)([event], __dirname + '/oom-worker');
240
+ const requests = mockServer.getRequests();
241
+ const lastRequest = requests[requests.length - 1];
242
+ const oomErrorInfo = lastRequest.body.event_data.error.oom_error_info;
243
+ // Verify all required fields are present
244
+ expect(oomErrorInfo).toHaveProperty('type', 'OOM_ERROR');
245
+ expect(oomErrorInfo).toHaveProperty('message');
246
+ expect(oomErrorInfo).toHaveProperty('memoryLimitMb');
247
+ expect(oomErrorInfo).toHaveProperty('totalAvailableMemoryMb');
248
+ expect(oomErrorInfo).toHaveProperty('isLambda');
249
+ expect(oomErrorInfo).toHaveProperty('isLocalDevelopment');
250
+ expect(oomErrorInfo).toHaveProperty('exitCode');
251
+ expect(oomErrorInfo).toHaveProperty('eventType');
252
+ // Verify types
253
+ expect(typeof oomErrorInfo.message).toBe('string');
254
+ expect(typeof oomErrorInfo.memoryLimitMb).toBe('number');
255
+ expect(typeof oomErrorInfo.totalAvailableMemoryMb).toBe('number');
256
+ expect(typeof oomErrorInfo.isLambda).toBe('boolean');
257
+ expect(typeof oomErrorInfo.isLocalDevelopment).toBe('boolean');
258
+ expect(typeof oomErrorInfo.exitCode).toBe('number');
259
+ }, 120000);
260
+ it('should correctly identify local development environment in OOM info', async () => {
261
+ const baseUrl = mockServer.getBaseUrl();
262
+ const event = (0, test_helpers_1.createEvent)({
263
+ eventType: extraction_1.EventType.StartExtractingData,
264
+ eventContextOverrides: {
265
+ callback_url: `${baseUrl}/internal/airdrop.external-extractor.message`,
266
+ worker_data_url: `${baseUrl}/internal/airdrop.external-worker`,
267
+ },
268
+ executionMetadataOverrides: {
269
+ devrev_endpoint: `${baseUrl}`,
270
+ },
271
+ });
272
+ await (0, extraction_2.default)([event], __dirname + '/oom-worker');
273
+ const requests = mockServer.getRequests();
274
+ const lastRequest = requests[requests.length - 1];
275
+ const oomErrorInfo = lastRequest.body.event_data.error.oom_error_info;
276
+ // Test runs in local development mode
277
+ expect(oomErrorInfo.isLocalDevelopment).toBe(true);
278
+ expect(oomErrorInfo.isLambda).toBe(false);
279
+ }, 120000);
280
+ });
281
+ describe('memory limit edge cases', () => {
282
+ it('should handle very small memory limit (32MB)', async () => {
283
+ const baseUrl = mockServer.getBaseUrl();
284
+ const event = (0, test_helpers_1.createEvent)({
285
+ eventType: extraction_1.EventType.StartExtractingData,
286
+ eventContextOverrides: {
287
+ callback_url: `${baseUrl}/internal/airdrop.external-extractor.message`,
288
+ worker_data_url: `${baseUrl}/internal/airdrop.external-worker`,
289
+ },
290
+ executionMetadataOverrides: {
291
+ devrev_endpoint: `${baseUrl}`,
292
+ },
293
+ });
294
+ // Use a very small memory limit
295
+ await (0, extraction_2.default)([event], __dirname + '/oom-worker', {
296
+ testMemoryLimitMb: 32,
297
+ });
298
+ const requests = mockServer.getRequests();
299
+ const lastRequest = requests[requests.length - 1];
300
+ expect(lastRequest.body.event_type).toBe(extraction_1.ExtractorEventType.DataExtractionError);
301
+ expect(lastRequest.body.event_data.error.oom_error_info.memoryLimitMb).toBe(32);
302
+ }, 120000);
303
+ it('should handle moderate memory limit (128MB)', async () => {
304
+ const baseUrl = mockServer.getBaseUrl();
305
+ const event = (0, test_helpers_1.createEvent)({
306
+ eventType: extraction_1.EventType.StartExtractingData,
307
+ eventContextOverrides: {
308
+ callback_url: `${baseUrl}/internal/airdrop.external-extractor.message`,
309
+ worker_data_url: `${baseUrl}/internal/airdrop.external-worker`,
310
+ },
311
+ executionMetadataOverrides: {
312
+ devrev_endpoint: `${baseUrl}`,
313
+ },
314
+ });
315
+ await (0, extraction_2.default)([event], __dirname + '/oom-worker', {
316
+ testMemoryLimitMb: 128,
317
+ });
318
+ const requests = mockServer.getRequests();
319
+ const lastRequest = requests[requests.length - 1];
320
+ expect(lastRequest.body.event_type).toBe(extraction_1.ExtractorEventType.DataExtractionError);
321
+ expect(lastRequest.body.event_data.error.oom_error_info.memoryLimitMb).toBe(128);
322
+ }, 120000);
323
+ });
324
+ describe('memory monitoring during OOM', () => {
325
+ it('should not cause issues when memory monitoring runs during OOM buildup', async () => {
326
+ const baseUrl = mockServer.getBaseUrl();
327
+ const event = (0, test_helpers_1.createEvent)({
328
+ eventType: extraction_1.EventType.StartExtractingData,
329
+ eventContextOverrides: {
330
+ callback_url: `${baseUrl}/internal/airdrop.external-extractor.message`,
331
+ worker_data_url: `${baseUrl}/internal/airdrop.external-worker`,
332
+ },
333
+ executionMetadataOverrides: {
334
+ devrev_endpoint: `${baseUrl}`,
335
+ },
336
+ });
337
+ // Gradual worker gives time for memory monitoring to run
338
+ await (0, extraction_2.default)([event], __dirname + '/oom-gradual-worker');
339
+ // Parent thread should still be functional
340
+ const requests = mockServer.getRequests();
341
+ expect(requests.length).toBeGreaterThan(0);
342
+ // Should still receive OOM error
343
+ const lastRequest = requests[requests.length - 1];
344
+ expect(lastRequest.body.event_type).toBe(extraction_1.ExtractorEventType.DataExtractionError);
345
+ }, 120000);
346
+ it('should properly clean up after OOM and allow subsequent operations', async () => {
347
+ const baseUrl = mockServer.getBaseUrl();
348
+ // First OOM event
349
+ const event1 = (0, test_helpers_1.createEvent)({
350
+ eventType: extraction_1.EventType.StartExtractingData,
351
+ eventContextOverrides: {
352
+ callback_url: `${baseUrl}/internal/airdrop.external-extractor.message`,
353
+ worker_data_url: `${baseUrl}/internal/airdrop.external-worker`,
354
+ },
355
+ executionMetadataOverrides: {
356
+ devrev_endpoint: `${baseUrl}`,
357
+ },
358
+ });
359
+ await (0, extraction_2.default)([event1], __dirname + '/oom-worker');
360
+ const requestsAfterFirst = mockServer.getRequests().length;
361
+ // Clear and run second OOM event
362
+ mockServer.clearRequests();
363
+ const event2 = (0, test_helpers_1.createEvent)({
364
+ eventType: extraction_1.EventType.StartExtractingData,
365
+ eventContextOverrides: {
366
+ callback_url: `${baseUrl}/internal/airdrop.external-extractor.message`,
367
+ worker_data_url: `${baseUrl}/internal/airdrop.external-worker`,
368
+ },
369
+ executionMetadataOverrides: {
370
+ devrev_endpoint: `${baseUrl}`,
371
+ },
372
+ });
373
+ await (0, extraction_2.default)([event2], __dirname + '/oom-worker');
374
+ const requestsAfterSecond = mockServer.getRequests().length;
375
+ // Both runs should have completed and made requests
376
+ expect(requestsAfterFirst).toBeGreaterThan(0);
377
+ expect(requestsAfterSecond).toBeGreaterThan(0);
378
+ // Both should have emitted OOM errors
379
+ const lastRequest = mockServer.getRequests()[mockServer.getRequests().length - 1];
380
+ expect(lastRequest.body.event_type).toBe(extraction_1.ExtractorEventType.DataExtractionError);
381
+ }, 240000); // 4 minute timeout for two OOM tests
382
+ });
383
+ describe('race condition handling', () => {
384
+ it('should handle rapid OOM (error before any logging)', async () => {
385
+ const baseUrl = mockServer.getBaseUrl();
386
+ const event = (0, test_helpers_1.createEvent)({
387
+ eventType: extraction_1.EventType.StartExtractingData,
388
+ eventContextOverrides: {
389
+ callback_url: `${baseUrl}/internal/airdrop.external-extractor.message`,
390
+ worker_data_url: `${baseUrl}/internal/airdrop.external-worker`,
391
+ },
392
+ executionMetadataOverrides: {
393
+ devrev_endpoint: `${baseUrl}`,
394
+ },
395
+ });
396
+ // Very small limit causes near-instant OOM
397
+ await (0, extraction_2.default)([event], __dirname + '/oom-worker', {
398
+ testMemoryLimitMb: 16,
399
+ });
400
+ const requests = mockServer.getRequests();
401
+ const lastRequest = requests[requests.length - 1];
402
+ // Should still properly detect and report OOM
403
+ expect(lastRequest.body.event_type).toBe(extraction_1.ExtractorEventType.DataExtractionError);
404
+ expect(lastRequest.body.event_data.error.oom_error_info).toBeDefined();
405
+ }, 120000);
406
+ });
407
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=oom-metadata-worker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oom-metadata-worker.d.ts","sourceRoot":"","sources":["../../../src/tests/oom-handling/oom-metadata-worker.ts"],"names":[],"mappings":""}
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const index_1 = require("../../index");
4
+ /* eslint-disable @typescript-eslint/require-await */
5
+ /* eslint-disable @typescript-eslint/no-unused-vars */
6
+ /**
7
+ * Worker that causes OOM during metadata extraction.
8
+ * Tests OOM handling for EventType.StartExtractingMetadata.
9
+ */
10
+ (0, index_1.processTask)({
11
+ task: async ({ adapter }) => {
12
+ console.log('OOM metadata worker starting');
13
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
14
+ const memoryHog = [];
15
+ while (true) {
16
+ const chunk = [];
17
+ for (let i = 0; i < 10000; i++) {
18
+ chunk.push({
19
+ data: 'metadata'.repeat(12),
20
+ index: i,
21
+ });
22
+ }
23
+ memoryHog.push(chunk);
24
+ }
25
+ },
26
+ onTimeout: async ({ adapter }) => {
27
+ await adapter.emit(index_1.ExtractorEventType.MetadataExtractionDone);
28
+ },
29
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=oom-worker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oom-worker.d.ts","sourceRoot":"","sources":["../../../src/tests/oom-handling/oom-worker.ts"],"names":[],"mappings":""}
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const index_1 = require("../../index");
4
+ /**
5
+ * Worker that intentionally causes an OOM error by allocating large arrays.
6
+ * This is used to test OOM detection and handling in the parent thread.
7
+ *
8
+ * Note: We use JavaScript arrays/objects to allocate V8 heap memory,
9
+ * not Buffers (which use external memory and aren't limited by resourceLimits).
10
+ */
11
+ (0, index_1.processTask)({
12
+ task: async ({ adapter }) => {
13
+ console.log('OOM worker starting - will intentionally cause OOM');
14
+ // Array to hold references to prevent garbage collection
15
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
16
+ const memoryHog = [];
17
+ try {
18
+ // Allocate memory in chunks until we run out
19
+ // Each chunk is approximately 1MB of heap memory (array of objects)
20
+ let totalAllocated = 0;
21
+ while (true) {
22
+ // Create an array of objects to consume V8 heap memory
23
+ // Each object with strings consumes heap memory
24
+ const chunk = [];
25
+ for (let i = 0; i < 10000; i++) {
26
+ chunk.push({
27
+ data: 'x'.repeat(100), // 100 bytes per string
28
+ index: i,
29
+ });
30
+ }
31
+ memoryHog.push(chunk);
32
+ totalAllocated += 1; // Approximately 1MB per chunk
33
+ if (totalAllocated % 10 === 0) {
34
+ console.log(`Allocated approximately ${totalAllocated}MB of heap memory`);
35
+ }
36
+ // Small delay to allow logging (but not too long to avoid timeout)
37
+ if (totalAllocated % 50 === 0) {
38
+ await new Promise((resolve) => setTimeout(resolve, 1));
39
+ }
40
+ }
41
+ }
42
+ catch (error) {
43
+ // This catch block may not be reached if OOM kills the process
44
+ console.error('Error during memory allocation:', error);
45
+ await adapter.emit(index_1.ExtractorEventType.DataExtractionError, {
46
+ error: { message: 'Memory allocation failed' },
47
+ });
48
+ }
49
+ },
50
+ onTimeout: async ({ adapter }) => {
51
+ // This should not be called in OOM scenario
52
+ console.log('OOM worker timeout handler called');
53
+ await adapter.emit(index_1.ExtractorEventType.DataExtractionProgress);
54
+ },
55
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=worker-memory.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker-memory.test.d.ts","sourceRoot":"","sources":["../../../src/tests/oom-handling/worker-memory.test.ts"],"names":[],"mappings":""}