Skip to content
This repository was archived by the owner on Oct 13, 2023. It is now read-only.

Commit e34fcd7

Browse files
committed
jsonfile: more defensive reader implementation
Tonis mentioned that we can run into issues if there is more error handling added here. This adds a custom reader implementation which is like io.MultiReader except it does not cache EOF's. What got us into trouble in the first place is `io.MultiReader` will always return EOF once it has received an EOF, however the error handling that we are going for is to recover from an EOF because the underlying file is a file which can have more data added to it after EOF. Signed-off-by: Brian Goff <[email protected]> Upstream-commit: 5a664dc87d79ddc9f09c7a963c52f491999dc939 Component: engine
1 parent d8a9fa4 commit e34fcd7

File tree

1 file changed

+42
-1
lines changed
  • components/engine/daemon/logger/jsonfilelog

1 file changed

+42
-1
lines changed

components/engine/daemon/logger/jsonfilelog/read.go

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,13 +110,54 @@ func (d *decoder) Decode() (msg *logger.Message, err error) {
110110
// If the json logger writes a partial json log entry to the disk
111111
// while at the same time the decoder tries to decode it, the race condition happens.
112112
if err == io.ErrUnexpectedEOF {
113-
d.dec = json.NewDecoder(io.MultiReader(d.dec.Buffered(), d.rdr))
113+
d.rdr = combineReaders(d.dec.Buffered(), d.rdr)
114+
d.dec = json.NewDecoder(d.rdr)
114115
continue
115116
}
116117
}
117118
return msg, err
118119
}
119120

121+
func combineReaders(pre, rdr io.Reader) io.Reader {
122+
return &combinedReader{pre: pre, rdr: rdr}
123+
}
124+
125+
// combinedReader is a reader which is like `io.MultiReader` where except it does not cache a full EOF.
126+
// Once `io.MultiReader` returns EOF, it is always EOF.
127+
//
128+
// For this usecase we have an underlying reader which is a file which may reach EOF but have more data written to it later.
129+
// As such, io.MultiReader does not work for us.
130+
type combinedReader struct {
131+
pre io.Reader
132+
rdr io.Reader
133+
}
134+
135+
func (r *combinedReader) Read(p []byte) (int, error) {
136+
var read int
137+
if r.pre != nil {
138+
n, err := r.pre.Read(p)
139+
if err != nil {
140+
if err != io.EOF {
141+
return n, err
142+
}
143+
r.pre = nil
144+
}
145+
read = n
146+
}
147+
148+
if read < len(p) {
149+
n, err := r.rdr.Read(p[read:])
150+
if n > 0 {
151+
read += n
152+
}
153+
if err != nil {
154+
return read, err
155+
}
156+
}
157+
158+
return read, nil
159+
}
160+
120161
// decodeFunc is used to create a decoder for the log file reader
121162
func decodeFunc(rdr io.Reader) loggerutils.Decoder {
122163
return &decoder{

0 commit comments

Comments
 (0)