You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
config: rework saving to be atomic and asynchronous
Config.save used os.WriteFile, which truncates the target before writing: killing awl in that window destroyed the config, losing the node's private key and known peers irrecoverably, and readers in other processes (awl-tray, CLI subcommands) could see the truncated file. A config that failed to parse was then quietly discarded — callers fall back to a fresh default that overwrites the same path with a new identity on the next start.
Making the write atomic turns a save into an fsync, ~7ms instead of ~0.1ms, and it happened inline under the caller's lock. Tunnel's VPN gateway setters save while holding peersLock, which HandleReadPackets needs for every batch read from the TUN, so the packet path stalled for the duration of the write.
- Writes go through moby/sys/atomicwriter: staged next to the target, fsynced, renamed over it.
- An unparseable config is copied to config_awl.json.corrupt-<ts> before the fallback, which is now logged as an error rather than a warning indistinguishable from a first run.
- Save hands the request to a single writer goroutine over a capacity-one channel and returns. The writer marshals at write time, so a burst collapses into one write of the newest state.
- Config.Close flushes, stops the writer and closes the emitter, which was never released before — a leak on every gomobile restart.
- NewConfigReadOnly / LoadConfigReadOnly for configs that are only read (CLI, tray without a running server, gomobile GetConfig and DnsServerIP): no writer, no emitter, nothing to close.
- UpdatePeerFields saves and emits only when something other than LastSeen changed. Status exchanges are the dominant caller and usually change nothing else.
0 commit comments