فهرست منبع

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 <cpuguy83@gmail.com>
Brian Goff 4 سال پیش
والد
کامیت
5a664dc87d
1فایلهای تغییر یافته به همراه42 افزوده شده و 1 حذف شده
  1. 42 1
      daemon/logger/jsonfilelog/read.go

+ 42 - 1
daemon/logger/jsonfilelog/read.go

@@ -110,13 +110,54 @@ func (d *decoder) Decode() (msg *logger.Message, err error) {
 		// If the json logger writes a partial json log entry to the disk
 		// If the json logger writes a partial json log entry to the disk
 		// while at the same time the decoder tries to decode it, the race condition happens.
 		// while at the same time the decoder tries to decode it, the race condition happens.
 		if err == io.ErrUnexpectedEOF {
 		if err == io.ErrUnexpectedEOF {
-			d.dec = json.NewDecoder(io.MultiReader(d.dec.Buffered(), d.rdr))
+			d.rdr = combineReaders(d.dec.Buffered(), d.rdr)
+			d.dec = json.NewDecoder(d.rdr)
 			continue
 			continue
 		}
 		}
 	}
 	}
 	return msg, err
 	return msg, err
 }
 }
 
 
+func combineReaders(pre, rdr io.Reader) io.Reader {
+	return &combinedReader{pre: pre, rdr: rdr}
+}
+
+// combinedReader is a reader which is like `io.MultiReader` where except it does not cache a full EOF.
+// Once `io.MultiReader` returns EOF, it is always EOF.
+//
+// For this usecase we have an underlying reader which is a file which may reach EOF but have more data written to it later.
+// As such, io.MultiReader does not work for us.
+type combinedReader struct {
+	pre io.Reader
+	rdr io.Reader
+}
+
+func (r *combinedReader) Read(p []byte) (int, error) {
+	var read int
+	if r.pre != nil {
+		n, err := r.pre.Read(p)
+		if err != nil {
+			if err != io.EOF {
+				return n, err
+			}
+			r.pre = nil
+		}
+		read = n
+	}
+
+	if read < len(p) {
+		n, err := r.rdr.Read(p[read:])
+		if n > 0 {
+			read += n
+		}
+		if err != nil {
+			return read, err
+		}
+	}
+
+	return read, nil
+}
+
 // decodeFunc is used to create a decoder for the log file reader
 // decodeFunc is used to create a decoder for the log file reader
 func decodeFunc(rdr io.Reader) loggerutils.Decoder {
 func decodeFunc(rdr io.Reader) loggerutils.Decoder {
 	return &decoder{
 	return &decoder{