GothenburgBitFactory / GothenburgBitFactory/timewarrior
A crash between finalize_all's renames desyncs undo.data from the data files; undo then deletes the wrong interval
- Dominant language
- C++
- Stars
- 1.7k
- Forks
- 117
- Avg merge
- 1d 7h
- Merged PRs (30d)
- 5
Description
`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` @ db7751cb, 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:
```sh
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:
```sh
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.
Contributor guide
Research direction
Start by reading AtomicFile::finalize_all() and reproduce the two shell cases described with the crash-consistency harness. Trace the rename sequence for the month data, undo.data, and tags.data files. Done means an interrupted finalize cannot leave those files inconsistent, and subsequent undo remains correct and usable.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp
- Domain
- cli, operating-systems
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100