-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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_tail: prevent wrongly unwatching with follow_inodes #4221
Conversation
We must not unwatch targets that still exist. If unwatching an existing target, it causes log duplication. The mechanism is as follows. The TailWatcher for rotated old file can be newly created because the path is changed (the inode is the same). (file.log -> file.log.1) If unwatching the inode before creating that new TailWatcher, it can't refer to the previous position and it reads logs in duplicate. Signed-off-by: Daijiro Fukuda <[email protected]>
# (It is because the path of `@tails` will be overwritten here.) | ||
# (It is not a big problem if we don't unwatch here, as long as we make | ||
# sure to detach it. The position will be cleared on restart eventually.) | ||
tail_watcher.unwatched = true unless expand_paths.key?(tail_watcher.ino) |
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'm worry calling expand_paths
here, because it's relatively high cost.
It might be too high cost on heavy rotated environment.
In addition, the file might be disappear soon just after calling expand_paths, as described in
fluentd/lib/fluent/plugin/in_tail.rb
Lines 337 to 348 in 691b2e0
# Even we just checked for existence, there is a race condition here as | |
# of which stat() might fail with ENOENT. See #3224. | |
begin | |
target_info = TargetInfo.new(path, Fluent::FileWrapper.stat(path).ino) | |
if @follow_inodes | |
hash[target_info.ino] = target_info | |
else | |
hash[target_info.path] = target_info | |
end | |
rescue Errno::ENOENT, Errno::EACCES => e | |
$log.warn "expand_paths: stat() for #{path} failed with #{e.class.name}. Skip file." | |
end |
(Although it might not so big problem as you described in the comment).
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.
Thanks for your review!
I'm worry calling expand_paths here, because it's relatively high cost.
It might be too high cost on heavy rotated environment.
I see.
I thought it would be okay since it was only called on rotation, but certainly, this is worrisome considering the case of so many files being rotated so often...
It may be better to cache the list in refresh_watcher
and use it here...
However, I'm unsure if I should go this far for unwatching here.
(Although it might not so big problem as you described in the comment).
What we should be concerned about is that, without unwatching here, the memory and file for PositionFile would be larger.
(It will be cleared only when restarting. We can't use pos_file_compaction_interval
to clear this because it doesn't check the target existence.)
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.
Possible fix:
- A: Don't unwatch here.
- B: Fix
refresh_watcher
to refresh PositionFile as well. - C: Cache the list in
refresh_watcher
and use it inupdate_watcher
- D: Change the key of
@tails
to inode when enablingfollow_inodes
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.
* A: Don't unwatch here.
We want to avoid this if possible since the memory and file for PositionFile would be larger.
* B: Fix `refresh_watcher` to refresh PositionFile as well.
It could be possible.
* C: Cache the list in `refresh_watcher` and use it in `update_watcher`
This is not good. It depends on the interval of refresh_watcher
.
* D: Change the key of `@tails` to inode when enabling `follow_inodes`
It could be possible.
We should consider the following PR for this.
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 close this PR since it should be fixed by B or D.
Are you OK for closing it?
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.
Yes! I close this! Thanks!
We should do this in another way. |
Which issue(s) this PR fixes:
What this PR does / why we need it:
We must not unwatch targets that still exist.
If unwatching an existing target, it causes log duplication.
This problem exists from v1.13.3 (the following fix).
This can occur when enabling
follow_inodes
.The mechanism is as follows.
The TailWatcher for rotated old file can be newly created because the path is changed (the inode is the same). (file.log -> file.log.1)
If unwatching the inode before creating that new TailWatcher, it can't refer to the previous position and it reads logs in duplicate.
Docs Changes:
No need.
Release Note:
Same as the title.
How to Reproduce
Config:
enable_stat_watcher
enable_stat_watcher false
is for making sure to detect rotation.nil
to it'srotate_handler
. This causes some problems. (I will create an issue or PR about this later.)in_tail
can't detect the rotation of the current file because of this, then this problem does not occur. The focus is on whether the rotation of the current file can be detected whenenable_stat_watcher
is enabled. If not, then this can occur only when disablingenable_stat_watcher
.Files:
Script to rotate:
Run:
Result:
refresh_watcher
.update_watcher
wrongly unwatches the existing inode.