Foundry
키-값 저장소 설계
고급
핵심

잠깐 죽은 사본 다루기

두 대가 죽어도 쓰기를 받으려면

300대 규모에서 노드는 늘 죽고 살아납니다. 배포, 재시작, 하드웨어 교체, 순간적인 네트워크 끊김이 모두 여기 해당합니다. 잠깐 죽는 것이 정상 상태입니다.

쓰기 성공 수를 못 채우는 순간

쓰기 성공 수를 둘로 정했으므로 사본 하나가 죽어도 괜찮습니다. 그런데 두 대가 겹쳐 죽으면 둘을 채울 수 없습니다.

요구사항은 쓰기를 거절하지 말라고 했습니다. 그러면 방법은 하나입니다. 원래 사본이 아닌 노드에 대신 받아 두는 것입니다.

사본 하나가 잠깐 죽으면 다른 노드가 대신 받아 두었다가 돌아올 때 넘긴다 사본 셋 중 하나가 잠깐 죽었다 사본 1 사본 2 사본 3 셋 중 둘만 받는다 쓰기는 계속 성공한다 그동안 대신 받아 둘 곳을 하나 더 쓴다 대신 노드 사본 3 몫을 표시해 보관 자기 데이터로 섞지 않는다 돌아오면 넘긴다 덕분에 두 대가 죽어도 쓰기를 받을 수 있다 대신 그 쓰기는 정족수가 약속한 사본에 아직 없다

대신 받은 노드는 그 데이터를 자기 것과 섞지 않습니다. "이것은 사본 3의 몫" 이라고 표시해 따로 보관하고, 사본 3이 돌아오면 넘기고 자기 쪽에서 지웁니다.

얻는 것과 잃는 것

항목내용
얻는 것두 대가 죽어도 쓰기를 계속 받는다
잃는 것그 쓰기는 정족수가 약속한 사본에 아직 없다

이것이 정족수의 약속을 느슨하게 만듭니다. 읽기 둘을 받아도 그 둘이 원래 사본이면 대신 받아 둔 값을 못 봅니다. 즉 방금 성공한 쓰기가 읽기에서 안 보일 수 있습니다.

요구사항이 쓰기 가용성을 우선했으므로 이 대가를 받아들입니다. 무엇을 우선했는지가 이 판단의 근거입니다.

대신 받아 두는 것의 한계

이 방법은 잠깐 죽는 경우에만 통합니다.

죽어 있는 시간결과
몇 분대신 받아 둔 것을 넘기면 끝난다
몇 시간대신 받아 둔 양이 그 노드 메모리와 디스크를 압박한다
영구히넘길 곳이 없다. 다른 방법이 필요하다

그래서 보관에 상한을 둡니다. 상한을 넘으면 대신 받기를 멈추고, 그 사본은 영구 장애로 다루는 쪽으로 넘깁니다. 다음 단계가 그 이야기입니다.

돌아온 노드가 위험한 순간

죽었다 돌아온 노드는 낡은 데이터를 들고 있습니다. 그 상태로 읽기를 받으면 옛 값을 최신으로 내놓습니다.

돌아오자마자 읽기를 받으면 낡은 값을 준다
넘겨받기가 끝날 때까지 읽기에서 빼 두는 편이 낫다

읽기 정족수가 둘이므로 낡은 사본 하나가 섞여도 다른 하나가 최신을 줍니다. 그래도 응답 두 개를 비교해 낡은 쪽을 걸러내는 장치가 있어야 이 보호가 작동합니다.

두 죽음을 구분해야 한다

잠깐 죽은 것과 영구히 죽은 것의 대응이 정반대입니다.

판단대응
잠깐데이터를 그대로 두고 기다린다
영구그 몫을 다른 노드에 새로 채운다

성급히 영구로 판단하면 수백 기가를 헛되게 복사하고, 늦게 판단하면 사본 수가 부족한 상태가 오래 이어집니다. 그래서 판단에 시간 기준을 두고, 그 기준을 대신 받아 두는 상한과 맞춥니다.

면접에서 이렇게 나옵니다

