Skip to content

Commit 3c16855

Browse files
lundibunditargos
authored andcommitted
test: unflake async hooks statwatcher test
Under high load 2 types of issues arise with this test. * filesystem calls gets queued even when the 'sync' is used which leads to async_hooks being called with the events of tmpdir clean or initial file write after clean. This is solved by counting all 'change' calls while making sure there is no dependency of StatWatcher's on one another and the expected changes are waited for. * some events are getting lost with the current clean->write->write_and_watch strategy. Specifically I observed the file size going from 0 to 5 entirely skipping 3 even though the write call was there (this happened reliably on -j128). So I've changed the strategy to avoid additional write considering this still tests the hooks correctly. This may indicate some sort of bug in async_hooks though I'm not sure. Closes: #21425 PR-URL: #30362 Fixes: #21425 Reviewed-By: Stephen Belanger <[email protected]> Reviewed-By: Rich Trott <[email protected]> Reviewed-By: Richard Lau <[email protected]> Reviewed-By: Vladimir de Turckheim <[email protected]>
1 parent 734b550 commit 3c16855

File tree

2 files changed

+53
-42
lines changed

2 files changed

+53
-42
lines changed

test/async-hooks/async-hooks.status

-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ prefix async-hooks
55
# sample-test : PASS,FLAKY
66

77
[true] # This section applies to all platforms
8-
# https://github.com/nodejs/node/issues/21425
9-
test-statwatcher: PASS,FLAKY
108

119
[$system==win32]
1210

test/async-hooks/test-statwatcher.js

+53-40
Original file line numberDiff line numberDiff line change
@@ -15,68 +15,81 @@ tmpdir.refresh({ spawn: false });
1515

1616
const file1 = path.join(tmpdir.path, 'file1');
1717
const file2 = path.join(tmpdir.path, 'file2');
18-
fs.writeFileSync(file1, 'foo');
19-
fs.writeFileSync(file2, 'bar');
2018

21-
const hooks = initHooks();
22-
hooks.enable();
23-
24-
function onchange1(curr, prev) {
25-
console.log('Watcher: w1');
19+
const onchangex = (x) => (curr, prev) => {
20+
console.log(`Watcher: ${x}`);
2621
console.log('current stat data:', curr);
2722
console.log('previous stat data:', prev);
28-
}
29-
// Install first file watcher
30-
const w1 = fs.watchFile(file1, { interval: 10 }, onchange1);
23+
};
24+
25+
const checkWatcherStart = (name, watcher) => {
26+
assert.strictEqual(watcher.type, 'STATWATCHER');
27+
assert.strictEqual(typeof watcher.uid, 'number');
28+
assert.strictEqual(watcher.triggerAsyncId, 1);
29+
checkInvocations(watcher, { init: 1 },
30+
`${name}: when started to watch file`);
31+
};
32+
33+
const hooks = initHooks();
34+
hooks.enable();
3135

36+
// Install first file watcher.
37+
const w1 = fs.watchFile(file1, { interval: 10 }, onchangex('w1'));
3238
let as = hooks.activitiesOfTypes('STATWATCHER');
3339
assert.strictEqual(as.length, 1);
40+
// Count all change events to account for all of them in checkInvocations.
41+
let w1HookCount = 0;
42+
w1.on('change', () => w1HookCount++);
3443

3544
const statwatcher1 = as[0];
36-
assert.strictEqual(statwatcher1.type, 'STATWATCHER');
37-
assert.strictEqual(typeof statwatcher1.uid, 'number');
38-
assert.strictEqual(statwatcher1.triggerAsyncId, 1);
39-
checkInvocations(statwatcher1, { init: 1 },
40-
'watcher1: when started to watch file');
41-
42-
function onchange2(curr, prev) {
43-
console.log('Watcher: w2');
44-
console.log('current stat data:', curr);
45-
console.log('previous stat data:', prev);
46-
}
45+
checkWatcherStart('watcher1', statwatcher1);
4746

