It Survives the Process — by the Least Clever Method

5 min
GoalFifth in the mcp_server course. A waiting count in a variable dies with the process. It gets written to a file — the whole file, every time. Slower than a partial update, and it never leaves a half-written record.

Through part 4 the waiting count lived like this.

var waiting = 3;

Kill the process and it is 3 again. Unplug the desk screen and the queue never happened.

Write it down

const _statePath = 'desk-state.json';

int _load() {
  final f = File(_statePath);
  if (!f.existsSync()) return 3;
  final m = jsonDecode(f.readAsStringSync()) as Map<String, dynamic>;
  return (m['waiting'] as num).toInt();
}

void _save(int waiting) =>
    File(_statePath).writeAsStringSync(jsonEncode({'waiting': waiting}));

And call it when the value changes.

waiting -= n;
_save(waiting);

Why rewrite the whole file

One value changes and the whole file is rewritten. Inefficient. But it has a property.

It never leaves a half-written record. The write either happened or it did not. Die in the middle and the old file is still there.

A partial update is not like that. Edit one record in place and die, and a file that is neither is left, and the next boot fails to parse. From there, recovery is work.

At tens of thousands of records this does not hold. That is where a database belongs. Until then, this is the safest thing there is.

Which floor

State can live on one of three floors.

Floor Do several screens see one value When the process dies
Inside the screen no — a different value per screen gone
The server process yes gone
Outside the server (file, DB) yes stays

Through part 4 it was the second. This piece moves it to the third.

If you do not decide, you get the second. And the day that becomes a problem is usually at night.

Verification — actually kill the process

Persistence is not confirmed by seeing "saved." It is confirmed by disconnecting and reading again.

rm -f desk-state.json
ask step5 '...{"name":"desk.admit","arguments":{"count":2}}}' >/dev/null   # process 1
ask step5 '...{"name":"desk.state","arguments":{}}}' > captures/s5.txt      # process 2
grep -q 'waiting.*1' captures/s5.txt || die "the count did not survive a restart"

ask starts a new process each time. When the first call ends, that process is gone.

step5  admitted 2, process ended, a new process still reads waiting 1

The server log records it too.

course step5: up, state in desk-state.json (waiting=3)
course step5: up, state in desk-state.json (waiting=1)

The second boot starts at 1.

Run it yourself

dart run bin/step5.dart
bash verify.sh

What to take

  1. _load and _save — including the default when the file is not there yet
  2. Whole-file rewrite — never leaves a half-written record
  3. A restart round trip — confirm from a new process's read, not from a save log

Next

Let the screen know the value changed. Without it asking.

Run the sample

cd course-server
dart pub get
dart run bin/step5.dart