X-Git-Url: https://git.giacomolli.uk/onechat/blobdiff_plain/feda522eba3e2a27c91741c77d0d01f1840c0004..97729fc59a2fbd73e3f8a424cae4c825f5ba885b:/journal.go?ds=inline diff --git a/journal.go b/journal.go index ef49c6e..cda9375 100644 --- a/journal.go +++ b/journal.go @@ -133,7 +133,9 @@ func (j *journal) Close() error { } // 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 { @@ -141,7 +143,7 @@ func (j *journal) Scan(ctx context.Context, place position, add func([]byte, pos } 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 @@ -156,7 +158,7 @@ func (j *journal) Scan(ctx context.Context, place position, add func([]byte, pos 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 } @@ -165,8 +167,9 @@ func (j *journal) Scan(ctx context.Context, place position, add func([]byte, pos 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) @@ -192,6 +195,10 @@ func scanFile(ctx context.Context, path string, day uint32, offset uint64, add f 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) } @@ -215,6 +222,16 @@ func scanFile(ctx context.Context, path string, day uint32, offset uint64, add f } } +// 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)