Q.사본 두 대가 겹쳐 죽으면 쓰기를 어떻게 받나요

원래 사본이 아닌 노드에 대신 받아 둡니다.

쓰기 성공 수가 둘인데 사본 두 대가 죽으면 둘을 채울 수 없습니다. 요구사항이 쓰기를 거절하지 말라고 했으므로 다른 노드가 대신 받습니다.

대신 받은 노드는 그 데이터를 자기 것과 섞지 않는다
"사본 3의 몫" 이라고 표시해 따로 보관한다
사본 3이 돌아오면 넘기고 자기 쪽에서 지운다

잃는 것도 말해야 합니다. 그 쓰기는 정족수가 약속한 사본에 아직 없으므로, 읽기 둘이 원래 사본이면 방금 성공한 쓰기가 안 보일 수 있습니다.

흔한 실수: 대신 받은 데이터를 그 노드의 정상 데이터로 섞는 것. 그러면 그 키의 담당이 아닌 노드에 값이 남아 나중에 누가 지워야 하는지 알 수 없게 됩니다.

Q.대신 받아 두는 방법의 한계는 무엇인가요

잠깐 죽는 경우에만 통합니다.

죽어 있는 시간결과
몇 분넘기면 끝난다
몇 시간보관량이 그 노드를 압박한다
영구히넘길 곳이 없다

쓰기가 계속 들어오는 시스템에서 보관량은 시간에 비례해 늘어납니다. 그래서 보관에 상한을 두고, 상한을 넘으면 대신 받기를 멈추고 그 사본을 영구 장애로 다룹니다.

흔한 실수: 상한을 두지 않는 것. 한 노드가 오래 죽어 있으면 대신 받던 노드가 그 무게로 함께 무너집니다. 장애 하나가 둘로 번지는 경로가 이렇게 만들어집니다.

Q.죽었다 돌아온 노드를 곧바로 쓰면 안 되나요

낡은 데이터를 들고 있으므로 옛 값을 최신으로 내놓습니다.

돌아오자마자 읽기를 받으면 낡은 값을 준다
넘겨받기가 끝날 때까지 읽기에서 빼 두는 편이 낫다

읽기 정족수가 둘이므로 낡은 사본 하나가 섞여도 다른 하나가 최신을 줍니다. 다만 그 보호가 작동하려면 응답 두 개를 비교해 낡은 쪽을 걸러내는 장치가 있어야 합니다.

비교 없이 먼저 온 응답을 쓰면 낡은 값이 그대로 사용자에게 갑니다.

흔한 실수: 노드가 살아났다는 신호만으로 트래픽을 넣는 것. 프로세스가 떴다는 것과 데이터가 최신이라는 것은 다른 상태입니다. 두 상태를 구분해 표시해야 합니다.

Q.잠깐 죽은 것과 영구히 죽은 것을 왜 구분하나요

대응이 정반대이기 때문입니다.

판단대응
잠깐데이터를 그대로 두고 기다린다
영구그 몫을 다른 노드에 새로 채운다

성급히 영구로 판단하면 수백 기가를 헛되게 복사합니다. 그 복사가 네트워크와 디스크를 먹어 다른 요청의 지연을 밀어 올립니다.

늦게 판단하면 사본 수가 부족한 상태가 오래 이어져 그 사이 한 대가 더 죽으면 데이터를 잃습니다.

그래서 시간 기준을 두고, 그 기준을 대신 받아 두는 상한과 맞춥니다.

흔한 실수: 두 판단을 하나의 감지 장치로 처리하는 것. 감지는 "응답이 없다" 만 알려 주고 얼마나 오래 없었는지가 판단의 근거입니다. 그 시간을 정하지 않으면 두 대응 중 어느 것도 제때 못 합니다.

먼저 스스로 답해보고 아래 답변과 견줘보세요. 막히는 부분은 문제로 확인할 수 있어요.

읽었으면 문제로 확인해보세요

키-값 저장소 설계 문제를 풀면 틀린 문제가 자동으로 노트에 쌓입니다. 가입 없이 5문제를 먼저 풀어볼 수도 있어요.