실제 운영 환경에서의 시스템은 어떤것이든지 잘못될 가능성이 있다면 잘못된다
단일 컴퓨터에서 실행되는 소프트웨어를 믿지 못할 근본적인 이유는 없다. 하드웨어에 이상이 없다면 같은 연산은 항상 같은 결과를 낸다
네트워크로 연결된 여러 컴퓨터에서 동작하는 환경은 다르다. 분산 시스템에선 이상화된 형태로 동작하지 않는다.
시스템의 어떤 부분은 잘 동작하지만 다른 부분은 예측할수 없는 방식으로 고장나는 경우도 있다.
이를 부분 장애라 하고, 이는 비결정적이라 예측하기 어렵다
신뢰성 없는 네트워크
비공유 분산 시스템에서, 장비들이 통신하는 유일한 수단은 네트워크이다.
공유 아키텍처로 설계할수도 있겠지만, 특별한 하드웨어가 필요치 않아 상대적으로 저렴하고, 클라우드상에서 작동하며, 지리적으로 분산 배치 가능한 점등의 장점들 때문에 주로 비공유 분산 시스템을 사용한다.
대부분의 통신은 비동기 패킷 네트워크인데, 이는 다른 노드로 패킷을 보낼 수 있지만 언제 도착할지, 도착하는것이 확실한지는 보장하지 않는다.
요청을 보내고 응답을 기다릴 때 이 패킷은 잘못될 수 있다.
전송 층은 패킷이 전송됐는지 아닌지 구별이 힘들고, 유일한 수단은 수신 측에서 응답 메시지를 보내는 것이지만 이 또한 유실되거나 지연될수 있다.
이런 문제는 비동기 네트워크에서 구별할수 없고, 이런 문제를 다루는 가장 흔한 방법은 타임아웃이다
결함 감지
분산 시스템은 결함 있는 노드를 자동으로 감지할수 있어야 한다.
네트워크의 불확실성 때문에 노드가 동작 중인지 아닌지 구별하기 어렵다.
타임아웃이 결함을 감지하는 수단이지만, 확실하게 구분하려면 타임아웃은 얼마나 길어야 할까?
이에 대한 간단한 답은 없다.
타임아웃이 길면 노드가 죽었다고 판단될때까지 기다리는 시간이 길어지고, 짧으면 결함은 빨리 발견하지만 노드가 살아있음에도 죽었다고 잘못 판단할 위험이 높아진다.
성급하게 노드가 죽었다고 판단하면, 해당 노드가 실행중인 작업이 중복 실행 될수 있으므로 문제가 된다.
노드가 실제로는 죽지 않았고 과부하 때문에 응답이 느린데, 이 부하를 다른 노드가 이어받게 되면 연쇄 장애를 유발할 수 있다.
동기 네트워크 대 비동기 네트워크
패킷 전송 지연 시간 최대치가 고정돼 있고 패킷이 유실되지 않는 네트워크라면 분산 시스템은 간단해진다.
왜 이 문제를 하드웨어 수준에서 해결하고 네트워크를 신뢰성 있게 만들수 없을까?
고정 회선 전화 네트워크는 극단적인 신뢰성을 지닌다.
전화 통화는 종단간 지연 시간이 낮아야 하며 대역폭이 충분해야 한다.
전화 네트워크에서는 전용 회선이 만들어 진다. 이는 통화가 끝날때까지 유지된다.
이런 네트워크는 동기식이다. 데이터가 여러 라우터를 거치더라도 큐 대기 문제를 겪지 않는다.
큐 대기가 없으므로 네트워크 종단 지연 시간 최대치가 고장돼 있다. 이를 제한있는지연 이라 한다.
TCP 는 위와는 상황이 다르다. 전화 네트워크 회선은 만들어진 동안 다른 누구도 사용할 수 없는 고정된 대역폭이지만, TCP 패킷은 가용 네트워크 대역폭을 기회적으로 사용한다.
TCP 는 가변 길이의 데이터를 보내면 가능한 짧은 시간 안에 전송하기 위해 대역폭을 많이 쓰고, 연결이 유휴 상태에 있는 동안에는 대역폭을 사용하지 않는다.
데이터센터 네트워크, 인터넷은 특성상 순간적으로 몰리는 트래픽에 최적화 하기 위해 이런 방식을 사용한다.
신뢰성 없는 시계
시계와 시간은 중요하다. 애플리케이션은 다양한 방식으로 시계에 의존한다
- 요청이 타임아웃됐나?
- 서비스의 99분의 응답 시간은?
- 서비스가 지난 5분동안의 초당 질의 처리수는?
- 사용자가 서비스에서 시간을 얼마나 보냈나?
등등 여러 방식이 있다.
분산 시스템에서는 통신이 즉각적이지 않으므로 시간을 다루기 까다롭다.
메시지가 네트워크를 거쳐 장비들로 전달되는데 시간이 걸리고, 네트워크 지연 변동성 때문에 어떤 일이 발생한 순서를 알아내기 어렵게 한다.
게다가 네트워크에 있는 장비들은 각각 자신의 시계를 따로 갖고 있다.
이는 완벽히 정확하지 않아 장비마다 시계는 약간 빠를수도 느릴수도 있다.
시간을 어느정도 동기화 할수 있는데, 가장 널리 쓰이는 방법은 네트워크 시간 프로토콜 (NTP) 이다.
https://en.wikipedia.org/wiki/Network_Time_Protocol
[Network Time Protocol - Wikipedia
From Wikipedia, the free encyclopedia Standard protocol for synchronizing time across devices Network Time ProtocolInternational standardRFC 5905Developed byDavid L. Mills, Network Time FoundationIntroduced1985 (1985) The Network Time Protocol (NTP) is a
en.wikipedia.org](https://en.wikipedia.org/wiki/Network_Time_Protocol)
견고한 소프트웨어는 잘못된 시계에 대비할 필요가 있다.
하지만 시계가 잘못된 것을 눈치채는건 쉽지 않다.
장비의 수정 시계에 결함이 있거나, NTP 클라이언트 설정이 잘못 됐다면 시계는 점점 실제 시간에서 멀어지지만 잘 동작하는 것으로 보인다.
단조 시계 대 일 기준 시계
현대 컴퓨터는 일기준 시계, 단조 시계 최소 두가지 종류의 시계를 갖고 있다.
일 기준 시계
어떤 달력에 따라 현재 날짜와 시간을 반환한다. ex) java System.currentTimeMillis()
일 기준 시계는 보통 NTP 로 동기화 된다.
이상적으로 한 장비의 타임스탬프는 다른 장비의 타임스탬프와 동일한 의미를 지닌다는 뜻이다.
단조 시계
타임아웃이나 서비스 응답시간 같은 지속시간을 재는데 적합하다. ex) java System.nanoTime().
여러 개의 CPU 소켓이 있는 서버는 CPU 마다 독립된 타이머가 있을 수도 있다. 이 타이머는 다른 CPU 와 반드시 동기화 되지는 않는다.
분산 시스템에서 경과 시간을 재는 데 단조 시계를 쓰는 것은 일반적으로 괜찮다.
이벤트 순서화용 타임스탬프
여러 클라이언트가 분산 데이터베이스에 쓰기 요청을 하면 누가 먼저 쓰게 될까?
위 그림은 일 기준 시간을 위험하게 사용하는 예이다.
클라이언트 A가 노드1에 x=1 을 쓰고, 이 쓰기는 노드3으로 복제된다.
클라이언트 B가 노드3에 있는 x를 증가시킨다.
이후 두 쓰기는 노드2로 복제된다.
쓰기가 다른 노드로 복제될때 쓰기가 발생한 노드의 일 기준 시계에 따른 타임스탬프가 붙는다.
노드1과 3의 시간 차이는 3밀리초 미만으로 매우 작지만, 위 예시에서는 x=2 가 나중에 쓰여졌지만, 타임스탬프는 x=1 이 더 뒤의 시간이기 때문에 B 의 증가 연산은 손실된다.
이런 충돌 해소 전략을 최종 쓰기 승리 라 부른다.
타임스탬프를 클라이언트에서 생성하는 방법도 있지만, 근본적인 문제를 바꾸진 못한다.
따라서 가장 최근 값을 유지하고 다른 것들을 버림으로써 충돌을 해소하려 하더라도, 최근 시간의 정의는 로컬 일 기준 시계에 의존하며 그 시계는 틀릴수도 있다는 것을 주의해야 한다.
전역 스냅샷용 동기화 시계
이전에 스냅샷 격리에 대해 설명했는데, 가장 흔한 스냅샷 격리 구현은 단조 증가하는 트랜잭션ID 를 이용하는 것이다.
데이터베이스가 여러 데이터센터 장비에 분산돼 있을때는 전역 단조 증가 트랜잭션 ID 를 생성하기 ㅇ러ㅕㅂ다.
작고 빠른 트랜잭션이 많으면 분산 시스템에서 트랜잭션 ID 생성은 병목지점이 된다.
동기화된 일 기준 시계를 사용할순 없을까? 문제는 시계 정확도에 대한 불확실성이다.
이를 보완하기 위해, 가장 이른 타임스탬프와 가장 늦은 타임스탬프를 포함하는 신뢰 구간 개념을 사용한다.
A = [Aearliest, Alatest], B = [Bearliest, Blatest]
라는 두 구간이 있을때 두 구간이 겹치지 않는다면 (Aearlest < Alatest < Bearliest < Blatest)
A는 분명히 B보다 먼저 실행됐단걸 알수 있다.
트랜잭션 타임스탬프가 인과성을 반영하는 것을 보장하기 위해 트랜잭션 커밋 전에 의도적으로 신뢰구간 길이만큼 기다린다.
프로세스 중단
파티션마다 리더가 하나씩 있는 데이터베이스가 있고 리더만 쓰기가 가능하다고 가정하자.
리더가 여전히 리더인지, 안전하게 쓰기를 받을수 있을지 어떻게 알수 있을까?
한가지 방법은 리더가 다른 노드들로부터 임차권 을 얻는 것이다.
특정 시점에 오직 하나의 리더만 임차권을 얻을수 있고, 따라서 임차권을 획득하면 만료될 때까지 자신이 리더인 것을 알수 있다.
계속 리더로 남아 있으려면 임차권이 만료되기 전에 주기적으로 갱신해야 한다.
노드에 장애가 나면 임차권 갱신을 멈추므로 임차권이 만료될 때 다른 노드가 리더 역할을 넘겨받을 수 있다.
이런 방식의 문제점은 무엇일까?
임차권에 대한 시간 처리를 동기화된 시계에 의존하면 문제가 된다.
임차권 만료 시간이 다른 장비에서 설정 됐는데, 로컬 시계와 비교하므로 시계 동기화가 깨졌을때 문제가 된다.
단조 시계를 사용하도록 하더라도, 임차권을 확인하는 시점과 요청을 처리하는 시간 사이에 예상치 못한 중단이 있다면 임차권 관리에 문제가 생긴다.
다른 장비에서 다중 스레드 코드를 작성할땐 좋은 도구들 (뮤텍스, 세마포어, 원자적 카운터, 락프리 주료구조, 블로킹 큐 등..) 이 있지만,
분산 시스템용으로 바로 사용할수는 없다.
분산 시스템의 노드에서의 실행은 어느 시점에 상당기간 멈출 수 있다고 가정해야 한다
응답 시간 보장
많은 프로그래밍 언어와 운영체제에서 작업은 중단될 수 있다.
만약 소프트웨어가 명시된 시간 안에 응답이 실패했을때 심각한 손상을 유발한다면, 이런 시스템에서는 소프트웨어가 응답해야 하는 데드라인이 명시된다.
이를 만족하지 못하면 전체 시스템 장애를 유발할 수 있다.
하지만 이를 구현하려면 많은 양의 부가 작업이 필요하고, 사용할수 있는 기술이 엄격히 제한된다.
대부분 소프트웨어는 실시간 보장은 전혀 경제적이지도 적절하지도 않다.
비잔틴 결함
분산 시스템 문제에서 노드가 거짓말을 할지도 모른다는 위험이 있다면 훨씬 더 어려워진다.
이를 비잔틴 결함 이라고 하며 이런 신뢰할 수 없는 환경에서 합의에 도달하는 문제를 비잔틴 장군 문제 라 한다.
웹 애플리케이션은 사용자가 입력하는 모든 값을 임의적이고 악의적이라고 예상해야 한다.
'책 정리와 리뷰' 카테고리의 다른 글
[데이터중심 어플리케이션 설계] 10장 정리 (1) | 2023.10.09 |
---|---|
[데이터중심 어플리케이션 설계] 9장 정리 (0) | 2023.10.03 |
[도둑맞은 집중력] 책 리뷰 (1) | 2023.09.10 |
[데이터중심 어플리케이션 설계] 7장 정리 (0) | 2023.08.15 |
[돈의 속성] 책 리뷰 (2) | 2023.08.08 |