v0.9.0: play counts that can't conflict
The merge windows kept coming because finishing a track rewrote all 15 MB of library.json. Both machines did that, so Syncthing saw two edits to one big file between syncs and produced a conflict file roughly once per song — and the merge then took max() of the two counts, discarding whichever side had played less. The .resolved/ backups showed the last nine library.json merges were ~98% play counts plus exactly one real edit, with the same ~45 tracks disagreeing every time and the count only creeping down over a day. library.json now holds only a base count. Each machine owns plays/<machine-id>.json with its own per-track totals, and the effective count is base + the sum of every journal. Only the owner writes its journal, so play data can't conflict; the journal stores totals rather than an append log, so there's no compaction step to double-count in; and a machine still on older code keeps bumping its own base, which stays additive with our journal. Two places carry the whole hazard, and both are pinned by tests: save_tracks writes journal.base_fields(), never the Track's effective count, and PlayJournal.load must be given a base-valued library — which is why reload_from_disk folds the journals onto the disk copy before reconciling. On the real 21k library: three plays write 83 bytes and leave library.json untouched. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M8Mzze7shr5pZoEgKQU5NW
This commit is contained in:
@@ -14,10 +14,13 @@ When a round closes, move its finished items to `tasks-done.md`.
|
||||
Folder + web-mix export, audio.js/jQuery dropped for a dependency-free
|
||||
player, lossless-only conversion. Round 36 below is still open.
|
||||
|
||||
## Round 36 — the merge rework (planned, Round 35 covered the speed half)
|
||||
## Round 38 (2026-08-20) — merge windows + play journals: done, see tasks-done.md
|
||||
|
||||
## Round 36 — the merge rework (the playlist half is still open)
|
||||
|
||||
The three symptoms in Round 35 shared a root cause; that round fixed the
|
||||
performance half. This is the correctness half.
|
||||
performance half. Round 38 took the play-count and dialog items; the two
|
||||
playlist items below are what's left.
|
||||
|
||||
- [ ] **Playlist merges lose position.** `_merge_playlist` is a 2-way union with
|
||||
no common ancestor: it takes one side's order wholesale and *appends* the
|
||||
@@ -31,13 +34,10 @@ performance half. This is the correctness half.
|
||||
So "most recently edited" often means "most recently resized". Give
|
||||
`Playlist` a `date_modified` bumped only in `_set_track_ids`, and merge on
|
||||
that.
|
||||
- [ ] **`max()` play counts discard concurrent plays.** Base 100, one machine
|
||||
plays 5 (105), the other plays 3 (103) → merge keeps 105 and those 3 are
|
||||
gone. Per-machine `plays/<machine-id>.json` journal (machine id kept
|
||||
outside the synced dir): only its owner ever writes it, so play data can
|
||||
never conflict, effective count = base + sum of journals, and `library.json`
|
||||
stops being rewritten every 3 s during playback — which is what generates
|
||||
the conflicts in the first place.
|
||||
- [x] **`max()` play counts discard concurrent plays.** Done as described —
|
||||
per-machine `plays/<machine-id>.json` journals, machine id in the config
|
||||
dir. Three plays on the real library now write 83 bytes instead of 15 MB,
|
||||
and `library.json` isn't touched at all.
|
||||
- [x] **Quiet the merge dialog.** One dialog per session: each merge is folded
|
||||
in as a timestamped entry (newest first) instead of opening another
|
||||
window. Fifteen had stacked up. Kept the window for every merge rather
|
||||
|
||||
Reference in New Issue
Block a user