Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: refactor test-fs-watch #4776

Closed
wants to merge 1 commit into from
Closed

Conversation

Trott
Copy link
Member

@Trott Trott commented Jan 20, 2016

  • Exchange 20 millisecond timers for setImmediate().
  • Do not attempt to unlink path that will have been guaranteed to be
    removed by common.refreshTmpDir()
  • Do not swallow errors thrown by failed creation of needed test
    subdirectory. If that happens, we want to know about it.
  • Use common.isSunOS in one place where it is applicable

* Exchange 20 millisecond timers for setImmediate().

* Do not attempt to unlink path that will have been guaranteed to be
removed by `common.refreshTmpDir()`

* Do not swallow errors thrown by failed creation of needed test
subdirectory. If that happens, we want to know about it.

* Use `common.isSunOS` in one place where it is applicable
@Trott Trott added fs Issues and PRs related to the fs subsystem / file system. test Issues and PRs related to the tests. labels Jan 20, 2016
@Trott
Copy link
Member Author

Trott commented Jan 20, 2016

CI: https://ci.nodejs.org/job/node-test-pull-request/1312/

I started looking at the test because it timed out on FreeBSD in a CI run with an unrelated change. If nothing else, this change does eliminate some error-swallowing which might mean we get more info if it fails again.

@evanlucas
Copy link
Contributor

This is a test I see fail pretty regularly locally. Pulled down the changes and haven't seen a fail yet.

Awesome job @Trott!!!

LGTM if CI is happy

@santigimeno
Copy link
Member

This is great! Without this patch the test is failing in my FreeBSD VM quite easily, with the patch I can't make it fail.

One question: does this mean a timer (setTimeout) can fire before setImmediate? I thought they were fired afterwards.

Probably the same fix could be applied for #4629 and other fs-watch tests?

@cjihrig
Copy link
Contributor

cjihrig commented Jan 20, 2016

LGTM. Maybe run the CI again though.

@Trott
Copy link
Member Author

Trott commented Jan 20, 2016

@santigimeno A setTimeout() can fire before a setImmediate(). It does so for me reliably with this code:

const immediate = () => { console.log('immediate'); };
const timeout = () => { console.log('timeout'); };

setTimeout(timeout, 1);
setImmediate(immediate);

@cjihrig While all the CI failures are build failures unrelated to the test, I sure do like green, so let's try again:

https://ci.nodejs.org/job/node-test-pull-request/1319/

@Trott
Copy link
Member Author

Trott commented Jan 20, 2016

CI is green. \o/

@santigimeno
Copy link
Member

@Trott I see, thanks for the info.
I'm still trying to understand why using setImmediate fixes the issue. Does it have anything to do with when in the event loop the setImmediate callbacks are fired that makes sure the fs event is registered? or is there still a race condition only that it's harder to make test fail? or something else? Thanks in advance

@Trott
Copy link
Member Author

Trott commented Jan 20, 2016

@santigimeno:

I'm still trying to understand why using setImmediate fixes the issue.

Can you confirm that it's the setImmediate() calls that fix the issue by running the current test with just the setTimeout() calls changed to setImmediate()? Maybe it's the removal of the unlinkSync() instead or something else. Maybe let's confirm what aspect of the refactor is really fixing things...

@santigimeno
Copy link
Member

@Trott I can confirm that in a FreeBSD VM:

  • Only removing the unlinkSync(): the test failed after ~ 300 executions.
  • Only replacing SetTimeout with SetImmediate: the test didn't fail after ~ 10000 executions.

@Trott
Copy link
Member Author

Trott commented Jan 21, 2016

@santigimeno And the failure is that the test hangs/times out? Or is there an assertion error or something?

@Trott
Copy link
Member Author

Trott commented Jan 21, 2016

@santigimeno If it helps clarify things, setTimeout() timers that will fire on the next tick can (will?) execute before those created with setImmediate(). So if there's something async happening in fs.watch(), it's possible that setImmediate() gives fs.watch() a chance to do its thing first. With multiple 20ms timeouts being set, one or more are pretty much guaranteed to fire on the next tick. If that's the bug this change is fixing, then the issue I'd expect to see is the test hanging or timing out because the change occurs before the watcher is fully watching.

