}
// Scan gives each line of the day files from place on to add, with its
-// position, oldest day first. The zero position starts at the first line.
+// position, oldest day first. The zero position starts at the first line. A
+// crash during a write can leave a part of a line at the end of the last day
+// file. Its write never returned, so Scan cuts it off.
func (j *journal) Scan(ctx context.Context, place position, add func([]byte, position) error) error {
paths, err := filepath.Glob(filepath.Join(j.dir, "*.jsonl"))
if err != nil {
}
slices.Sort(paths)
- for _, path := range paths {
+ for n, path := range paths {
day, err := dayNumber(strings.TrimSuffix(filepath.Base(path), ".jsonl"))
if err != nil {
return err
offset = place.offset
}
- err = scanFile(ctx, path, day, offset, add)
+ err = scanFile(ctx, path, day, offset, n == len(paths)-1, add)
if err != nil {
return err
}
return nil
}
-// scanFile gives each line of the day file at path from offset on to add.
-func scanFile(ctx context.Context, path string, day uint32, offset uint64, add func([]byte, position) error) (err error) {
+// scanFile gives each line of the day file at path from offset on to add. In
+// the last day file, it cuts a part of a line at the end.
+func scanFile(ctx context.Context, path string, day uint32, offset uint64, last bool, add func([]byte, position) error) (err error) {
file, err := os.Open(path)
if err != nil {
return fmt.Errorf("open day file: %w", err)
return nil
}
+ if errors.Is(err, io.EOF) && last {
+ return cutFile(path, offset)
+ }
+
if errors.Is(err, io.EOF) {
return fmt.Errorf("%s ends in an incomplete line at %d", filepath.Base(path), offset)
}
}
}
+// cutFile cuts the file at path to size bytes.
+func cutFile(path string, size uint64) error {
+ err := os.Truncate(path, int64(size))
+ if err != nil {
+ return fmt.Errorf("cut incomplete line: %w", err)
+ }
+
+ return nil
+}
+
// dayNumber returns the day YYYY-MM-DD as the number YYYYMMDD.
func dayNumber(day string) (uint32, error) {
number, err := strconv.ParseUint(strings.ReplaceAll(day, "-", ""), 10, 32)