Skip to content

Calling QueueSave() on every change means SaveIfRequired() never saves: with no maximum wait, a crash loses everything since the last 3 s idle gap #329

Description

@matt-edmondson

What's wrong

Every QueueSave() resets SaveQueuedTime (AppDataStorage/AppData.cs:250), and SaveIfRequired() only saves once DateTime.UtcNow - SaveQueuedTime > SaveDebounceTime (IsDoubounceTimeElapsed, AppData.cs:474). Nothing caps how long a queued save can be put off, so as long as saves keep being queued less than 3 s apart, SaveIfRequired() never writes.

Why it matters

The README recommends QueueSave() for "frequent updates (e.g., UI-driven changes)" and calling SaveIfRequired() from a game loop or timer. That is exactly the pattern that starves the save:

  • dragging a window or slider
  • typing into a bound text field
  • any app that queues a save each frame while something animates

Nothing reaches disk until input pauses for 3 s. A clean exit still flushes through the dispose-on-exit hook, but a crash, kill or power loss loses every change since the last idle gap. For a long editing session with no pause, that is all of it.

Reproduction

The loop below calls QueueSave() and SaveIfRequired() every 100 ms for 8 s:

var s = MySettings.Get();
var sw = Stopwatch.StartNew();
while (sw.Elapsed < TimeSpan.FromSeconds(8))
{
    s.Theme = "dark";
    s.QueueSave();
    s.SaveIfRequired();
    Thread.Sleep(100);
}
// file on disk still has Theme = "light"; 0 saves happened

Observed result: saves during 8s of continuous QueueSave: 0, and the file on disk is unchanged.

Suggested fix

Add a maximum wait alongside the debounce:

Acceptance criteria

  • A test with continuous QueueSave() + SaveIfRequired() calls, using a mockable clock or short intervals, shows a save happening within the max-wait bound.
  • The existing behaviour is unchanged: a single queued save still waits for the debounce.

Related: #324 (wall-clock debounce). This issue is about starvation, not clock steps.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions