@@ -10,6 +10,7 @@ import (
1010
1111 "github.com/ozontech/file.d/logger"
1212 "github.com/ozontech/file.d/pipeline"
13+ "github.com/ozontech/file.d/xtime"
1314 "go.uber.org/atomic"
1415)
1516
@@ -24,9 +25,10 @@ type offsetDB struct {
2425}
2526
2627type inodeOffsets struct {
27- filename string
28- sourceID pipeline.SourceID
29- streams map [pipeline.StreamName ]int64
28+ filename string
29+ sourceID pipeline.SourceID
30+ streams map [pipeline.StreamName ]int64
31+ lastReadTimestamp int64
3032}
3133
3234type (
@@ -88,6 +90,7 @@ func (o *offsetDB) parseOne(content string, offsets fpOffsets) (string, error) {
8890 filename := ""
8991 inodeStr := ""
9092 sourceIDStr := ""
93+ lastReadTimestampStr := ""
9194 var err error
9295
9396 filename , content , err = o .parseLine (content , "- file: " )
@@ -103,6 +106,11 @@ func (o *offsetDB) parseOne(content string, offsets fpOffsets) (string, error) {
103106 return "" , fmt .Errorf ("can't parse source_id: %w" , err )
104107 }
105108
109+ lastReadTimestampStr , content , err = o .parseOptionalLine (content , " last_read_timestamp: " )
110+ if err != nil {
111+ return "" , fmt .Errorf ("can't parse last_read_timestamp: %w" , err )
112+ }
113+
106114 sysInode , err := strconv .ParseUint (inodeStr , 10 , 64 )
107115 if err != nil {
108116 return "" , fmt .Errorf ("wrong offsets format, can't parse inode: %s: %w" , inodeStr , err )
@@ -120,10 +128,21 @@ func (o *offsetDB) parseOne(content string, offsets fpOffsets) (string, error) {
120128 return "" , fmt .Errorf ("wrong offsets format, duplicate inode %d" , inode )
121129 }
122130
131+ var lastReadTimestampVal int64
132+ if lastReadTimestampStr != "" {
133+ lastReadTimestampVal , err = strconv .ParseInt (lastReadTimestampStr , 10 , 64 )
134+ if err != nil {
135+ return "" , fmt .Errorf ("invalid timestamp format %q: %w" , lastReadTimestampStr , err )
136+ }
137+ } else {
138+ lastReadTimestampVal = xtime .GetInaccurateUnixNano ()
139+ }
140+
123141 offsets [fp ] = & inodeOffsets {
124- streams : make (map [pipeline.StreamName ]int64 ),
125- filename : filename ,
126- sourceID : fp ,
142+ streams : make (map [pipeline.StreamName ]int64 ),
143+ filename : filename ,
144+ sourceID : fp ,
145+ lastReadTimestamp : lastReadTimestampVal ,
127146 }
128147
129148 return o .parseStreams (content , offsets [fp ].streams )
@@ -172,21 +191,43 @@ func (o *offsetDB) parseStreams(content string, streams streamsOffsets) (string,
172191 return content , nil
173192}
174193
175- func (o * offsetDB ) parseLine (content string , start string ) (string , string , error ) {
176- l := len (start )
194+ func (o * offsetDB ) parseLine (content string , prefix string ) (string , string , error ) {
195+ if content == "" {
196+ return "" , "" , fmt .Errorf ("unexpected end of content while looking for %q" , prefix )
197+ }
177198
178199 linePos := strings .IndexByte (content , '\n' )
179200 if linePos < 0 {
180- return "" , "" , fmt .Errorf ("wrong offsets format, no nl: %q" , content )
201+ return "" , "" , fmt .Errorf ("no newline found in content" )
202+ }
203+
204+ line := content [:linePos ]
205+ remaining := content [linePos + 1 :]
206+
207+ if len (line ) < len (prefix ) || line [:len (prefix )] != prefix {
208+ return "" , "" , fmt .Errorf ("expected prefix %q, got %q" , prefix , safeSubstring (line , len (prefix )))
209+ }
210+
211+ return line [len (prefix ):], remaining , nil
212+ }
213+
214+ func (o * offsetDB ) parseOptionalLine (content string , prefix string ) (string , string , error ) {
215+ if content == "" {
216+ return "" , content , nil // No content, return empty value
181217 }
182- line := content [0 :linePos ]
183218
184- content = content [linePos + 1 :]
185- if linePos < l || line [0 :l ] != start {
186- return "" , "" , fmt .Errorf ("wrong offsets file format expected=%q, got=%q" , start , line [0 :l ])
219+ if len (content ) >= len (prefix ) && content [:len (prefix )] == prefix {
220+ return o .parseLine (content , prefix )
187221 }
188222
189- return line [l :], content , nil
223+ return "" , content , nil
224+ }
225+
226+ func safeSubstring (s string , length int ) string {
227+ if len (s ) < length {
228+ return s
229+ }
230+ return s [:length ]
190231}
191232
192233func (o * offsetDB ) save (jobs map [pipeline.SourceID ]* Job , mu * sync.RWMutex ) {
@@ -234,6 +275,10 @@ func (o *offsetDB) save(jobs map[pipeline.SourceID]*Job, mu *sync.RWMutex) {
234275 o .buf = strconv .AppendUint (o .buf , uint64 (job .sourceID ), 10 )
235276 o .buf = append (o .buf , '\n' )
236277
278+ o .buf = append (o .buf , " last_read_timestamp: " ... )
279+ o .buf = strconv .AppendInt (o .buf , job .eofReadInfo .getUnixNanoTimestamp (), 10 )
280+ o .buf = append (o .buf , '\n' )
281+
237282 o .buf = append (o .buf , " streams:\n " ... )
238283 for _ , strOff := range job .offsets {
239284 o .buf = append (o .buf , " " ... )
0 commit comments