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

기록자가 하나면 경합이 일어날 자리도 하나야

~12 min · concurrency, architecture, records, design

Level 0젖은 흙
0 XP0/36 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete

병렬 작업의 고통 둘

공유된 작업물에 세션을 여럿 동시에 돌리면 문제 둘이 바로 와. 로그 경합이랑 커밋 충돌. 같은 문제처럼 느껴져. 행위자 둘, 자원 하나. 그런데 답이 완전히 달라. 이 강의는 첫 번째고, 다음 트랙이 두 번째야.

로그 경합엔 깔끔한 구조적 답이 있고, 그 답은 잠금이 아냐. 프로세스 하나만 기록자로 만들어. 모든 세션, 모든 명령줄 도구, 모든 사용자 인터페이스가 그 프로세스한테 써달라고 부탁하는 클라이언트야. 세션끼리는 이제 안 다퉈. 근데 그걸 실제로 해주는 게 뭔지 봐. 프로세스 개수가 아냐. 단일 진입점은 분산 경합을 없애고, 기록자 안에서는 동시 요청이 여전히 스레드 풀로 들어와서 잠금 하나 뒤에서 직렬화돼. 클라이언트마다 맞아야 하던 게, 직렬화가 맞아야 하는 자리 하나로 줄어든 거야. 그게 산 거고.

이게 잠금보다 나은 이유

잠금은 공학 의상을 입은 규율이야. 모든 기록자한테 제대로 획득하고, 제대로 해제하고, 쥔 채로 죽는 경우까지 처리하라고 부탁해. 그리고 하나가 잊거나, 나중에 추가된 경로 하나가 그 관례를 모르면 조용히 깨져. 실패 양상은 멀쩡해 보이는 뒤엉킨 쓰기고.

단일 기록자는 질문 자체를 없애. 새 클라이언트가 어길 관례가 없어. 클라이언트는 아예 쓸 수가 없으니까. API 가 유일한 문이고, 문은 규칙보다 훨씬 올바르게 유지하기 쉬워.

원래 같으면 고약했을 분산 문제 한 부류도 같이 사라져. 작업이 여행 중인 노트북에서도 집의 워크스테이션에서도 벌어질 때, 공유 파일은 진짜 어려운 동기화 문제고 공유 API 종단점은 아예 문제가 아냐. 여행 중인 기계의 도구는 API 클라이언트니까 나머지랑 똑같은 단일 기록자한테 기록하고, 누구 사본이 더 새 거냐는 질문이 아예 안 생겨.

클라이언트한테 걸리는 제약

따라 나오는 명제를 적어둘 값어치가 있어. 실수로 어겨지거든. 엔진이 유일한 기록자면 클라이언트는 절대 쓰면 안 돼. 저장소에도, 로그 파일에도, "이관 때문에 딱 한 번"도 안 돼. 뭘 빨리 고치려고 자기 API 를 우회하는 명령줄 도구는 이 설계 전체가 막으려던 두 번째 기록자를 도로 들여온 거고, 여섯 달 뒤 데이터가 어긋날 때 아무도 기억 못 하는 쓰기가 바로 그거야.

기분 좋은 부수 효과도 있어. 클라이언트가 작아진 채로 남아. 데이터베이스 드라이버도, 스키마 지식도, 이관 사연도 없어. 출력을 예쁘게 찍는 HTTP 클라이언트고, 그 말은 설치 없이 아무 데서나 돌고 아무것도 못 망가뜨린다는 뜻이야.

다툼을 중재하기보다 없애는 쪽을 골라. 잠금이랑 트랜잭션이랑 재시도는 기록자마다 중재하게 만들고, 단일 기록자는 중재해야 하는 자리를 딱 하나만 남겨. 먼저 물어야 할 설계 질문은 "이 기록자 둘이 어떻게 협조하지"가 아니라 "왜 기록자가 둘이지"야. 두 번째는 작업이 벌어지는 자리에서 첫 번째한테 손이 안 닿아서 생긴 경우가 많거든.

Code

아키텍처 둘, 그중 하나는 경합할 수가 없어·text
CONTENDING - every actor writes the store

   session A ---\
   session B -----> [ log store ] <---- interface
   session C ---/         ^
   traveling  ----------/

   needs: a locking convention every writer honors, forever,
          including paths added next year by someone who has
          not read this diagram.
   fails: silently, as interleaved writes that look fine.


SINGLE WRITER - one process, everything else is a client

   session A ---\
   session B -----> [ engine ] --> [ log store ]
   session C ---/       ^
   traveling  --------/

   needs: one lock, in one place. clients cannot write, so
          no convention exists to violate and no new path can break it.
   bonus: the traveling machine is not a special case - it is
          the same client over the same API.


THE COROLLARY, WHICH GETS VIOLATED BY ACCIDENT
   the client must NEVER touch the store directly. not for a
   migration, not for a quick fix, not once. that write is the
   second writer, and it is the one nobody remembers when the
   data disagrees six months later.

External links

Exercise

제일 중요한 데이터 저장소의 기록자 수를 세어봐. 이관 스크립트, 관리 콘솔, 일회성 수리 스크립트, 그리고 어딘가 설정 파일에 자격 증명을 담은 것까지 전부 넣어야 해. 주 응용 프로그램이 아닌 기록자마다 왜 존재하는지 적어. 대부분은 주 경로가 뭘 노출 안 해서 존재하는 걸로 드러나고, 그 빠진 종단점이 진짜 처방이야.
Hint
수리 스크립트가 제일 흔한 두 번째 기록자고 없애기 제일 어려워. API 가 일부러 지원 안 하는 상황을 위해 존재하니까. 쓸모 있는 수는 대개 조용히 쓰는 스크립트를 남겨두는 대신, 명시적으로 이름 붙고 기록되고 호출하기 번거로운 관리 작업을 API 에 주는 거야.

Progress

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

댓글 0

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

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