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

Write file atomically, watch create events, handle empty files. #56

Merged
merged 10 commits into from
Sep 8, 2017

Conversation

sayrer
Copy link
Contributor

@sayrer sayrer commented Sep 7, 2017

No description provided.

Copy link

@tdarci tdarci left a 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 @@
/*
Copy link

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

Copy link
Contributor Author

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)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

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)?

Copy link
Contributor Author

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)
Copy link
Member

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?

Copy link
Contributor Author

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)
Copy link
Member

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.

Copy link
Contributor Author

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 {
Copy link
Contributor

@vladlosev vladlosev Sep 7, 2017

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 unixpackage 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.

Copy link
Contributor Author

@sayrer sayrer Sep 7, 2017

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...)

Copy link
Contributor Author

@sayrer sayrer Sep 7, 2017

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 {

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)

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)

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)?

@sayrer sayrer merged commit 5492539 into master Sep 8, 2017
@sayrer
Copy link
Contributor Author

sayrer commented Sep 8, 2017

(I got vlad's verbal ok for this)

@sayrer sayrer deleted the atomic_file_write branch September 8, 2017 01:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants