-
Notifications
You must be signed in to change notification settings - Fork 12
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
Write file atomically, watch create events, handle empty files. #56
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm
@@ -0,0 +1,51 @@ | |||
/* |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
perhaps we should include a mention here of where this file came from?
https://github.com/youtube/vitess/blob/master/go/ioutil2/ioutil.go
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
@@ -338,7 +338,7 @@ func (c *Client) WriteOutputFile(kvb stores.KVBytes) { | |||
os.Exit(1) | |||
} | |||
|
|||
err = ioutil.WriteFile(c.config.Watcher.OutputPath, bts, 0644) | |||
err = ioutil2.WriteFileAtomic(c.config.Watcher.OutputPath, bts, 0644) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we know if non-Go clients depend on the modify-in-place behavior (and don't listen on Create
events)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I checked the node.js and PHP clients--they seem fine.
|
||
// Write file to temp and atomically move when everything else succeeds. | ||
func WriteFileAtomic(filename string, data []byte, perm os.FileMode) error { | ||
dir, name := path.Split(filename) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dont we need to check if dir and name are valid? i.e. not empty?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
n.b. this is code from https://github.com/youtube/vitess/blob/master/go/ioutil2/ioutil.go
I didn't want to import that whole project. That will fail below (and won't happen to us, I think)
if err != nil { | ||
return err | ||
} | ||
_, err = f.Write(data) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't we want to exit immediately on error (and cleanup temp file)? This code will execute every subsequent operation after an error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The error handling is a little weird, but looks correct, and I don't want to rewrite this external file
@@ -74,8 +74,11 @@ func (w *Watcher) Watch() { | |||
w.mu.Lock() | |||
select { | |||
case event := <-w.watcher.Events: | |||
if event.Op&fsnotify.Write == fsnotify.Write { | |||
w.UpdateBytes() | |||
if event.Op&fsnotify.Write == fsnotify.Write || event.Op&fsnotify.Create == fsnotify.Create { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this code supposed to work exclusively with the file-writing code in cli/api/client
or is it meant to be generic? In the latter case, it may still observe partial writes, of course. You can use the inotify
methods in the unix
package directly to listen for the IN_CLOSE_WRITE
and the IN_MOVED_TO
events in order to fix that. You also won't need atomic writes. And in the former case, the code does not need to handle the fsnotify.Write
case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's supposed to be somewhere in between. If the producer is using ioutils.WriteFile, a partial write will signal an error. (https://golang.org/src/io/ioutil/ioutil.go?s=2722:2780#L76)
Doing it this way will preserve compatibility with users that have extended this package in some way (and help roll out our own upgrade...)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The atomic writes are necessary for the PHP client, I think. It seems to be occasionally picking up a truncated file from ioutils.WriteFile. The node client also responds to every event without checking. We could fix all these, or just switch to atomic writes. I think the latter is simpler for consumers.
@@ -107,6 +110,10 @@ func (w *Watcher) UpdateBytes() error { | |||
return err | |||
} | |||
|
|||
if len(bts) == 0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🙏
if event.Op&fsnotify.Write == fsnotify.Write || event.Op&fsnotify.Create == fsnotify.Create { | ||
err := w.UpdateBytes() | ||
if err != nil { | ||
printer.LogErrf("[dcdr] watch error: %v", err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: can we call this an updateBytes
error or something to differentiate from L84?
@@ -338,7 +338,7 @@ func (c *Client) WriteOutputFile(kvb stores.KVBytes) { | |||
os.Exit(1) | |||
} | |||
|
|||
err = ioutil.WriteFile(c.config.Watcher.OutputPath, bts, 0644) | |||
err = ioutil2.WriteFileAtomic(c.config.Watcher.OutputPath, bts, 0644) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we know if non-Go clients depend on the modify-in-place behavior (and don't listen on Create
events)?
(I got vlad's verbal ok for this) |
No description provided.