4847
// Install second file watcher
49-
const w2 = fs.watchFile(file2, { interval: 10 }, onchange2);
48+
const w2 = fs.watchFile(file2, { interval: 10 }, onchangex('w2'));
5049
as = hooks.activitiesOfTypes('STATWATCHER');
5150
assert.strictEqual(as.length, 2);
51+
// Count all change events to account for all of them in checkInvocations.
52+
let w2HookCount = 0;
53+
w2.on('change', () => w2HookCount++);
5254

5355
const statwatcher2 = as[1];
54-
assert.strictEqual(statwatcher2.type, 'STATWATCHER');
55-
assert.strictEqual(typeof statwatcher2.uid, 'number');
56-
assert.strictEqual(statwatcher2.triggerAsyncId, 1);
5756
checkInvocations(statwatcher1, { init: 1 },
5857
'watcher1: when started to watch second file');
59-
checkInvocations(statwatcher2, { init: 1 },
60-
'watcher2: when started to watch second file');
58+
checkWatcherStart('watcher2', statwatcher2);
6159

6260
setTimeout(() => fs.writeFileSync(file1, 'foo++'),
6361
common.platformTimeout(100));
64-
w1.once('change', common.mustCall((curr, prev) => {
65-
console.log('w1 change', curr, prev);
62+
w1.on('change', common.mustCallAtLeast((curr, prev) => {
63+
console.log('w1 change to', curr, 'from', prev);
64+
// Wait until we get the write above.
65+
if (prev.size !== 0 || curr.size !== 5)
66+
return;
67+
// Remove listeners to make w1HookCount final
68+
w1.removeAllListeners('change');
69+
6670
setImmediate(() => {
67-
checkInvocations(statwatcher1, { init: 1, before: 1, after: 1 },
71+
checkInvocations(statwatcher1,
72+
{ init: 1, before: w1HookCount, after: w1HookCount },
6873
'watcher1: when unwatched first file');
6974
checkInvocations(statwatcher2, { init: 1 },
7075
'watcher2: when unwatched first file');
7176

7277
setTimeout(() => fs.writeFileSync(file2, 'bar++'),
7378
common.platformTimeout(100));
74-
w2.once('change', common.mustCall((curr, prev) => {
75-
console.log('w2 change', curr, prev);
79+
w2.on('change', common.mustCallAtLeast((curr, prev) => {
80+
console.log('w2 change to', curr, 'from', prev);
81+
// Wait until we get the write above.
82+
if (prev.size !== 0 || curr.size !== 5)
83+
return;
84+
// Remove listeners to make w2HookCount final
85+
w2.removeAllListeners('change');
86+
7687
setImmediate(() => {
77-
checkInvocations(statwatcher1, { init: 1, before: 1, after: 1 },
88+
checkInvocations(statwatcher1,
89+
{ init: 1, before: w1HookCount, after: w1HookCount },
7890
'watcher1: when unwatched second file');
79-
checkInvocations(statwatcher2, { init: 1, before: 1, after: 1 },
91+
checkInvocations(statwatcher2,
92+
{ init: 1, before: w2HookCount, after: w2HookCount },
8093
'watcher2: when unwatched second file');
8194
fs.unwatchFile(file1);
8295
fs.unwatchFile(file2);
@@ -85,13 +98,13 @@ w1.once('change', common.mustCall((curr, prev) => {
8598
});
8699
}));
87100

88-
process.on('exit', onexit);
89-
90-
function onexit() {
101+
process.once('exit', () => {
91102
hooks.disable();
92103
hooks.sanityCheck('STATWATCHER');
93-
checkInvocations(statwatcher1, { init: 1, before: 1, after: 1 },
104+
checkInvocations(statwatcher1,
105+
{ init: 1, before: w1HookCount, after: w1HookCount },
94106
'watcher1: when process exits');
95-
checkInvocations(statwatcher2, { init: 1, before: 1, after: 1 },
107+
checkInvocations(statwatcher2,
108+
{ init: 1, before: w2HookCount, after: w2HookCount },
96109
'watcher2: when process exits');
97-
}
110+
});

0 commit comments

Comments
 (0)