2

How do I find out what the mtime resolution of a system is from Node.js?

Why I'm asking

In Node.js, fs.watch will sometimes emit duplicate change events. In order to avoid taking redundant actions, it's common to use code like this (from CoffeeScript's coffee utility):

  if event is 'change'
    fs.stat source, (err, stats) ->
      throw err if err
      return if stats.size is prevStats.size and
        stats.mtime.getTime() is prevStats.mtime.getTime()
      prevStats = stats
      ...

Here's the problem: Under OS X, because of the underlying HFS+ filesystem, mtime has a resolution of only 1 second. That is, mtime.getTime() values are of the form

1322068921000

So whenever two changes occur within 1 second of each other, there's a chance that the second change will not affect the file's mtime. Thanks to the stats.size check, this is only a problem if the second change had no effect on the file's size. Still, it's a problem.

A reliable solution would be to "debounce" the change events by the mtime interval; i.e. under OS X, when a change occurs, I would wait 1 second to act on it (thereby grouping all change events that may have the same mtime together). But I'd like to delay the event by the minimum possible time under each file system, rather than adopting the greatest common denominator.

Trevor Burnham
  • 76,828
  • 33
  • 160
  • 196

2 Answers2

1

I had an identical problem when writing some Java code. This problem transcends Node.js, because most languages have a similar API when dealing with files (and are all limited by filesystem specific constraints). Other filesystems in Linux have the same problem (1s timestamp granularity), as well as the old FAT32 for Windows.

One hacky solution is to actually set the file's mtime back one second before you read the file (or send the file events). That way, if another modification occurs within the same granularity window (1s in this case), the new mtime will be different, and you'll be able to detect the modification.

This should work well if no other apps are using the file's mtime, otherwise it might very well interfere with their operation. It's a workaround hack, really, not a proper solution. Still, I found it useful in some scenarios, for example, for the automated tests of my Java code (the tests modify files in quick succession, more than would naturally occur in real world usage).

BrunoMedeiros
  • 1,441
  • 12
  • 14
1

Iterate all files in some directory (like /tmp) and do something like this

files.forEach(function(filename) {
    sum += fs.statSync(filename).mtime % 1000;
});

if (sum == 0) {
   // supports only 1 second resolution
} 

Bit hacky, I know.

Teemu Ikonen
  • 11,861
  • 4
  • 22
  • 35
  • I was really hoping for a solution that wouldn't involve I/O. This is for a library. – Trevor Burnham Nov 23 '11 at 20:44
  • Maybe you could scan the module directory on library initialization, the node will anyway scan, require and load it so few extra I/O calls wouldn't make huge difference. – Teemu Ikonen Nov 24 '11 at 06:37
  • How could I be sure that the module directory would contain a set of files with several different `mtime`s? – Trevor Burnham Nov 24 '11 at 16:40
  • You can not be sure, but it's highly unlikely that all files would have mtime on exact second if filesystem supports millisecond granularity. – Teemu Ikonen Nov 24 '11 at 16:47
  • Right, as long as there are several files that were created at different times. But how can I find a directory like that? – Trevor Burnham Nov 24 '11 at 17:14
  • In UNIX and OS/X the /var or /tmp are bests places. In windows the temp directory (look env variable TEMP). – Teemu Ikonen Nov 24 '11 at 18:28