고아 프로세스와 레지스트리
좀비 프로세스가 쌓임
섹션 제목: “좀비 프로세스가 쌓임”원인. 강제 종료된 호스트입니다. 프로세스 내부의 모든 방어 장치 — job object, 프로세스 그룹, 워치독 — 는 우리 코드가 얼마간 실행되어야 하는데, SIGKILL은 그 어느 것도 실행하지 않습니다. CLI는 살아남고, 이후의 어떤 호스트에게도 보이지 않습니다.
해결. 이것이 CliRegistry가 존재하는 이유입니다. spawn 시 등록하고, 종료 시 등록 해제하며, 시작 시 훑으십시오.
const registry = new CliRegistry(stateDirectory);
// 시작 시, 무엇이든 spawn하기 전에.registry.prune();for (const orphan of registry.orphans()) { console.warn('left behind by a dead host:', orphan.pid, orphan.sessionId); // 죽이거나, 죽일지 물어보십시오 — 결정은 호스트의 몫입니다.}
// 각 세션 주변에서.registry.register(childProcess, sessionId, workingDirectory);session.onClose(() => registry.unregister(session.pid));워치독도 함께 걸어두어, 흔한 경우 — 호스트가 정리 코드를 실행하지 않고 닫히는 것 — 를 프로세스 내부에서 잡으십시오.
new ParentWatchdog().start(() => { session.stop('SIGKILL'); process.exit(0);});레지스트리가 아무것도 보고하지 않음
섹션 제목: “레지스트리가 아무것도 보고하지 않음”| 원인 | 상세 |
|---|---|
| state 디렉토리에 쓸 수 없음 | register()는 설계상 쓰기 실패를 삼킵니다 — state 디렉토리에 쓸 수 없는 호스트라도 CLI는 실행할 수 있어야 합니다 |
| pid가 다른 것으로 돌아옴 | liveEntries()는 session id가 여전히 프로세스의 커맨드라인에 나타날 것을 요구하므로, 재사용된 pid는 올바르게 제외됩니다 |
| identity를 읽을 수 없었음 | commandLine()이 null을 반환하는 것은 죽었다는 뜻이 아니라 알 수 없다는 뜻이며, 알 수 없음은 의도적으로 일치로 치지 않습니다. 그 대안이 남의 프로세스를 죽이는 것이기 때문입니다 |
한 대화에서 모순된 답변이 나옴
섹션 제목: “한 대화에서 모순된 답변이 나옴”원인. 두 CLI가 같은 대화에 덧붙이고 있고, 그래서 히스토리가 분기합니다.
해결. resume하기 전에 물어보십시오.
const existing = registry.findLive(sessionId);if (existing) { throw new Error(`session ${sessionId} is already driven by pid ${existing.pid}`);}이것이 고아 정리와 나란히 레지스트리가 존재하는 두 번째 이유입니다.
여러 장치가 겹치는 이유
섹션 제목: “여러 장치가 겹치는 이유”각 방어 장치는 서로 다른 방식으로 실패하며, 그래서 어느 하나만으로는 충분하지 않습니다. 전체 표는 아키텍처에 있습니다. 요약하면, 레지스트리는 프로세스 내부의 모든 방어 장치가 놓친 것을 사후에 잡습니다. spawn 시 파일을 쓰고 종료 시 지우는 것이, 호스트가 통째로 죽임당해도 살아남는 유일한 기록이기 때문입니다.