The problem with this theory is that I haven't located the asynchronous code in fs.watch() that would be involved in the race condition. But I also didn't look that hard (and I certainly didn't think about if it's possible that it's in libuv or the OS...)

@Trott
Copy link
Member Author

Trott commented Jan 21, 2016

@santigimeno Oh, wait, yeah, you identify the delay/asynchronous issue in #4629. Seems at least plausible to me.

Trott added a commit to Trott/io.js that referenced this pull request Jan 21, 2016
* Exchange 20 millisecond timers for setImmediate().

* Do not attempt to unlink path that will have been guaranteed to be
removed by `common.refreshTmpDir()`

* Do not swallow errors thrown by failed creation of needed test
subdirectory. If that happens, we want to know about it.

* Use `common.isSunOS` in one place where it is applicable

PR-URL: nodejs#4776
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Evan Lucas <evanlucas@me.com>
@Trott
Copy link
Member Author

Trott commented Jan 21, 2016

Landed in d26b014

@Trott Trott closed this Jan 21, 2016
@santigimeno
Copy link
Member

@Trott The failure was indeed a timeout.
Regarding the race condition, I think the problem may be similar to this issue in libuv: libuv/libuv#581.

@santigimeno
Copy link
Member

I've got this error on OS X a couple of times running the test suite:

=== release test-fs-watch ===                                             
Path: sequential/test-fs-watch
assert.js:89
  throw new assert.AssertionError({
  ^
AssertionError: 'newfile.txt' == 'watch.txt'
    at FSWatcher.<anonymous> (/Users/sgimeno/node/node/test/sequential/test-fs-watch.js:89:18)
    at emitTwo (events.js:101:13)
    at FSWatcher.emit (events.js:186:7)
    at FSEvent.FSWatcher._handle.onchange (fs.js:1314:12)
Command: out/Release/node /Users/sgimeno/node/node/test/sequential/test-fs-watch.js

Something interesting is that when this test failed, in the same run also failed test-fe-watch-recursive as described in #4629

@Trott
Copy link
Member Author

Trott commented Jan 22, 2016

@santigimeno That's odd. Perhaps it might be a good idea to isolate the three tests in test-fs-watch by moving them to separate files.

@jasnell
Copy link
Member

jasnell commented Jan 23, 2016

@Trott ... see any pressing need to backport this to LTS?

@Trott
Copy link
Member Author

Trott commented Jan 23, 2016

@jasnell Pressing need? No. But it if the commit merges cleanly, it's probably better to have it in LTS than not.

@jasnell
Copy link
Member

jasnell commented Jan 23, 2016

+1... watch label added

rvagg pushed a commit that referenced this pull request Jan 25, 2016
* Exchange 20 millisecond timers for setImmediate().

* Do not attempt to unlink path that will have been guaranteed to be
removed by `common.refreshTmpDir()`

* Do not swallow errors thrown by failed creation of needed test
subdirectory. If that happens, we want to know about it.

* Use `common.isSunOS` in one place where it is applicable

PR-URL: #4776
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Evan Lucas <evanlucas@me.com>
MylesBorins pushed a commit that referenced this pull request Mar 2, 2016
* Exchange 20 millisecond timers for setImmediate().

* Do not attempt to unlink path that will have been guaranteed to be
removed by `common.refreshTmpDir()`

* Do not swallow errors thrown by failed creation of needed test
subdirectory. If that happens, we want to know about it.

* Use `common.isSunOS` in one place where it is applicable

PR-URL: #4776
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Evan Lucas <evanlucas@me.com>
scovetta pushed a commit to scovetta/node that referenced this pull request Apr 2, 2016
* Exchange 20 millisecond timers for setImmediate().

* Do not attempt to unlink path that will have been guaranteed to be
removed by `common.refreshTmpDir()`

* Do not swallow errors thrown by failed creation of needed test
subdirectory. If that happens, we want to know about it.

* Use `common.isSunOS` in one place where it is applicable

PR-URL: nodejs#4776
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Evan Lucas <evanlucas@me.com>
@Trott Trott deleted the refactor-fs-watch branch January 13, 2022 22:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
fs Issues and PRs related to the fs subsystem / file system. test Issues and PRs related to the tests.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants