Skip to content

Commit aa550ed

Browse files
authored
fix: wait for persist state listeners to run when event manager closes (#1481)
1 parent 7eb6197 commit aa550ed

File tree

9 files changed

+120
-2
lines changed

9 files changed

+120
-2
lines changed

packages/core/src/events/event_manager.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ export abstract class EventManager {
5858

5959
// Emit final PERSIST_STATE event
6060
this.emit(EventType.PERSIST_STATE, { isMigrating: false });
61+
62+
// Wait for PERSIST_STATE to process
63+
await this.waitForAllListenersToComplete();
6164
}
6265

6366
on(event: EventTypeName, listener: (...args: any[]) => any): void {
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.idea
2+
.DS_Store
3+
node_modules
4+
package-lock.json
5+
apify_storage
6+
crawlee_storage
7+
storage
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
FROM apify/actor-node:16
2+
3+
COPY packages ./packages
4+
COPY package*.json ./
5+
6+
RUN npm --quiet set progress=false \
7+
&& npm install --only=prod --no-optional \
8+
&& echo "Installed NPM packages:" \
9+
&& (npm list --only=prod --no-optional --all || true) \
10+
&& echo "Node.js version:" \
11+
&& node --version \
12+
&& echo "NPM version:" \
13+
&& npm --version
14+
15+
COPY . ./
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"name": "test-automatic-persist-value",
3+
"version": "0.0",
4+
"buildTag": "latest",
5+
"env": null
6+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { Actor, KeyValueStore } from 'apify';
2+
import { ApifyStorageLocal } from '@apify/storage-local';
3+
import { BasicCrawler } from '@crawlee/basic';
4+
5+
const mainOptions = {
6+
exit: Actor.isAtHome(),
7+
storage: process.env.STORAGE_IMPLEMENTATION === 'LOCAL' ? new ApifyStorageLocal() : undefined,
8+
};
9+
10+
await Actor.main(async () => {
11+
const kv = await KeyValueStore.open('test');
12+
13+
const crawler = new BasicCrawler({
14+
async requestHandler() {
15+
const automaticValue = await kv.getAutoSavedValue('hello');
16+
17+
automaticValue.crawlee = 'awesome!';
18+
},
19+
});
20+
21+
await crawler.run(['https://example.com']);
22+
}, mainOptions);
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"name": "test-automatic-persist-value",
3+
"version": "0.0.1",
4+
"description": "Key-Value Store - Automatic Persist Value Test",
5+
"dependencies": {
6+
"apify": "^3.0.0-beta.75",
7+
"@apify/storage-local": "^2.1.0",
8+
"@crawlee/basic": "file:./packages/basic-crawler",
9+
"@crawlee/core": "file:./packages/core",
10+
"@crawlee/memory-storage": "file:./packages/memory-storage",
11+
"@crawlee/types": "file:./packages/types",
12+
"@crawlee/utils": "file:./packages/utils"
13+
},
14+
"scripts": { "start": "node main.js" },
15+
"type": "module",
16+
"license": "ISC"
17+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { initialize, expect, getActorTestDir, runActor, getKeyValueStoreItems } from '../tools.mjs';
2+
3+
const testActorDirname = getActorTestDir(import.meta.url);
4+
await initialize(testActorDirname);
5+
6+
await runActor(testActorDirname);
7+
8+
const kvsItems = await getKeyValueStoreItems(testActorDirname, 'test');
9+
10+
expect(kvsItems.length === 1, 'Key-value store automatically saved the value expected to be auto-saved');
11+
12+
const [{ name, raw }] = kvsItems;
13+
14+
expect(name === 'crawlee', 'Key-value store auto-saved value is named "crawlee"');
15+
16+
const parsed = JSON.parse(raw.toString());
17+
18+
expect(typeof parsed === 'object' && parsed !== null, 'Key-value store auto-save value is a non-nullable object');
19+
expect(parsed.crawlee === 'awesome!', 'Key-value store auto-save value has a property "crawlee" that is set to "awesome!"');

test/e2e/autoscaling-max-tasks-per-minute/actor/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
"apify": "^3.0.0-beta.75",
77
"@apify/storage-local": "^2.1.0",
88
"@crawlee/basic": "file:./packages/basic-crawler",
9-
"@crawlee/browser-pool": "file:./packages/browser-pool",
109
"@crawlee/core": "file:./packages/core",
1110
"@crawlee/memory-storage": "file:./packages/memory-storage",
1211
"@crawlee/types": "file:./packages/types",

test/e2e/tools.mjs

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import { dirname, join, resolve } from 'node:path';
22
import { fileURLToPath } from 'node:url';
33
import { existsSync } from 'node:fs';
4-
import { readdir } from 'node:fs/promises';
4+
import { readdir, readFile } from 'node:fs/promises';
55
import { homedir } from 'node:os';
66
import { setTimeout } from 'node:timers/promises';
77
import { execSync } from 'node:child_process';
88
import fs from 'fs-extra';
99
import { Actor } from 'apify';
10+
// eslint-disable-next-line import/no-relative-packages
1011
import { URL_NO_COMMAS_REGEX } from '../../packages/utils/dist/index.mjs';
1112

1213
export const SKIPPED_TEST_CLOSE_CODE = 404;
@@ -207,6 +208,35 @@ export async function getDatasetItems(dirName) {
207208
return datasetItems;
208209
}
209210

211+
/**
212+
* Gets all items in the key-value store, as a Buffer
213+
* @param {string} dirName
214+
* @param {string} storeName
215+
*/
216+
export async function getKeyValueStoreItems(dirName, storeName) {
217+
const dir = getStorage(dirName);
218+
const storePath = join(dir, `key_value_stores/${storeName}`);
219+
220+
if (!existsSync(storePath)) {
221+
return [];
222+
}
223+
224+
const dirents = await readdir(storePath, { withFileTypes: true });
225+
const fileNames = dirents.filter((dirent) => dirent.isFile());
226+
const keyValueStoreRecords = [];
227+
228+
for (const fileName of fileNames) {
229+
if (fileName.name.includes('__metadata__')) continue;
230+
231+
const filePath = join(storePath, fileName.name);
232+
const buffer = await readFile(filePath);
233+
234+
keyValueStoreRecords.push({ name: fileName.name.split('.').slice(0, -1).join('.'), raw: buffer });
235+
}
236+
237+
return keyValueStoreRecords;
238+
}
239+
210240
/**
211241
* @param {string} dirName
212242
*/

0 commit comments

Comments
 (0)