-
Notifications
You must be signed in to change notification settings - Fork 142
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
Reader should not choke on underlying reader errors #171
Comments
I agree that your example is indeed a bug and it will be fixed. However, I don't see in the io.Reader doc where io.EOF is allowed to be a transient one, or on how to handle transient errors? |
In my experience, when transient errors are possible, you wrap the Reader to deal with them rather than encumbering every consumer with the problem. How would LZ4 know which errors are transient? Specifically in the case of EOF, when a Reader returns n>0, EOF, it is then required to return EOF again on the next call (and I've always taken that to imply it must keep returning EOF, but I admit that's not in the io.Reader docs). |
@dsnet I think I misunderstood your example: it is not that the Reader discards received data along an EOF, it is that it discards data after one. In which case I agree with @greatroar that this behaviour is not the responsability of the LZ4 reader. |
On second thought, maybe I was wrong and it would be better to pass received errors through and not remember them, so the caller can handle them by retrying. Also, I would actually expect the LZ4 reader to return something other than EOF if it encountered it before the footer. Now, io.Copy pretends that it read a complete file. Perhaps Read should...
Does that sound reasonable? A remaining question is whether EOF in the middle of a stream is a format error to be remembered or an underlying Reader error to be handled by the caller :) |
@greatroar, what you described in your last comment is what I would expect as the behavior.
I expect that to be simply changed into an As a general rule-of-thumb, regardless of what errors are returned by the underlying reader, so long as the caller knows that they are temporary, we should be able keep retry calling That is, the follow should always work: b := make([]byte, ...)
zr := lz4.NewReader(r)
for {
n, err := zr.Read(b)
if n > 0 {
... = b[:n] // handle n bytes of uncompressed output
}
switch {
case err == io.EOF:
return nil
case isTemporaryError(err): // let user code decide whether this is a temporary problem
continue
case err != nil:
return err
}
}
As an implementation note,
Wrapping is fine so long as it's compatible with |
@dsnet I disagree on your assumption. A Reader that has returned any error is not in a usable state, and should IMO not be used any more. I don't think it is unreasonable for this package to assume an un-broken stream of input. The stdlib inflate has stateful errors. The official snappy package also has stateful errors and I am pretty sure you can find more examples. As pointed out, there is nothing preventing you from wrapping the input Reader in retry code, that can do whatever it wants on what is transient errors in your case. |
Using 677f6a5.
A large number of
io.Reader
errors are transient and non-fatal. However,lz4.Reader
currently remembers all underlying reader errors and fails to make progress once one has been encountered.Example program:
This currently prints:
I expect it to print:
The problem is that
lz4.Reader
seeio.EOF
and never tries to read from the underlyingio.Reader
again. However,io.Reader
is allowed to reportio.EOF
at some point, but then stop doing so in the future. It's also allowed to do that for other errors (e.g., a transient network timeout).The text was updated successfully, but these errors were encountered: