Skip to content

A crash between finalize_all's renames desyncs undo.data from the data files; undo then deletes the wrong interval #778

Description

@yottayoshida

AtomicFile::finalize_all() renames the database files one after another — month data, then undo.data, then tags.data. A process killed between two renames (SIGKILL, OOM killer, hard reboot) leaves files that are each intact but inconsistent with each other. The signal masking from #316 cannot block SIGKILL, and this is a different mechanism from #772: no fsync is involved, and an fsync fix would not close this window.

Confirmed on 1.4.3 (Debian bookworm) and on develop @ db7751c, built from source. I found the two states below with a crash-consistency test harness I wrote (it SIGKILLs the process immediately before each state-directory syscall in turn) and verified them by hand; the reverted file is byte-for-byte what a kill between the renames leaves behind.

Case 1 — the journal misses the last transaction (killed after the data rename, before the undo rename). timew undo then reports success and deletes an interval committed before the crash:

export TIMEWARRIORDB=$(mktemp -d)
timew track 2020-01-01T10:00 - 2020-01-01T11:00 alpha :yes
cp "$TIMEWARRIORDB/data/undo.data" /tmp/undo.before
timew track 2020-01-02T10:00 - 2020-01-02T11:00 beta :yes
cp /tmp/undo.before "$TIMEWARRIORDB/data/undo.data"   # the crash window

timew undo         # prints "Undo", exits 0
timew export       # alpha is gone; beta survives

Case 2 — tags.data misses the last update (killed after the undo rename, before the tags rename). timew undo then aborts and stays unusable until the file is repaired by hand:

export TIMEWARRIORDB=$(mktemp -d)
timew track 2020-01-01T10:00 - 2020-01-01T11:00 alpha :yes
cp "$TIMEWARRIORDB/data/tags.data" /tmp/tags.before
timew track 2020-01-02T10:00 - 2020-01-02T11:00 beta :yes
cp /tmp/tags.before "$TIMEWARRIORDB/data/tags.data"   # the crash window

timew undo         # "Trying to decrement non-existent tag 'beta'", exits 255

The window is only a few syscalls wide, but the inconsistent state is durable, and nothing reads it until the next timew undo — possibly days later. Case 1 is silent loss of committed data; case 2 leaves undo broken.

Could you confirm this analysis? I can provide more detail if useful.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions