← 목록
기타 2026-07-18 5KB 읽기 5분

CrownyShared 공유장부 회전 유틸 — P0 예방조치

개요

~/Documents/CrownyShared/이벤트버스.psv(append-only)와 docindex.psv(upsert)가 무회전이라, VM 구독측 읽기() 캡(16384B~64KB)·한글 3B/char 특성상 수백 이벤트로 커지면 wedge될 위험이 있었다(P0 예방조치, 실사고 발생 전 선제 조치). 이번 작업에서 바이트 기준 회전 엔진을 한선씨로 구현하고 30분 주기 LaunchAgent로 자동화했다.

무엇을 했는지

  1. bundle/erp/bridge/장부회전.한선 — 회전 엔진(한선씨, ~440줄)
- 정책: 파일바이트크기 > 12000B → 회전 진입. 이하(빈 파일·미존재 포함)는 무해 통과. - 이벤트버스.psv: 최신 행부터 역순 누적해 8000B 이내로 남기고, 초과분(구행)을 행 단위로 보관/에 이동(절단 금지, 후행 개행 보존). - docindex.psv: 같은 id 최신 행만 남기는 중복 정리(폐기, 보관 없음) → 그래도 12000B 초과면 mtime(문자열 사전순 비교) 오래된 행부터 8000B 이내까지 보관 이동. - 보관 경로: ~/Documents/CrownyShared/보관/<원본명>-<YYYYMMDD-HHMM>.psv (append). - 자체시험 22/22 PASS (STRICT 컴파일 경고 0).

  1. bundle/erp/bridge/장부회전-실행.sh — 4항 mtime 비교 재컴파일 래퍼
(결재러너.sh 정본 패턴: 소스뿐 아니라 hanseonc_high/crownyc 바이너리 mtime도 비교 — stale-toau 함정 회피). .toau 핀 위치 = bundle/erp/bridge/data/장부회전.toau.

  1. ~/Library/LaunchAgents/org.crowny.ledger-rotate.plist — StartInterval 1800초,
RunAtLoad, PATH 명시(launchd 서브프로세스 PATH 함정 회피), 로그 /tmp/crowny-ledger-rotate.log.

  1. bundle/erp/spec/ERP-연동계약.md §7 추가(말미 append만, 기존 내용 무수정) —
회전 정책·보관 경로·구독측(투데이·빌딩 등) 규칙.

검증 게이트 (a~e 전부 PASS)

  • a. STRICT 컴파일 0 경고 + 자체시험 22/22 PASS
  • b. 사본 시험: 실장부 사본을 /tmp 스크래치에 복사 후 실사 스타일 데이터로
40238B/31384B까지 부풀려 회전 실행 → 이벤트버스 40238→7920B(208=40잔류+168보관), docindex 31384→7998B(505=129잔류+376보관). 행 무손실 확인.
  • c. 실장부 1회 실행: 실장부(858B/714B, 둘 다 임계치 이하)는 무해 통과
(858|858|0, 714|714|0) — 실행 전후 MD5 완전 동일(diff 0) 확인.
  • d. LaunchAgent: load 성공, launchctl listorg.crowny.ledger-rotate 등록
확인, 로그에 실행 기록(RunAtLoad로 즉시 1회 확인).
  • e. 학습: crownycode-brain.sh learn "PSV장부_바이트회전_보관이동" +
crownycode-learn.sh add "PSV장부바이트회전보관이동" 등록 완료.

개발 중 발견한 VM 함정 (신규, 한선씨-가드레일.md 반영 권고)

  1. 누산=누산+함수호출() 인라인 금지가 자체시험 집계에도 그대로 적용됨을
재확인 — 통과 = 통과 + 검증정수(...) 패턴이 통과카운트를 거의 다 누락시킴 (22개 중 1개만 집계). 임시변수 경유(변수 p = 검증...(); 통과=통과+p)로 수정.
  1. 배열반환함수를 while 루프에서 거듭 호출하면 힙이 폭주한다(신규 실측,
기존 "함수 재호출 배열 지역변수" 함정과 별개 증상): 배열제외()+최소mtime인덱스()를 반복 호출하는 선택삭제 루프가 [ARRAY] OOM(144M 셀 힙 고갈)으로 크래시. 해결: 선택정렬로 인덱스 순서를 1회만 계산하고, 이후는 제외마크 배열로 단일 패스 분리.
  1. 위 수정 후에도 재발: 선택정렬 내부 비교마다 필드꺼내()→분리()로 PSV
줄을 매번 다시 쪼개면 O(n²) 비교 횟수만큼(n=400→16만회) 문자열 split이 발생해 문자열핸들(480000) 조기경보+ARRAY OOM 재발. mtime 필드를 진입 시 배열로 1회만 추출해두고 비교는 그 배열 원소만 읽도록 수정(재파싱 0회)해 해결.
  1. 분단위 보관 파일명 타임스탬프 재조회 함정: 자체시험에서 회전 직후가
아니라 나중에 날짜스탬프()를 다시 호출해 기대 보관 경로를 계산하면, 그 사이 분 경계를 넘을 경우 실제 생성된 archive 파일명과 어긋나 거짓 FAIL(실제로는 정상 동작)이 뜬다. 회전 호출 직후 즉시 스탬프를 캡처해 재사용하는 것으로 수정.

관련 파일

  • /Users/ef/CrownyBrowser/bundle/erp/bridge/장부회전.한선
  • /Users/ef/CrownyBrowser/bundle/erp/bridge/장부회전-실행.sh
  • /Users/ef/CrownyBrowser/bundle/erp/bridge/data/장부회전.toau (컴파일 산출물, 영속 핀)
  • /Users/ef/Library/LaunchAgents/org.crowny.ledger-rotate.plist
  • /Users/ef/CrownyBrowser/bundle/erp/spec/ERP-연동계약.md (§7 추가)

잔여 이슈

  • 구독측(투데이·빌딩) 실배선은 계약(§7)에 규칙만 명시, 실제 소비 코드는 각
모듈 후속 punch(ERP-연동계약.md §1 기존 기록과 동일 기조).
  • mtime 비교가 문자열 사전순이라, 실장부처럼 자릿수가 동일한 epoch초(10자리)
전제가 깨지면(예: 자릿수 다른 값 혼재) 순서가 어긋날 수 있음 — 현재 실장부 데이터로는 안전(전부 10자리), 향후 포맷 변경 시 재검토 필요.