sprockets: Intermittent access violations in Windows
A few versions ago I started to receive intermittent EACCESS errors, e.g. Permission denied @ utime_failed - /a/sprockets/cache/file.cache
But whenever I tried to reproduce it, I failed. Until today 😃
EDIT: After further investigation I found that this problem also applies to threaded servers on Windows, and not just external processes interfering with sprockets. See comment below
The problem is triggered by FileStore#get
:
- On every request this method reads the file and also updates its timestamps with
FileUtils.touch(path)
. - An external process, like the IDE or antivirus, picks up the change and opens it read-only for further inspection. This happens asynchronously, that’s why the error is not always triggered. (EDIT: With threaded servers it is the server itself, which openes the file. The error occurs almost every time when two or more parallel requests are received)
- A possible next call of
FileStore#get
to the same cache entry opens that file again (which is allowed even if the file is still opened by another process/thread) … - … and changes the timestamp, which fails on Windows. Apparently you can’t change the timestamp if the file is open, even if just read-only. (But that might be specific to the Ruby implementation)
The access violation can be suppressed by changing the offending line to FileUtils.touch(path) rescue nil
which provides no (statistically relevant) performance penalty. (Benchmark below)
It also should not affect the garbage collection feature, since if the file is locked, then its timestamp has been updated recently.
Benchmark:
Benchmark.measure { 10_000.times { FileUtils.touch('sample.txt') } }
Benchmark.measure { 10_000.times { FileUtils.touch('sample.txt') rescue nil } }
Linux:
0.020000 0.010000 0.030000 ( 0.028624) FileUtils.touch
0.020000 0.010000 0.030000 ( 0.027564) FileUtils.touch rescue nil
Windows:
0.031000 0.437000 0.468000 ( 0.468788) FileUtils.touch
0.031000 0.438000 0.469000 ( 0.478555) FileUtils.touch rescue nil
PS: Note the time difference between the two platforms 😱
About this issue
- Original URL
- State: open
- Created 9 years ago
- Comments: 15 (8 by maintainers)
The problem is still there, because the semantics of
FileUtils.touch
is still different on the Windows platform. But I’m no longer confident, that my “fix” is the proper solution.Today I ran my demo snippet on various Ruby versions again:
On Ruby 2.2 the
cache.get
and its subsequenttouch
method reliably raised EACCES exceptions, where Ruby 2.3 and 2.4 both seem to simply deadlock. On the other hand this happens far less than the AVs in Ruby 2.2: My demo snippet usually created an error after less than 5-6 concurrent reads. (= touch calls) In Ruby 2.3+ I can get about 1.000 concurrent reads until the deadlock. I suspect this is caused by changes to the threading system in Ruby, but honestly I have no idea what’s causing this.In my Rails application I get this situation about once a week which IMHO is totally acceptable in the development environment. In production (which shouldn’t be on Windows 🤞) this isn’t a problem anyway.