edge-host: batch zwave-js fsync via libeatmydata (+ lazytime) to spare the eMMC #120

Merged
k.renner merged 1 commits from feat/edge-zwave-fsync-batching into master 2026-07-07 09:30:37 +00:00
1 Commits
Author SHA1 Message Date
k.rennerandClaude Opus 4.8 9add6f1a56 edge-host: batch zwave-js fsync via libeatmydata (+ lazytime) to spare eMMC
With logs off flash, the dominant remaining eMMC writer is zwave-js-ui: its
value DB is fsync'd on every value change (jsonl-db; zwave-js-ui doesn't
expose the driver throttle). On a busy network that fsync storm churned
~2.6 GiB/day for ~0.1 KiB/s of real data — near-pure write amplification
(each fsync forces a block write + ext4 journal commit).

Preload libeatmydata into the zwave-js container (musl .so extracted from the
image's Alpine base, mounted ro, LD_PRELOAD) so fsync/fdatasync become no-ops
and the kernel batches writes via normal writeback. The WS/MQTT value stream
to HA and the on-disk cache are unchanged — full data resolution, no
re-interview on restart. No ext4 mount option can do this (fsync is a
durability contract; commit=/data=writeback/nobarrier don't batch it, and the
only mount-level alternative — tmpfs — makes the whole store volatile).

Also:
- noatime -> noatime,lazytime on root: batches the inode-timestamp writeback
  (e.g. the zwave-js lockfile heartbeat) without weakening data durability.
- edge-zwave-settings-backup.timer: hourly validated rolling backup of
  settings.json (Z-Wave network keys), since neutralized fsync means a
  power-loss mid settings-write could truncate it. Value-cache loss on crash
  is harmless (devices re-report; jsonl-db tolerates a partial trailing line).

Verify the drop on the eMMC-write monitor; rollback = drop the LD_PRELOAD env
+ .so mount from the compose file.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 11:26:46 +02:00