DeviceId writes device.txt non-atomically on a shared volume #24

Open
opened 2026-09-13 13:33:16 +10:00 by unkin-agent · 0 comments
Member

Context: multi-replica stateless deployment (two 2-replica apps, cheeztv and fafflix, each one PostgreSQL plus valkey, no sticky sessions). Not a 12.0 regression — live in the deployed build. Tracking: #12.

Problem

Emby.Server.Implementations/Devices/DeviceId.cs persists the generated id to device.txt on the shared /config volume with a plain File.WriteAllText, and memoises the value it read.

Two pods doing first boot simultaneously can interleave that write, leaving a truncated or mixed file. A pod that read the partial value then serves a wrong SystemId for its whole lifetime, because the value is memoised.

Low impact and rare — but the window exists on every fresh deployment, and the fix is a one-liner.

Proposal

Write to a temporary file on the same filesystem and File.Move it into place with overwrite, so a reader only ever sees a complete value. Memoisation is fine once the write is atomic; no need for a watcher.

Acceptance

  • The write path is atomic: temp file plus rename on the same filesystem, no partial state visible to readers.
  • A test with concurrent writers against a shared directory always reads a complete, valid id — never a truncated or mixed one.
Context: multi-replica stateless deployment (two 2-replica apps, `cheeztv` and `fafflix`, each one PostgreSQL plus valkey, no sticky sessions). Not a 12.0 regression — live in the deployed build. Tracking: #12. ## Problem `Emby.Server.Implementations/Devices/DeviceId.cs` persists the generated id to `device.txt` on the shared `/config` volume with a plain `File.WriteAllText`, and memoises the value it read. Two pods doing first boot simultaneously can interleave that write, leaving a truncated or mixed file. A pod that read the partial value then serves a wrong `SystemId` for its whole lifetime, because the value is memoised. Low impact and rare — but the window exists on every fresh deployment, and the fix is a one-liner. ## Proposal Write to a temporary file on the same filesystem and `File.Move` it into place with overwrite, so a reader only ever sees a complete value. Memoisation is fine once the write is atomic; no need for a watcher. ## Acceptance - The write path is atomic: temp file plus rename on the same filesystem, no partial state visible to readers. - A test with concurrent writers against a shared directory always reads a complete, valid id — never a truncated or mixed one.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: unkin/jellyfin-ha-src#24