-
-
Notifications
You must be signed in to change notification settings - Fork 16
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
Unexpected behavior and some other issues #1
Comments
There's actually a comment about this in the codebase: Line 68 in 0edcf2c
The thing is when native recursion is used (macOS and Windows) setting a depth limit doesn't actually cause directories at greater depth than that to not be watched, Node doesn't expose any API for controlling that. So should the library just discard some events? At the end of the day setting a depth is just useful from a performance perspective I think.
The depth property sets the maximum depth watched, not the minimum depth watched, so basically when recursion is disabled it's useless. Maybe the library should emit an error when
Could you post some code about this, so that I can better understand the scenario?
I'll look into that, it sounds like a bug.
I don't think closing a watcher should throw an error, if it does the library should be handling it, I'll double check that it handles potential errors. |
Hmm, I'm not sure actually. Are you saying that on Windows and Mac, this library always uses native watcher when By the way, doesn't Linux have a built-in file system watcher as well? I thought I've read about it somewhere.
Yeah, that's what I'm saying as well. That's why I don't see the point of having 2 options for controlling the same thing. When you set
As you probably know, async functions can block (freeze) the thread when it computes a computationally intensive task. In this case, watcher blocks the thread until it's done scanning the drive, which means you cannot cancel the task by calling import Watcher from 'watcher'
let watcher = null
function initDirWatch (dirPath) {
console.log('Watcher init:', dirPath)
// Reset watcher
try { watcher.close() } catch (error) {}
// Init watcher
watcher = new Watcher(dirPath, {
ignoreInitial: true,
depth: 6,
recursive: true
})
watcher.on('all', (event, targetPath, targetPathNext) => {
console.log('Watcher:', event, targetPath, targetPathNext)
})
}
// Init watcher for different paths
initDirWatch('C:/')
setTimeout(() => { initDirWatch('D:/') }, 100)
setTimeout(() => { initDirWatch('C:/Windows') }, 200)
setTimeout(() => { initDirWatch('D:/') }, 300)
setTimeout(() => { initDirWatch('C:/') }, 400) The idea is simple, imagine this watcher is used in a file manager app. The user navigates through directories quickly, causing this function to run 5 times for different dirs in like 2 seconds. The first function would freeze the thread for like ~60 seconds until it's finished scanning the drive. This is why I proposed to implement a pause that would un-freeze the event loop for a few ms and check if there was a Expected behavior: each function call cancels the previous task by calling >00:00:000 Watcher init: 'C:/
>00:00:010 Watcher init: 'D:/
>00:00:020 Watcher init: 'C:/Windows
>00:00:030 Watcher init: 'D:/
>00:00:040 Watcher init: 'C:/ Current behavior: the previous task cannot be canceled because the first function call blocked the thread. The thread remains blocked until it finishes the first drive scan which becomes irrelevant by the time it's done, because the path has been changed: >00:00:000 Watcher init: 'C:/
>00:60:000 Watcher init: 'D:/ <=== takes 60 seconds to be called because the first call blocked the thread
>00:60:010 Watcher init: 'C:/Windows
>00:60:020 Watcher init: 'D:/
>00:60:030 Watcher init: 'C:/
It throws an error when you are calling the function for the first time because it is initialized as Thanks for looking into this 👍 |
Yes exactly. We could switch to manual watching logic under all platforms, but using the native implementations should have much better performance.
Apparently not a recursive one nodejs/node#36005 (comment)
It would be cleaner to just have one option, but the current options align more with chokidar which people are already familiar with and keeping them aligned makes it easier for people to switch libraries.
Scanning the directory is done asynchronously precisely in order not to block the thread, I'm not sure what's happening here, I'll have to take a closer look at this.
It sounds like an interesting use case, especially from my point of view because whatever bugs this library has you are probably going to hit them eventually with a use case like that, so they can be fixed. I haven't had the time to work on this yet, but I've been keeping a tab open for it in my browser, I'm relying on this library too so definitely whatever bugs are found I'm committed to fix them. In the meantime if you have the time to look more closely at these issues PRs are very welcome. |
Perhaps it shouldn't use Node modules for that at all, but instead exec a system command via Node's child_process?
Yep it's async, but I think it uses 100% of the thread and since it runs on the same thread as the main process, the main process cannot do anything else during the scan. It's a known issue with async functions - if they are super computationally intensive, they will still block the thread they run on. That's why I proposed pausing the read stream every 1ms or so, or specifying Thanks. |
An update on this:
|
Unless a native recursive watcher gets implemented or used for Linux by Node itself I don't think this library is going to do anything about it, I don't want to deal with other native binaries at all. |
@fabiospampinato thanks for working on this and for the explanations. I'll try it again with the new options. Though, why is the this.state = {
isCanceled: false
}
function tinyReaddirRecursiveInterruptableScan (path) {
if (this.state.isCanceled) {
return
}
...
} |
That just was the previous state of things, I didn't mean to say that it's impossible to scan the drive in chunks and interrupting the whole scan at pretty much any point, just that being able to do that required some changes to be made. I just finished making those changes, as of v1.1.1 now:
Everything reported in this issue should be addressed now, let me know if you can find any other issues and if in fact those issues actually got addressed for you. |
@fabiospampinato great job 👍 |
Feel free to open another issue if you can find other issues. |
Thanks for creating a "chokidar" alternative. I'm considering moving to "watcher" at some point in the future because it handles drive root dirs like
C:/
properly (doesn't emitunlink
events on init), but I encountered some problems with it, as well as some unexpected behavior, while testing it:depth: 3
andrecursive: true
it still detects changes at deeper levels: 4, 5, 6, 7, ... and so on, which doesn't make much sense since depth is supposed to represent the scan depth limit.depth
option but do not specifyrecursive: true
it won't detect any changes in the nested directories at depth > 0, which doesn't make sense either. I think it should automatically setrecursive: true
if you specifydepth
> 0. Am I misunderstanding the purpose of this property? What's the point of settingdepth: 10
if it does not detect any changes at depth > 0 until you setrecursive: true
?highWaterMark
value for the node's read stream that scans the drive, not sure.Uncaught TypeError
shown below during the drive scan (before it emitsready
event). It happens 100% of the time for some paths likeC:/
(system drive) past certain depth (for me it's 4) and only ~25% of the times for other paths. I just reload the app, and after a few seconds manually trigger the watcher function for some path with a button. Sometimes I get no errors and it logsready
, sometimes I get the error once before it logsready
, sometimes it throws the error multiple times:I'm not sure how to catch that error. I tried adding
watcher.on ( 'error', error => {})
and wrapping the whole function with atry / catch
block. Neither catches the error.Code
I init this watcher inside of a web worker in an Electron app.
dirWatcherWorker.js
Environment
OS: x64, Windows 10 20H2 build 19042.662
Exec env: web worker in an Electron app v10.1.3 (Chrome 85 + Node v12.16)
The text was updated successfully, but these errors were encountered: