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

추가 전용인데, 정정은 지운다

~12 min · append-only, invariant, data-integrity, history

Level 0원석
0 XP0/36 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
"추가 전용은 정정이 자기가 무효화한 걸 제거한다는 뜻이다. 대체물 옆에 남겨두는 일은 절대 없다."

겉보기 모순

저장 불변식은 스냅샷이 추가 전용이라고 말해. 지표 역사 행은 계열, 날짜, 값, 출처, 가져온 시각을 들고 있고 절대 고쳐 쓰지 않는다고. 그런 다음 같은 불변식이 정정은 삭제한다고 말해. 빨리 읽으면 두 문장이 싸워.

안 싸워. 그리고 이 긴장을 푸는 게 이 레슨에서 제일 쓸모 있는 부분이야. 절반만 붙잡고 나머지 절반을 놓치는 팀이 꽤 많거든.

추가 전용이 실제로 지키는 것

추가 전용은 역사의 조용한 제자리 변형을 막으려고 존재해. 막으려는 실패는 예전엔 이거였는데 지금은 저거인 값이, 바뀌었다는 걸 어디에도 기록 안 한 채로 있는 상태야. 역사 계열을 못 믿게 만드는 건 그거야. 한 번 틀렸다는 게 아니라, 지금 보고 있는 게 기록된 그거인지를 더 이상 알 수 없다는 거.

그러니까 규칙은 이거야. 행의 값을 제자리에서 고치지 않는다. 새 값은 새 행으로 와. 역사는 수정되는 게 아니라 쌓여.

추가 전용이 아닌 것

추가 전용은 한 번 쓰인 행은 참이든 아니든 영원히 남아야 한다는 규칙이 아니야. 그렇게 읽으면 불변식이 거짓인 줄 아는 것들을 보존하는 기계가 돼. 그리고 계열별 최신 행을 집는 읽기 모델을 가진 저장소에서, 보존된 거짓은 무해한 역사 유물이 아니야. 제품이 내놓는 답이지.

그게 정확히 벌어진 일이고. 앞으로 찍힌 행들은 "기록용으로 남겨야 할 역사" 가 아니었어. 일어난 적 없는 장을 설명하고 있었고, 읽기 모델 맨 앞에 앉아 있었어. 그걸 갖고 있는다는 건 제품이 맞는 것보다 추상적 순수함을 고르는 거였을 거야.

틀린 값이랑 대체된 값을 구분해. 맞았는데 소스가 개정한 값은 역사야. 둘 다 갖고, 날짜가 순서를 잡게 둬. 일어나지 않은 사건을 설명하는 행은 역사가 아니라 오류고, 제자리에 둔 오류는 보관되는 게 아니라 서빙돼. 추가 전용은 첫 번째 경우를 지켜. 두 번째에 대해서는 할 말이 없고.

실제 수리가 어떻게 생겼냐

이 사고의 수리는 이름 붙고 날짜 붙은 스크립트였어. DB 를 붙잡고 대화형으로 만진 게 아니라. 이 구분이 보이는 것보다 중요해.

스크립트는 임시방편 수정에 없는 성질 셋을 가져. 어떤 행을 무슨 기준으로 지우는지 코드로 정확히 말하고, 돌리기 전에 다른 사람이 읽을 수 있고, 끝난 뒤에도 뭘 했는지의 기록으로 저장소에 남아. 나중에 누가 "이 계열은 왜 8 월 초에 구멍이 있지?" 라고 물으면, 답이 버전 관리에 앉아 있는 이름과 날짜가 붙은 파일이야.

삭제 기준은 필요하다고 생각하는 것보다 좁게 잡아. 구체적인 결함으로 지워. 이 게이지들, 이 시장, 이 정확한 날짜들, 이 출처 문자열. 그걸 우연히 포함하는 넓은 범위로는 절대 안 되고. 데이터 수리 중의 너무 넓은 삭제는 이 도메인에서 진짜로 복구 불가능한 몇 안 되는 실수고, 하필 서두르게 만드는 조건에서 일어나.

Code

상태 셋, 서로 다른 옳은 행동 셋·text
CASE 1 -- a NEW reading for the same series
  action:  append a new row with its own data_date
  history: both rows remain; dates order them
  why:     the series moved. That is what a series does.

CASE 2 -- the SOURCE revised a past value
  ideal:   keep both -- "what we knew then" is itself a fact,
           and quarterly macro data is revised routinely
  ACTUAL:  THIS store cannot. Its primary key is
           (gauge, market, series, data_date) and the write is
           INSERT OR REPLACE, so a revision for the same series
           and date overwrites the original IN PLACE -- the one
           thing append-only was supposed to prevent.
  why it is tolerable here: the gauges are published series whose
           vendors are themselves the record of revisions.
  what it would cost to fix: `fetched_at` or `source` in the key,
           and then every read model needs a rule for which of
           several rows for one date it means.
  -> Know which of these you have. "Append-only" describes a
     WRITE discipline; whether history is actually retained is a
     property of the KEY, and the two are easy to conflate.

CASE 3 -- the row describes something that NEVER HAPPENED
  action:  DELETE, by a narrow criterion, in a named script
  history: nothing is lost, because nothing true was there
  why:     it is not history, it is an error -- and in a
           newest-row read model, an error is the ANSWER

# The mistake is treating case 3 as case 2 out of respect for
# append-only. Purity that serves a false number is not integrity.

External links

Exercise

네가 소유한 시계열에 대해 세 경우를 직접 적어봐. 새 값, 상류 개정, 불가능한 행. 필요해지기 전에 각각의 행동을 정해둬. 그다음 네 시스템이 지금 세 번째 경우에 뭘 하는지 확인해. 대부분 시스템은 답이 없고, 그건 실제로는 불가능한 행을 갖고 있으면서 서빙한다는 뜻이야.
Hint
두 번째 경우가 사람들이 설계를 빼먹었다가 압박 속에서 발견하는 그거야. 상류가 과거 값을 개정하면 개정본만 원해, 아니면 쌍을 원해? 거시 통계에서는 쌍이 진짜 값어치가 있어. '그때 발표된 게 뭐였나' 가 그때 내려진 결정들을 설명하거든.

Progress

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

댓글 0

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

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