프로세스가 죽어도 남는다 — 가장 안 똑똑한 방법으로

5 분
목표MCP 서버 강좌 5편. 대기 인원이 변수에 있으면 프로세스와 함께 사라진다. 파일에 적는데, 매번 전체를 다시 쓴다. 부분 갱신보다 느리지만 반쯤 쓰이다 만 기록이 생기지 않는다.

4편까지 대기 인원은 이렇게 살았다.

var waiting = 3;

프로세스가 죽으면 3으로 돌아간다. 데스크 화면을 뽑았다 꽂으면 줄이 없던 일이 된다.

적는다

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}));

그리고 값이 바뀔 때 부른다.

waiting -= n;
_save(waiting);

왜 전체를 다시 쓰나

값 하나 바뀌는데 파일 전체를 다시 쓴다. 비효율적이다. 그런데 이 방식에는 성질이 하나 있다.

반쯤 쓰인 기록이 생기지 않는다. 쓰기가 일어났거나 안 일어났거나 둘 중 하나다. 중간에 죽으면 옛 파일이 그대로 남는다.

부분 갱신은 그렇지 않다. 레코드 하나를 제자리에서 고치다 죽으면 어느 쪽도 아닌 파일이 남고, 다음 부팅에서 파싱이 깨진다. 그때부터는 복구가 일이 된다.

기록이 수만 건이 되면 이 방식은 못 쓴다. 그때가 DB 를 쓸 자리다. 다만 그전까지는 이게 제일 안전하다.

어느 층에 두는가

상태가 있을 수 있는 자리가 셋이다.

층 여러 화면이 같은 값을 보나 프로세스가 죽으면
화면 안 아니다 — 화면마다 다른 값 사라진다
서버 프로세스 그렇다 사라진다
서버 밖(파일·DB) 그렇다 남는다

4편까지는 두 번째였다. 이번 편이 세 번째로 옮긴다.

정하지 않으면 두 번째가 된다. 그리고 그게 문제가 되는 날은 대개 밤이다.

검증 — 프로세스를 실제로 죽인다

영속은 "저장했다" 를 보면 안 된다. 끊고 다시 붙어서 읽어야 한다.

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

ask 가 매번 새 프로세스를 띄운다. 첫 호출이 끝나면 그 프로세스는 죽는다.

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

서버 로그에도 남는다.

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

두 번째 부팅이 1로 시작한다.

직접 돌려보기

dart run bin/step5.dart
bash verify.sh

가져갈 것

  1. _load / _save 두 함수 — 없으면 3부터 시작한다는 기본값 포함
  2. 전체 재기록 — 반쯤 쓰인 기록을 만들지 않는다
  3. 재시작 왕복 검사 — 저장 로그가 아니라 새 프로세스의 읽기로 확인

다음 편

값이 바뀐 걸 화면이 알게 한다. 묻지 않고.

샘플 실행하기

makemind-academy/course_server/
git clone https://github.com/makemind-academy/course_server
cd course_server
dart pub get
dart run bin/step5.dart
GitHub에서 열기 →