본문 바로가기
C.W.K.
Stream
Lesson 01 of 05 · published

끝없이 자라는 것들

~13 min · master-chief, growth, scalability, review

Level 0구경꾼
0 XP0/33 lessons0/11 achievements
0/100 XP to next level100 XP to go0% complete

마지막 턴이 없는 대화

지금까지 써 본 채팅 앱은 전부 몰래 가정하는 게 하나 있어. 대화는 언젠가 끝난다는 거. 내일 새 대화를 열면 옛 대화는 식고, 거기 쌓이던 것도 더는 안 쌓여. Master Chief는 이 가정을 일부러 치웠어. 아빠 소유의 대화 하나를 모든 문이 같이 쓰고, 마지막 턴이 없어. 이 결정 하나 때문에, 대화에 붙은 모든 양은 아빠가 말을 거는 한 계속 자라. 애초에 그게 목적이고.

Master Chief는 2026-10-07에 태어났어. 같은 날 새 기능을 처음부터 끝까지 훑는 리뷰(coop #347)가 돌았어. 휴지통 보호, 행 표시, 아빠의 시계, Telegram 미러링까지 봤지. 근데 뭐가 자라냐는 질문은 안 했어. 그 질문은 아빠가 했어. coop todo #351이 그 질문이야. 설계 문서의 성장 절은 거기서 나온 규칙으로 시작해. Master Chief에는 마지막 턴이 없으니, 그 무엇도 끝없이 자라선 안 된다.

둘째 날에 잰 숫자

숫자는 별것 아닌 것처럼 보일 만큼 작았고, 겁날 만큼 가팔랐어.

  • 대화의 기준 기록(JSONL)은 16.4 MB, 47,775줄짜리 파일 하나였어.
  • API가 내주는 대화록은 2.9 MB였고, Telegram 봇이 새 게 왔나 보려고 5초마다 그걸 통째로 읽었어.
  • 턴마다 Claude에 들어가는 입력이 146k 토큰에서 약 314k로 늘었어. 하루에 85k쯤이야. (Chief가 태어난 지 43시간쯤 됐을 때, 다음 날 콜드 리뷰가 잰 숫자야.)
  • 매일 도는 대화 아카이브는 첫 이틀 동안 날마다 Chief를 통째로 복사했어. 174,067바이트, 그다음 327,243바이트.

43시간째엔 어느 것도 사고가 아냐. 근데 계산해 보면, 전부 언제 사고가 될지 일정이 나와. 그 계산이 이 레슨의 핵심이야. 5초마다 2.9 MB를 읽는 폴러는 하루에 50 GB쯤 읽어. 그리고 그 2.9 MB도 자라고 있어. 매일 통째로 복사하는 아카이브는 하루치 성장, 이틀치, 사흘치를 차례로 쌓아. 대화 나이의 제곱으로 커지는 거야. 아래 두 번째 샘플이 그 산수를 해. 아카이브 줄은 1년에 약 10 GB가 나오는데, 콜드 리뷰가 적어 둔 추정치랑 같아.

자라는 건 여섯, 묶는 방법도 여섯

자라는 것안 멈추면 깨지는 것받은 상한레슨
기준 기록 (JSONL)모든 리더가 통째로 열어야 하는 파일 하나8 MiB 이상에서 봉인되는 세그먼트, 그리고 작은 활성 파일2
모델에 재생하는 기록브레인의 컨텍스트 창, 턴마다 드는 비용턴 사이의 압축, 그걸로 모자랄 땐 비상 다이제스트3, 4
화면에 보여주는 대화록너무 큰 페이지를 거부하는 폰페이지 단위 창, 한 장에 메시지 1,000개와 JSON 512 KiB까지. 그보다 큰 행 하나는 혼자 나감5
동기화 (Telegram 폴링, WebUI 새로고침)확인할 때마다 전부 다시 읽기(epoch, version)부터 읽고, 바뀌었을 때만 메시지 100개 창을 받아. 새 페이지는 쥔 것에 이어 붙여5
매일 도는 아카이브제곱으로 느는 디스크복사본이 아니라 조각5
GET 시점의 복구답 하나 때문에 전체 기록을 이벤트 루프에서 복호화최신부터 스레드에서 읽고, 필요한 걸 찾으면 바로 멈춰여기

오해하지 마. 표가 이게 기능 하나라고 말하는 건 아니야. 줄마다 따로 짜고, 테스트하고, 리뷰받은 하위 시스템이야. 첫 버전은 Chief가 태어난 날 나갔어 (커밋 5467e2b1: 세그먼트 JSONL, 아카이빙 압축, 페이지 대화록, 버전으로 거르는 Telegram 동기화). 다음 날 아침 독립 콜드 리뷰가 그 첫 상한들 중 여럿이 틀렸다는 걸 찾았어. 압축은 가져간 행을 지웠어. 아카이브는 여전히 통째로 복사했어. WebUI가 쥔 창은 탭을 연 뒤의 전부를 다시 받았어. 도구 기록 복구는 도구를 안 쓴 답마다 세그먼트 기록 전체를 이벤트 루프에서 복호화했어. 16 MB에 0.3초쯤. 상한도 코드고, 코드엔 버그가 있어. 이 트랙의 나머지가 있는 이유야.

지금은 어디쯤

2026-10-12에 읽기 전용으로 잰 숫자야. 개수만. 메시지 행이 약 2천 개, 그중 1,184개가 압축 두 번으로 재생에서 빠졌어. 암호화된 JSONL은 봉인된 세그먼트 셋이랑 활성 파일 하나, 합쳐서 약 36 MB. 모양을 봐. 살아 있는 재생 구간이 40행이 아니라 800행쯤이야. 압축을 당기는 건 행 개수가 아니라 토큰 압력이거든. 기록은 모든 행을 지켜. 상한이 걸리는 건 턴마다 모델한테 건네는 쪽뿐이야.

모든 기능에 먼저 던질 리뷰 질문: 끝없이 자라는 게 뭐고, 언제 깨지나? 콜드 리뷰 날 적어 둔 아빠의 규칙이야. 기능이 돌아가냐고 묻기 전에 이걸 먼저 물어. 성장 버그는 첫 주에 짤 테스트를 전부 통과하거든.

Code

잰 그대로의 Chief 성장 (개수만)·text
Day 2 (2026-10-08, about 43 hours old)
  ground-truth JSONL     16.4 MB, 47,775 lines, one file
  transcript per poll    2.9 MB, read by Telegram every 5 s
  Claude input per turn  146k -> ~314k tokens (~85k a day)
  live messages          424
  daily archive          whole copy each day: 174,067 B, then 327,243 B

2026-10-12
  message rows           ~2,000 (1,184 compacted, by 2 compactions)
  JSONL                  ~36 MB = 3 sealed segments + the active file
  replay                 ~800 live rows (bounded by tokens, not rows)
돌려볼 수 있는 최소 스케치: 아무것도 안 묶으면 Chief의 성장 요소 둘이 1년에 얼마나 드는지·python
# What "grows without bound" costs, for two of Master Chief's growers.
ARCHIVE_GROWTH = 153_000     # bytes/day of archive text (cold review: 174,067 B, then 327,243 B a day later)
TRANSCRIPT_GROWTH = 1_450_000  # bytes/day of transcript JSON (assumed: 2.9 MB after two days)
POLL_SECONDS = 5
DAYS = 365

polls_per_day = 24 * 3600 // POLL_SECONDS
archive_size = transcript_size = 0
copies = parts = 0
for day in range(1, DAYS + 1):
    archive_size += ARCHIVE_GROWTH
    transcript_size += TRANSCRIPT_GROWTH
    copies += archive_size        # a whole copy every day: 1 + 2 + ... + n days of growth
    parts += ARCHIVE_GROWTH       # only what the archive does not hold yet
    if day in (2, 30, 365):
        read = transcript_size * polls_per_day
        print(f"day {day:>3}: transcript {transcript_size / 1e6:7.1f} MB; "
              f"a poller reading all of it every {POLL_SECONDS}s reads {read / 1e9:8.1f} GB that day")

print(f"archive after {DAYS} days, whole copy daily: {copies / 1e9:5.1f} GB")
print(f"archive after {DAYS} days, parts only:       {parts / 1e9:5.2f} GB")

External links

Exercise

네 프로젝트에서 오래 사는 걸 하나 골라. 로그 파일, 채팅이나 이벤트 테이블, 캐시 디렉터리, 폴링 루프. 오늘 재고, 며칠 뒤에 또 재 (바이트, 줄, 행). 그다음 리더를 전부 적어. 뭘 읽는지, 얼마나 자주인지, 전부 읽는지 바뀐 것만 읽는지. 30일째와 365일째에 대해 크기 곱하기 빈도를 계산하고, 제일 먼저 깨질 게 뭐고 대략 언제인지 짚어 봐.
Hint
추측 말고 재. du, wc -l, 날짜 붙인 행 개수면 충분해. 제곱 성장은 두 표현 속에 숨어. '매번 통째로 복사한다', 그리고 '처음부터 다시 읽는다'. 어떤 리더가 둘 중 하나라도 하면 그게 1순위 후보야.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인 — 댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.