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

In the fs.watch callback, event parameter is always "rename" on OS X #7420

Closed
Drenmi opened this issue Jun 25, 2016 · 15 comments
Closed

In the fs.watch callback, event parameter is always "rename" on OS X #7420

Drenmi opened this issue Jun 25, 2016 · 15 comments
Labels
fs Issues and PRs related to the fs subsystem / file system. macos Issues and PRs related to the macOS platform / OSX. wontfix Issues that will not be fixed.

Comments

@Drenmi
Copy link

Drenmi commented Jun 25, 2016

  • Version: v6.2.2
  • Platform: Darwin 15.5.0 Darwin Kernel Version 15.5.0: Tue Apr 19 18:36:36 PDT 2016; root:xnu-3248.50.21~8/RELEASE_X86_64 x86_64
  • Subsystem: File System

No matter what manipulation of the file is made, the callback parameter event is set to "rename":

fs.watch('./tmp', function(event, filename) {
  console.log(event);
});

// Create file
> rename

// Rename file
> rename

// Delete file
> rename
@Trott
Copy link
Member

Trott commented Jun 25, 2016

Appending to a file will log 'change instead of rename.

$ node -v
v6.2.2
$ uname -a
Darwin Richards-MacBook-Pro.local 14.5.0 Darwin Kernel Version 14.5.0: Thu Apr 21 20:40:54 PDT 2016; root:xnu-2782.50.3~1/RELEASE_X86_64 x86_64
$ node
> fs.watch('.', (name) =>{ console.log(name); })

While that runs, in another terminal window and in the same directory:

$ touch foo
$ echo 'hi' >> foo
$

The touch will log a rename in the first terminal window, and the echo...>>... will log a change.

Per the docs as of this writing, change and rename are the only events that get logged.

@Trott Trott added the fs Issues and PRs related to the fs subsystem / file system. label Jun 25, 2016
@mscdex mscdex added the macos Issues and PRs related to the macOS platform / OSX. label Jun 25, 2016
@lance
Copy link
Member

lance commented Jun 28, 2016

@Trott I see why this is happening here. But I wonder if the logic of preferring rename over change is completely sound. When a file is added or removed within a watched directory, the more intuitive event to my mind is change instead of rename.

@Trott
Copy link
Member

Trott commented Jun 28, 2016

@lance I suspect that if we prefer change to rename, that means there will never be a rename event because every rename is a change.

In theory, I'd be OK with that. In practice, it would be a semver-major change and I'm not sure we're prepared to deal with the ecosystem breakage it might entail.

The original comment was written by @bnoordhuis so let's see if he has any further insight. Like, maybe there's been a change to libuv that allows us to handle the events more precisely somehow or something like that.

@bnoordhuis
Copy link
Member

It's possible now to check if a handle is closed or closing so the 'drop one event' logic could be revisited. Whether that's a good change, I'll let you decide but adding a HandleWrap::IsClosing() method that checks wrap->state_ >= kClosing would do it (and making the second callback, of course.)

@lance
Copy link
Member

lance commented Jun 28, 2016

@Trott I see your point. And I can understand how 'create' file operations are a rename from nothing to something, and likewise, 'delete' file operations are a rename from something to nothing. I suppose by that logic that UV_RENAME and UV_CHANGE are both always emitted for each of 'create', 'rename' and 'delete' file operations. So @bnoordhuis' comment makes sense, I suppose. I can't tell from the libuv docs which actions cause multiple events to fire, but I assume it must be for all three.

Given that, I don't see what benefit would be gained by checking if the handle is closed and conditionally emitting both events. Since even by doing all of that, there's no clear way to discern one type of file operation from the other. This is all speculation, of course, since I don't really know libuv.

@jorangreef
Copy link
Contributor

I have spent some time using fs.watch() across platforms and I think it's better to avoid putting too much weight on the event parameter. Rather treat the fs.watch() api as an efficient means to know when a filename is changed, without relying on it to tell you how it was changed (because the underlying mechanisms cross-platform were never really geared for that). You can then track all changed paths over a period of say 200 milliseconds, and then do a mini-scan of just those paths to figure out for yourself what happened exactly (create, update, delete, rename, restore).

For example, assuming you are a watching a directory, then within a 200 millisecond window, you might receive 10 events for a, 2 events for c and 1 event for b. At the end of the window, you could then wait for an idle time of 100-200 milliseconds (to make sure you don't straddle dependent events) and you could then callfs.stat() on a, b, and c.

You might find that a no longer exists and was deleted, b has a different file size and was updated, and that c is a new file and was created. You could then compare the stats of c with what you remember of a and use heuristics on the stat properties to give a score to how similar c is to a. If it passes a threshold, you could merge the a delete event with the c create event into a a > c rename event.

Perhaps that won't fit your use-case but I hope it helps.

@lance lance added the wontfix Issues that will not be fixed. label Jul 5, 2016
@lance
Copy link
Member

lance commented Jul 5, 2016

I've labeled this as wontfix since it seems this is expected behavior. Any additions to core in order to make fs.watch() provide more/better information are not worth the effort involved since, as @jorangreef points out, there are ways to accomplish this in user-land.

Closing for now. @Trott @bnoordhuis or others, please feel free to reopen if you think it makes sense.

@lance lance closed this as completed Jul 5, 2016
@chyingp
Copy link

chyingp commented Jul 16, 2016

Platform: OSX 10.11.4
Node: v6.1.0

@Drenmi I got the same question.I've tried the following actions, and the event name is always rename.

  • creating a file.
  • modifying a file.
  • deleting a file.

@Trott
Copy link
Member

Trott commented Jul 16, 2016

@chyingp Appending to a file will fire an event of type change rather than rename.

@Drenmi
Copy link
Author

Drenmi commented Jul 16, 2016

@chyingp: According to @jorangreef's reply, this is likely a higher level abstraction that should go in a separate library. (I haven't yet checked if one exists.)

@chyingp
Copy link

chyingp commented Jul 16, 2016

@Trott I tried the following two ways of modifying a file, and got different feedback. It was quite confusing. :(

  1. Open the file in an editor like sublime, and modified the content. -> got "rename"
  2. In the terminal, ran echo "hello" >> fileForWrite.txt. --> got "change"

@chyingp
Copy link

chyingp commented Jul 16, 2016

@Drenmi you may try this one chokidar .

@aralroca
Copy link

aralroca commented Oct 11, 2023

Also using fs.cpSync(watchDirFile, outDirFile, { recursive: true }); is triggering rename event... Is it possible to use cpSync without generating any event or renaming the triggered event?

@aralroca
Copy link

aralroca commented Oct 11, 2023

I am interested in knowing more about the title of the issue, does this mean that in other operating systems the events are different? I thought it was unified for all operating systems 🤔

@bnoordhuis
Copy link
Member

No, that's fs.watchFile(). fs.watch() expressly allows divergent cross-platform behavior in exchange for better performance.

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. macos Issues and PRs related to the macOS platform / OSX. wontfix Issues that will not be fixed.
Projects
None yet
Development

No branches or pull requests

8 participants