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

fix a race when StopAtEOF is called #70

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

tgummerer
Copy link

Currently when StopAtEOF is called, and we previously encountered an EOF already, we stop reading the file immediately. However when tailing a file, new data might have become available in the meantime, before the StopAtEOF is called. The watcher might however not have notified us about that yet.

Instead of exiting immediately if that happens, and leaving the data that's already in the file unread, continue iterating until we get the next EOF, as we can be reasonably sure that that's the EOF the user meant to stop at, making sure to read all the data that has been written by the time StopAtEOF is called.

Fixes #67

Currently when StopAtEOF is called, and we previously encountered an
EOF already, we stop reading the file immediately.  However when
tailing a file, new data might have become available in the meantime,
before the StopAtEOF is called.  The watcher might however not have
notified us about that yet.

Instead of exiting immediately if that happens, and leaving the data
that's already in the file unread, continue iterating until we get
the *next* EOF, as we can be reasonably sure that that's the EOF the
user meant to stop at, making sure to read all the data that has been
written by the time StopAtEOF is called.
@tgummerer
Copy link
Author

@nxadm, any interest in this? Can I do something to help get this landed?

tgummerer added a commit to pulumi/pulumi that referenced this pull request Dec 13, 2024
This fixes a couple of race conditions in the tail library, combining
the ideas of nxadm/tail#70 and
nxadm/tail#71.

- Currently when StopAtEOF is called, and we previously encountered an
  EOF already, we stop reading the file immediately. However when
  tailing a file, new data might have become available in the meantime,
  before the StopAtEOF is called. The watcher might however not have
  notified us about that yet.

  Instead of exiting immediately if that happens, and leaving the data
  that's already in the file unread, continue iterating until we get the
  next EOF, as we can be reasonably sure that that's the EOF the user
  meant to stop at, making sure to read all the data that has been
  written by the time StopAtEOF is called.

- When a StopAtEOF() is called the code should continue to send all
  lines to the Lines channel. The issue here is if the caller is not
  ready to receive a new line the code blocks as it is using a
  unbuffered channel. However <-tail.Dying() would return in this case
  so the line was skipped. This means that the caller did not get all
  lines until EOF. Now we still want to skip in case any other reason
  for kill was given therefore add special logic to only not read the
  Dying channel on the EOF case.

  The one downside is that StopAtEOF() could block forever if the
  caller never reads new Lines but this seems logical to me. If the
  caller wants to wait for EOF but never reads remaining Lines this
  would be a bug on their end.

Co-authored-by: Paul Holzinger <[email protected]>
tgummerer added a commit to pulumi/pulumi that referenced this pull request Dec 13, 2024
This fixes a couple of race conditions in the tail library, combining
the ideas of nxadm/tail#70 and
nxadm/tail#71.

- Currently when StopAtEOF is called, and we previously encountered an
  EOF already, we stop reading the file immediately. However when
  tailing a file, new data might have become available in the meantime,
  before the StopAtEOF is called. The watcher might however not have
  notified us about that yet.

  Instead of exiting immediately if that happens, and leaving the data
  that's already in the file unread, continue iterating until we get the
  next EOF, as we can be reasonably sure that that's the EOF the user
  meant to stop at, making sure to read all the data that has been
  written by the time StopAtEOF is called.

- When a StopAtEOF() is called the code should continue to send all
  lines to the Lines channel. The issue here is if the caller is not
  ready to receive a new line the code blocks as it is using a
  unbuffered channel. However <-tail.Dying() would return in this case
  so the line was skipped. This means that the caller did not get all
  lines until EOF. Now we still want to skip in case any other reason
  for kill was given therefore add special logic to only not read the
  Dying channel on the EOF case.

  The one downside is that StopAtEOF() could block forever if the
  caller never reads new Lines but this seems logical to me. If the
  caller wants to wait for EOF but never reads remaining Lines this
  would be a bug on their end.

Co-authored-by: Paul Holzinger <[email protected]>
tgummerer added a commit to pulumi/pulumi that referenced this pull request Dec 16, 2024
This fixes a couple of race conditions in the tail library, combining
the ideas of nxadm/tail#70 and
nxadm/tail#71.

- Currently when StopAtEOF is called, and we previously encountered an
  EOF already, we stop reading the file immediately. However when
  tailing a file, new data might have become available in the meantime,
  before the StopAtEOF is called. The watcher might however not have
  notified us about that yet.

  Instead of exiting immediately if that happens, and leaving the data
  that's already in the file unread, continue iterating until we get the
  next EOF, as we can be reasonably sure that that's the EOF the user
  meant to stop at, making sure to read all the data that has been
  written by the time StopAtEOF is called.

- When a StopAtEOF() is called the code should continue to send all
  lines to the Lines channel. The issue here is if the caller is not
  ready to receive a new line the code blocks as it is using a
  unbuffered channel. However <-tail.Dying() would return in this case
  so the line was skipped. This means that the caller did not get all
  lines until EOF. Now we still want to skip in case any other reason
  for kill was given therefore add special logic to only not read the
  Dying channel on the EOF case.

  The one downside is that StopAtEOF() could block forever if the
  caller never reads new Lines but this seems logical to me. If the
  caller wants to wait for EOF but never reads remaining Lines this
  would be a bug on their end.

Co-authored-by: Paul Holzinger <[email protected]>
github-merge-queue bot pushed a commit to pulumi/pulumi that referenced this pull request Dec 16, 2024
Note: This PR is probably best read commit by commit.

The nxadm/tail library we're using has a couple of race conditions, that
may appear when using the automation API. This PR vendors parts of that
library, and pulls in a couple of fixes from upstream (in particular
nxadm/tail#70 and
nxadm/tail#71) to address these race conditions.
This also means we can get rid of the hacky workaround we have for these
upstream issues.

fixes #15235

---------

Co-authored-by: Paul Holzinger <[email protected]>
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.

StopAtEOF is racy
1 participant