Skip to content

1.3.1: the library loses no item (atomic index.json, restore from item.json) - #20

Merged
LeeO86 merged 1 commit into
mainfrom
fix/library-index-1.3.1
Oct 30, 2026
Merged

LeeO86 merged 1 commit into
mainfrom
fix/library-index-1.3.1

Conversation

@LeeO86

@LeeO86 LeeO86 commented Oct 30, 2026

Copy link
Copy Markdown
Owner

For 1.3.1. Not merged, tagged or released.

Problem

After an upgrade to 1.3.0, a library listed 3 of its 7 items at the first start. Nobody deleted them, and their folders and item.json files were still on disk. Nothing was logged.

Cause

Library::save_index rewrote index.json in place with std::ofstream and ignored a failed write (src/media.cpp, 1.3.0 line 171). Library::load_index loads only what index.json lists.

  • If index.json cannot be opened for writing (for example a file the player's user may not write), every later write fails without a word. The running player still lists every item in memory, but the file keeps an older list. The next start loads only that list. The first change after the start (a conversion, an upload) then writes the shorter list, so the loss is permanent.
  • A write that stops halfway (the process killed after the truncate) leaves a cut-off file. The next start loads no item.
  • An entry with a field of the wrong type threw out of load_index, so the player did not start.
  • With two processes on one library (the example Deployment had no strategy, so a rolling update could run the old and the new pod together), the last one to write index.json dropped the items the other had added.

Reproduced with the released images: 1.2.4 with 3 items, index.json made read-only, then 4 more uploads. 1.2.4 lists 7 and mxl_test_player_library_items is 7, but index.json still lists 3. After a restart on 1.3.0 the library has 3 items, with 7 folders on disk and nothing in the log. The two-pod case was also reproduced: 1.2.4 and 1.3.0 on one directory; after a re-upload to the new pod, index.json lists 4.

Fix

  • index.json and item.json are written to a unique temporary file and renamed over the old one. A reader or a restart sees the old file or the new one, never part of one. A read-only index.json is still replaced. A failed write is logged (ERROR library: …). Invalid UTF-8 no longer throws in dump. All save_index callers already hold mu_; that is now written in a comment.
  • load_index: a bad entry, or an index.json that is not valid JSON, is logged and skipped, not the rest.
  • At start, every item folder whose item.json is not in index.json is restored from that item.json. Each one is logged as WARN library: restored <id> (<name>) …, and then index.json is written again. The start also logs INFO library: N items (M restored from item.json). This brings back items lost this way before 1.3.1.
  • A delete removes item.json first, so a folder that cannot be removed completely does not come back.
  • deploy/k8s/deployment.yaml: strategy: Recreate.

Tests

New unit test "the library loses no item…" covers a bad entry, a stale index, a cut-off and an empty index, a deleted item that must stay deleted, and a read-only index.json (the last one is skipped when running as root). On v1.3.0 the test fails (type_error.302 thrown out of the constructor). With the fix the test passes, and so does the full suite (37/37). In the lab, the fixed binary on the stale 3-item library from the reproduction starts with 7 items and logs 4 restores.

Docs: CHANGELOG ## 1.3.1 (unreleased), SPECIFICATION §6.3, README (PLAYER_LIBRARY_DIR).

🤖 Generated with Claude Code

https://claude.ai/code/session_017ybuzgBaGxuB5UiaE8dRa4

index.json was rewritten in place and a failed write was ignored. When it
could not be written, the running player still listed every item, but the
file kept an older list; the next start loaded only that, and the first
change after it made the loss permanent. A write cut short left a broken
file and the next start loaded nothing. A bad entry threw out of the start.

- index.json and item.json are written to a temporary file and renamed;
  a failed write is logged.
- A bad entry or a broken index.json is logged and skipped, not the rest.
- At start every item folder whose item.json is not in index.json is
  restored from it (logged), and index.json is written again.
- A delete removes item.json first, so a half-removed folder stays gone.
- deploy/k8s: strategy Recreate (one player per library).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017ybuzgBaGxuB5UiaE8dRa4
@LeeO86
LeeO86 merged commit acb0246 into main Oct 30, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant