이제부터 잘못될 가능성이 있다 -> 잘못된다로 인식하자(충분히 합리적).
우리는 시스템이 잘못되더라도 사용자의 기대하는 보장을 만족하는 시스템을 구축할 것이다.
이번장은 분산 시스템에 어떤 문제가 있고 어느 정도로 회피가 가능한지 알아보며,
분산 시스템의 상태에 대해 생각하는 방법과 무슨일이 일어났는지 추론하는 방법을 알아본다.
결함과 부분 장애
단일 컴퓨터의 소프트웨어는 결정적(동작하거나 동작하지 않거나)이다.(종종 버그가 재부팅되면서 해결되는 경우가 있지만 그건 동작하지 않는 것이다.)
소프트웨어가 결정적인 이유는 원하지 않는 결과를 내느니 동작하지 않길 원하기 때문이다.(사이드 이펙트는 최악)
단일 컴퓨터에서 우리는 이상적으로 동작하길 바라고, 실제로 유사하게 동작한다.
그러나 분산 시스템에서는 비결정적이다. 어떤 노드가 동작할때도 있고 동작하지 않을때도(부분 장애) 있어서 전체 시스템은 애매한 상태에 놓이게 된다.
클라우드 컴퓨팅과 슈퍼 컴퓨팅
대규모 컴퓨팅 시스템 구축 철학은 몇가지가 있다
슈퍼컴퓨팅(HPC): 여러 CPU를 가지고 있어 대규모 계산 작업을 하는 경우
클라우드 컴퓨팅: 멀티테넌트 데이터센터 or 이더넷 상용 컴퓨터 등등
전통적인 기업 데이터센터는 이 둘의 중간지점
슈퍼컴퓨터의 경우 체크포인트 저장 후 단일 노드의 장애 발생 시 클러스터를 중지시켜 노드 복구 후 체크포인트부터 재실행한다.(단일 컴퓨터와 유사하게 동작)
그러나 우리가 원하는 분산 시스템은 슈퍼컴퓨터와는 다른 특징을 가진다.
온라인 애플리케이션을 운영할때 노드 전체를 중단하는건 말도 안됨
슈퍼컴퓨터와 다르게 클라우드는 상용 하드웨어를 사용 -> 싼 대신 장애가 잦다
네트워크 토폴로지도 다르다 -> 클라우드 컴퓨팅이 훨씬 안좋다..
규모가 커질수록 장애가 발생한 노드가 있을 확률은 증가한다. 이 상황에 전체 중단 시 영원히 복구만 할 수도 있다
슈퍼컴퓨터는 지역적으로 가까이 있지만 클라우드 데이터센터는 여러곳에서 배포를 한다(IDC 이중화)
그럼에도 클라우드를 사용하는 이유는 장애가 발생하더라도 서비스는 중단되지 않는다는 것이다. 롤링업데이트나 새로운 장비로 대체하는 등 유연하게 처리할 수 있다
결국 우리는 신뢰할 수 없는(언제든 장애가 발생하는) 시스템을 신뢰성 있게 구축하는 것을 해야한다.
신뢰성 없는 네트워크
우리가 다루는 분산 시스템은 HPC와 달리 비공유 네트워크를 사용한다. 즉 다른 장비의 메모리나 디스크에 접근이 불가능하다.
이런 비공유 네트워크를 사용하는 이유는 다음과 같다.
특별한 네트워크 하드웨어 불필요해 싸다
상품화된 클라우드 서비스를 활용 가능
지리적으로 분산되어도 데이터센터를 중복 배치 가능
이런 시스템에서는 보통 비동기 패킷 네트워크(asynchronous packet network)를 사용한다.
노드는 다른 노드로 패킷을 보낼 수 있으나, 이는 패킷의 전송 시간과 결과를 보장하지 않는다.
즉 다음과 같은 문제들이 발생할 수 있다.
요청이 손실됐을 수 있다
요청이 큐에서 대기하다 나중에 손전송될 수 있다(네트워크나 수신자에 과부화가 걸려 지연)
원격 노드에 장애가 생겼을 수 있다
원격 노드가 요청을 처리 후 응답이 손실됐을 수 있다
원격 노드가 요청을 처리했지만 응답이 지연되다가 나중에 전송될 수 있다
응답을 받지 못했을 경우 클라이언트 측은 알 수 없다. 타임아웃을 이용해 판단은 할 수 있지만 어떤 문제가 발생한 것인지 모른다.
현실의 네트워크 결함
실제로 네트워크 장비를 중복 설정하고 하더라도 스위치 설정 오류 등의 인적 장애를 해결하지 못하기 때문에 신뢰성 있는 네트워크를 만드는 것은 불가능하다. 결국 우리는 일어날 수 있는 네트워크 결함을 대비해야한다는 것이다. (fault tolerance를 가져야함)
단지 에러를 보내는 것을 넘어 시스템이 복구될 수 있도록 해야한다.
결함감지
원격 노드가 요청을 처리하다 죽었다면 원격 노드에서 데이터가 얼마나 처리됐는지 알 수 있는 방법이 없다. 이런경우 다음과 같은 방법으로 결함을 감지할 수 있다.
노드 프로세스가 죽었지만(관리자가 죽였지만) 노드의 운영체제가 아직 실행중이라면 스크립트로 다른 노드에게 프로세스가 죽었다고 알려 다른 노드가 타임아웃이 만료되길 기다릴 필요없이 빠르게 역할을 넘겨받게함 (HBase)
데이터 센터 내 네트워크 스위치의 관리 인터페이스에 접근할 수 있으면 질의를 보내 하드웨어 수준의 링크 장애를 감지할 수 있다. (이는 접근이 가능하다는 가정이 없다면 무의미)
접속하려는 IP 주소에 도달할 수 없다고 라우터가 확신하면 ICMP Destination Unreachable 패킷으로 응답할 수 있다. 그러나 라우터가 이런 기능을 모두 지원하길 바랄 수 없다.
애플리케이션에게 2xx,3xx 응답을 받지 않으면 우린 TCP Ack 만으로 안심할 수 없다 결국 뭔가 잘못되면 응답을 받지 못할 것을 가정하고 타임아웃을 두어 처리해야한다.
타임아웃과 기약없는 지연
타임아웃은 길어도 문제 안길어도 문제.. 보통 어떤 시간 d 내에 전송되거나 손실되지만 결코 d보다 오래걸리지 않고 장애가 나지 않는 노드가 r만큼의 처리 시간을 보장한다고 할때 2d+r의 타임아웃을 두나 실제로 분산시스템은 기약없는 지연(unbounded delay)이 있어 서버 구현이 대부분 어떤 시간 내에 처리한다고 보장할 수 없다.
네트워크 혼잡과 큐 대기
여러 노드에서 보낸 패킷은 스위치의 큐에서 대기
코어가 바쁜상태라면 운영체제 큐에서대기
가상 장비의 경우 다른 가상 장비가 코어를 사용하는 동안 새로 들어오는 데이터를 큐에 넣어 보관(버퍼링)
TCP 흐름제어(congestion avoidance, backpressure)를 실행함 -> 추가적인 큐대기 가능
추가로 TCP는 재전송도 있음.. 리소스가 풍부하면 상관없으나 실제 클라우드는 엄청 많은 서비스가 동작하고 있고, 맵리듀스 같은 일괄 처리 작업부하 등으로 네트워크 링크를 포화시키기 쉽다.
결국 우리는 실험적으로 타임아웃을 조정할 수 밖에 없다. 더 유동적으로 변동성(jitter)를 측정하고 관찰된 응답시간 분포에 따라 타임아웃을 자동으로 조절하게 하는 방법도 있다. 파이 증가 장애 감지기를 쓰는 아카와 카산드라가 그 예이다.
동기 네트워크 대 비동기 네트워크
전화처럼 동기 네트워크를 사용하면 되지 않을까? 라는 생각을 한다면 오산이다. 전화의 경우 다음과 같은 특징을 갖는다.
통화를 하면 회선(circuit)이 만들어진다.
회선은 고정되고 보장된 양의 대역폭이 할당된다.
회선은 통화가 끊길때까지 유지된다 -> 즉 큐대기 등의 과정이 필요없다. 네트워크 리소스가 고정되어 할당되기 때문이다.
제한 있는 지연(bounded delay)이기 떄문에 신뢰성이 높다.
TCP의 경우 가용한 네트워크 대역폭을 기회주의적으로 사용한다. 이는 요청의 크기가 제각각인 다수의 통신을 가능하게 하기 위함이다(순간적으로 몰리는 트래픽 bursty traffic에 최적화돼있다). 리소스 사용률을 높이기 위함도 있따.
만약 특정 대역을 할당해서 사용한다면 얼만큼을 사용할지도 문제가 된다. 너무 적은 대역폭을 할당하면 리소스가 감당하지 못할 것이고,
너무 큰 리소스를 할당하면 리소스가 부족해 네트워크 리소스를 할당받는 시간만으로 기존의 대기시간을 넘어버릴 것이다.(애초에 IP와 이더넷은 패킷교환 프로토콜이라 네트워크에 기약없는 지연이 있다. 프로토콜부터 바꿔야함)
ATM, InfiniBand 등의 하이브리드 시도도 있었다.
서비스 품질(Quality of Service, 패킷에 우선순위를 매기고 스케줄링함), 진입제어(admission control, 전송측에서 전송률 제한)를 잘쓰면 패킷 네트워크에서 회선 교환을 흉내 내거나 통계적으로 제한 있는 지연을 제공하는 것이 가능하다.
문제는 이런 통신은 인터넷과 이더넷에서 사용할 수 없다..ㅠ 결국 실험적으로..
신뢰성 없는 시계
애플리케이션은 다양한 방식으로 시계에 의존한다. 대표적으로 지속 시간과 시점을 측정하는데,
분산 시스템에서는 통신이 즉각적이지 않기 때문에 시간을 다루기 까다롭다.
네트워크 지연으로 메세지를 받은 시간은 항상 보낸 시간보다 나중이지만 얼마나 나중일지 알 수 없다.
실제 개별 장비는 자신만의 수정 발진기(quartz crystal oscillator)를 사용하는데 이는 정확하지 않아 빠를수도 느릴수도 있다.
시간을 어느 정도 동기화하기 위해 네트워크 시간 프로토콜(NTP)등의 방식을 사용하는데, 서버 그룹에서 보고한 시간에 따라 컴퓨터 시계를 조정할 수 있게 하는 것이다. 그리고 서버들은 다시 GPS 수신자 같은 더욱 정확한 시간 출처로부터 시간을 얻는다.
단조 시계 대 일 기준 시계
현재 컴퓨터는 최소 두가지 이상의 시계를 가진다(각각 사용 목적이 다름)
일 기준 시계(time-of-day clock)
직관적인 시계에 기대하는 일을 한다.
어떤 달력에 따라 현재 날짜와 시간을 반환한다. (벽시계 시간 이라고도 함)
자바의 System.currentTimeMillis()는 epoch 이래로 흐른 초(또는 밀리초) 수를 반환한다.
일 기준 시계는 보통 NTP로 동기화된다. 한 장비의 타임스탬프는 (이상적으로) 다른 장비의 타임스탬프와 동일한 의미를 지닌다.
로컬 시계가 NTP 서버보다 너무 앞서면 강제로 리셋되어 과거 시점으로 설정된다.
이는 경과 시간을 측정하는데 적합하지 않도록 한다.
역사적으로 매우 거친(coarse-grained) 해상도를 가진다.(단위가 크다) -> 현재 문제되지 않음
단조 시계(monotonic clock)
타임아웃이나 서비스 응답 시간 같은 지속 시간(시간 구간)을 재는 데 적합
자바 System.nanoTime()이 이에 해당
단조 시계란 이름은 항상 앞으로 흐른다는 의미
두 값 사이의 차이로 두번의 확인 사이에 시간이 얼마나 흘렀는지 쟤는데 유용하다
이는 절대 시간보다는 상대시간이 중요하다는 것을 의미(서로 다른 컴퓨터의 단조시간을 비교하는건 무의미)
NTP는 단조시계가 NTP 서버 시간과 차이가 크면 진행하는 진도수를 조절 할 수 있다(시계를 돌린다slewing고 한다.)
0.05% 올리거나 내리는게 허용되지만 시각을 뛰게 할 수는 없다.
해상도는 매우 좋다 마이크로초나 그 이하 단위로 측정 가능하다
분산 시스템에서 경과 시간을 재는데 단조시계를 쓰는 것은 일반적으로 괜찮다 (다른 노드와 동기화될 필요 없으니)
시계 동기화와 정확도
일 기준 시계는 NTP서버와 동기화 돼야하는데 사실 다음과 같은 이유로 신뢰할만하지 않다
드리프트(시간이 빠르거나 늦어지는 것)가 발생한다. 온도차로도 발생
동기화가 많이 안되는 경우 컴퓨터 시간을 리셋한다 -> 이는 타임워프한 것처럼 보이게 한다
뜻하지 않게 노드와 NTP 서버 사이 방화벽으로 박히면 안좋은 설정이 유지될 수 있다 (실제 사례 존재)
동기화가 잘되더라도 네트워크가 혼잡하면 무쓸모..
NTP 서버가 이상하면 다른 컴퓨터에 모두 영향이 간다
윤초가 발생하면 1분이 59~61초가 되어 윤초를 고려하지 않고 설계된 시스템에서 시간에 대한 과정을 망친다. 이를 해결하기 위한 방법으로 윤초 조정을 하루에 걸처 NTP서버가 거짓말하게 하는 것이다. 문지름smearing 이라고 부른다
가상장비 공유 시 다른 애플리케이션은 중단된 상태라 미래로 뛴 듯 보인다.
완전히 제어할 수 없는 장치(모바일 또는 임베디드 장치) -> 사용자가 고의로 바꿀 수 있음
돈이 많으면 어느정도 해결할 수 있는 방법(좋은 장비와 프로토콜)이 있으나 완전히 해결하진 못한다.
동기화된 시계에 의존하기
우리는 이런 시간 결함을 고려하여 시스템을 설계해야한다.
보통 수정시계가 잘못되거나 NTP클라이언트 설정이 잘못된 경우 빠르게 눈치채지 못할 가능성이 높다.
따라서 동기화된 시계가 필요한 소프트웨어를 사용한다면 필수적으로 모든 장비 사이의 시계차이를 조심스럽게 모니터링 해야한다.
이벤트 순서화용 타임스탬프
카산드라에서 다음과 같은 상황이 발생할 수 있다.
여기서 Node2는 set x=1을 나중으로 인식하여 x+=1 연산을 버리고 만다.
이 충돌 해소 전략을 최종 쓰기 승리(last write wins, LWW)라고 불리며 다중 리더 복제와 카산드라 리악과 같은 리더 없는 데이터베이스에서 널리 사용된다. 타임스탬프를 클라이언트에서 구현할 수 있지만 근본적인 문제를 해결하진 못한다.
시계가 뒤처지는 노드는 시계가 빠른 노드가 먼저 쓴 내용을 그들 사이에 차이나는 시간이 흐를 때까지 덮어쓸 수 없다. 즉, 임의의 데이터가 쓰여지지 못해 유실될 수 있다.
LWW는 연속쓰기와 동시쓰기를 구분할 수 없다. 인과성 위반을 막으려면 버전 배ㅔㄱ터 같은 부가적인 인과성 추적 메커니즘이 필요하다
두 노드가 독립적으로 동일한 타임스탬프를 가진 쓰기 작업을 만들 수도 있다. 해상도가 낮을수록 더 심하다. 이런 충돌을 해소하려면 같은 값을 다르게 만들어줄 부가적인 값이 필요하지만 이는 또 인과성 위반을 낳을 수 있다.
우린 결국 일 기준 시계가 틀릴 수 있다는 것을 인지해야한다.
올바른 순서화를 위해서는 시계 출처가 측정하려고 하는 대상(즉 네트워크 지연)보다 훨씬 더 정확해야한다. 이른바 논리적 시계(logical clock)는 진동하는 수정 대신 증가하는 카운터를 기반으로 하며 이벤트 순서화의 안전한 대안이 된다. 논리적 시계는 일 기준 시간이나 경과한 초 수를 측정하지 않고 이벤트의 상대적인 순서만 측정한다.
시계 읽기는 신뢰 구간이 있다
아무리 마이크로초에서 나노초로 읽을 수 있는 시계가 있다해도 그 시계는 쉽게 드리프트되기 때문에 신뢰할 수 없다. 다만 우린 특정 신뢰하는 구간으로 시간을 읽을 수 있을 뿐이다. 시계에서 제공하는 오차 범위를 기반으로 계산하는 것이다(자세한 계산법은 생략)
그러나 실제 시스템은 이 불확실성을 노출하지 않는다. cloack_gettime()을 호출하더라도 오차가 몇인지는 알려주진 않으므로 신뢰구간이 5밀리초인지 5년인지 알 수 없다.
하지만 구글의 트루타임(TrueTime) 같은 API는 시계의 신뢰구간을 명시적으로 보고한다. API 요청 시 [earliest, latest]를 받는다.
전역 스냅숏용 동기화된 시계
스냅숏 격리에서 전역 단조 증가 트랜잭션ID가 필요하다. 그러나 분산 시스템에서 모든 파티션에 걸쳐 트랜잭션 ID를 생성하는 것이 쉽지 않다. 동기화된 일 기준 시계 타임스탬프를 기준으로 트랜잭션ID를 생성하는 것은 위험하다.(이미 많이 설명했다)
스패너는 트루타임 API의 신뢰구간을 비교함으로써 스냅숏 격리를 구현한다. 두 구간이 겹치지 않는다면 순서를 보장하는 것이다.
A = [A earliest, A latest] and B = [B earliest, B latest])
A earliest < A latest < B earliest < B latest
문제는 두 구간이 겹칠 시 순서를 보장할 수 없다는 것이다.
스패너는 읽기 쓰기 트랜잭션을 커밋하기 전에 의도적으로 신뢰구간만큼 기다림으로써 이를 해결한다.
너무 많은 지연이 되지 않기 위해서 구글은 각 데이터센터 내에 GPS 수신기나 원자 시계를 배치하여 약 7밀리초 이내로 동기화시킨다.
프로세스 중단
프로세스는 종종 다음과 같은 이유로 중단된다
GC의 stop-the-world (많이 해결됐지만 아예 없는건 아니다)
가상 장비의 서스펜드-재개 시
인적 실수 (사용자가 노트북 덮개를 덮었다 연 경우, SIGSTOP 신호 실수로 날린 경우 등)
운영체제 컨텍스트 스위칭
I/O 연산 대기
디스크 스왑 및 페이지 폴트
이 경우 실행 중인 쓰레드는 어떤 시점에 선점하고 얼마간의 시간이 흐른 후 재개할 수 있는데, 선점된 쓰레드는 이를 알지 못한다. 이런 경우 멈춘 시간 동안 불필요하게 변화가 발생할 수 있다 (리더의 선점권 잃음 등)
단일 장비에서는 프로세스 중단을 대비할 많은 방법이 있으나 분산 시스템은 이를 바로 적용할 수 없다.
응답 시간 보장
위의 프로세스 중단은 충분히 많은 노력을 들여 원인을 제거할 수는 있지만 이는 리얼타임 운영체제가 필요하고,
우리가 사용하는 시스템에서 원하지 않는 결과를 갖는다.
가비지 컬렉션의 영향을 제한하기
가비지 컬렉션으로 인해 stop-the-world 가 발생하기 전에 애플리케이션에 알려줘서 요청을 중지하거나 하는 방법이 있다.
Issue Brief
범위
분산 시스템의 골칫거리
이제부터 잘못될 가능성이 있다 -> 잘못된다로 인식하자(충분히 합리적). 우리는 시스템이 잘못되더라도 사용자의 기대하는 보장을 만족하는 시스템을 구축할 것이다.
이번장은 분산 시스템에 어떤 문제가 있고 어느 정도로 회피가 가능한지 알아보며, 분산 시스템의 상태에 대해 생각하는 방법과 무슨일이 일어났는지 추론하는 방법을 알아본다.
결함과 부분 장애
단일 컴퓨터의 소프트웨어는 결정적(동작하거나 동작하지 않거나)이다.(종종 버그가 재부팅되면서 해결되는 경우가 있지만 그건 동작하지 않는 것이다.) 소프트웨어가 결정적인 이유는 원하지 않는 결과를 내느니 동작하지 않길 원하기 때문이다.(사이드 이펙트는 최악) 단일 컴퓨터에서 우리는 이상적으로 동작하길 바라고, 실제로 유사하게 동작한다.
그러나 분산 시스템에서는 비결정적이다. 어떤 노드가 동작할때도 있고 동작하지 않을때도(부분 장애) 있어서 전체 시스템은 애매한 상태에 놓이게 된다.
클라우드 컴퓨팅과 슈퍼 컴퓨팅
대규모 컴퓨팅 시스템 구축 철학은 몇가지가 있다
슈퍼컴퓨터의 경우 체크포인트 저장 후 단일 노드의 장애 발생 시 클러스터를 중지시켜 노드 복구 후 체크포인트부터 재실행한다.(단일 컴퓨터와 유사하게 동작) 그러나 우리가 원하는 분산 시스템은 슈퍼컴퓨터와는 다른 특징을 가진다.
결국 우리는 신뢰할 수 없는(언제든 장애가 발생하는) 시스템을 신뢰성 있게 구축하는 것을 해야한다.
신뢰성 없는 네트워크
우리가 다루는 분산 시스템은 HPC와 달리 비공유 네트워크를 사용한다. 즉 다른 장비의 메모리나 디스크에 접근이 불가능하다. 이런 비공유 네트워크를 사용하는 이유는 다음과 같다.
이런 시스템에서는 보통 비동기 패킷 네트워크(asynchronous packet network)를 사용한다. 노드는 다른 노드로 패킷을 보낼 수 있으나, 이는 패킷의 전송 시간과 결과를 보장하지 않는다. 즉 다음과 같은 문제들이 발생할 수 있다.
응답을 받지 못했을 경우 클라이언트 측은 알 수 없다. 타임아웃을 이용해 판단은 할 수 있지만 어떤 문제가 발생한 것인지 모른다.
현실의 네트워크 결함
실제로 네트워크 장비를 중복 설정하고 하더라도 스위치 설정 오류 등의 인적 장애를 해결하지 못하기 때문에 신뢰성 있는 네트워크를 만드는 것은 불가능하다. 결국 우리는 일어날 수 있는 네트워크 결함을 대비해야한다는 것이다. (fault tolerance를 가져야함) 단지 에러를 보내는 것을 넘어 시스템이 복구될 수 있도록 해야한다.
결함감지
원격 노드가 요청을 처리하다 죽었다면 원격 노드에서 데이터가 얼마나 처리됐는지 알 수 있는 방법이 없다. 이런경우 다음과 같은 방법으로 결함을 감지할 수 있다.
애플리케이션에게 2xx,3xx 응답을 받지 않으면 우린 TCP Ack 만으로 안심할 수 없다 결국 뭔가 잘못되면 응답을 받지 못할 것을 가정하고 타임아웃을 두어 처리해야한다.
타임아웃과 기약없는 지연
타임아웃은 길어도 문제 안길어도 문제.. 보통 어떤 시간
d
내에 전송되거나 손실되지만 결코 d보다 오래걸리지 않고 장애가 나지 않는 노드가r
만큼의 처리 시간을 보장한다고 할때2d+r
의 타임아웃을 두나 실제로 분산시스템은 기약없는 지연(unbounded delay)이 있어 서버 구현이 대부분 어떤 시간 내에 처리한다고 보장할 수 없다.네트워크 혼잡과 큐 대기
추가로 TCP는 재전송도 있음.. 리소스가 풍부하면 상관없으나 실제 클라우드는 엄청 많은 서비스가 동작하고 있고, 맵리듀스 같은 일괄 처리 작업부하 등으로 네트워크 링크를 포화시키기 쉽다.
결국 우리는 실험적으로 타임아웃을 조정할 수 밖에 없다. 더 유동적으로 변동성(jitter)를 측정하고 관찰된 응답시간 분포에 따라 타임아웃을 자동으로 조절하게 하는 방법도 있다. 파이 증가 장애 감지기를 쓰는 아카와 카산드라가 그 예이다.
동기 네트워크 대 비동기 네트워크
전화처럼 동기 네트워크를 사용하면 되지 않을까? 라는 생각을 한다면 오산이다. 전화의 경우 다음과 같은 특징을 갖는다.
TCP의 경우 가용한 네트워크 대역폭을 기회주의적으로 사용한다. 이는 요청의 크기가 제각각인 다수의 통신을 가능하게 하기 위함이다(순간적으로 몰리는 트래픽 bursty traffic에 최적화돼있다). 리소스 사용률을 높이기 위함도 있따. 만약 특정 대역을 할당해서 사용한다면 얼만큼을 사용할지도 문제가 된다. 너무 적은 대역폭을 할당하면 리소스가 감당하지 못할 것이고, 너무 큰 리소스를 할당하면 리소스가 부족해 네트워크 리소스를 할당받는 시간만으로 기존의 대기시간을 넘어버릴 것이다.(애초에 IP와 이더넷은 패킷교환 프로토콜이라 네트워크에 기약없는 지연이 있다. 프로토콜부터 바꿔야함)
ATM, InfiniBand 등의 하이브리드 시도도 있었다. 서비스 품질(Quality of Service, 패킷에 우선순위를 매기고 스케줄링함), 진입제어(admission control, 전송측에서 전송률 제한)를 잘쓰면 패킷 네트워크에서 회선 교환을 흉내 내거나 통계적으로 제한 있는 지연을 제공하는 것이 가능하다. 문제는 이런 통신은 인터넷과 이더넷에서 사용할 수 없다..ㅠ 결국 실험적으로..
신뢰성 없는 시계
애플리케이션은 다양한 방식으로 시계에 의존한다. 대표적으로 지속 시간과 시점을 측정하는데, 분산 시스템에서는 통신이 즉각적이지 않기 때문에 시간을 다루기 까다롭다.
단조 시계 대 일 기준 시계
현재 컴퓨터는 최소 두가지 이상의 시계를 가진다(각각 사용 목적이 다름)
일 기준 시계(time-of-day clock)
System.currentTimeMillis()
는epoch
이래로 흐른 초(또는 밀리초) 수를 반환한다.단조 시계(monotonic clock)
System.nanoTime()
이 이에 해당시계 동기화와 정확도
일 기준 시계는 NTP서버와 동기화 돼야하는데 사실 다음과 같은 이유로 신뢰할만하지 않다
돈이 많으면 어느정도 해결할 수 있는 방법(좋은 장비와 프로토콜)이 있으나 완전히 해결하진 못한다.
동기화된 시계에 의존하기
우리는 이런 시간 결함을 고려하여 시스템을 설계해야한다. 보통 수정시계가 잘못되거나 NTP클라이언트 설정이 잘못된 경우 빠르게 눈치채지 못할 가능성이 높다. 따라서 동기화된 시계가 필요한 소프트웨어를 사용한다면 필수적으로 모든 장비 사이의 시계차이를 조심스럽게 모니터링 해야한다.
이벤트 순서화용 타임스탬프
카산드라에서 다음과 같은 상황이 발생할 수 있다.
여기서 Node2는
set x=1
을 나중으로 인식하여x+=1
연산을 버리고 만다. 이 충돌 해소 전략을 최종 쓰기 승리(last write wins, LWW)라고 불리며 다중 리더 복제와 카산드라 리악과 같은 리더 없는 데이터베이스에서 널리 사용된다. 타임스탬프를 클라이언트에서 구현할 수 있지만 근본적인 문제를 해결하진 못한다.우린 결국 일 기준 시계가 틀릴 수 있다는 것을 인지해야한다. 올바른 순서화를 위해서는 시계 출처가 측정하려고 하는 대상(즉 네트워크 지연)보다 훨씬 더 정확해야한다. 이른바 논리적 시계(logical clock)는 진동하는 수정 대신 증가하는 카운터를 기반으로 하며 이벤트 순서화의 안전한 대안이 된다. 논리적 시계는 일 기준 시간이나 경과한 초 수를 측정하지 않고 이벤트의 상대적인 순서만 측정한다.
시계 읽기는 신뢰 구간이 있다
아무리 마이크로초에서 나노초로 읽을 수 있는 시계가 있다해도 그 시계는 쉽게 드리프트되기 때문에 신뢰할 수 없다. 다만 우린 특정 신뢰하는 구간으로 시간을 읽을 수 있을 뿐이다. 시계에서 제공하는 오차 범위를 기반으로 계산하는 것이다(자세한 계산법은 생략)
그러나 실제 시스템은 이 불확실성을 노출하지 않는다.
cloack_gettime()
을 호출하더라도 오차가 몇인지는 알려주진 않으므로 신뢰구간이 5밀리초인지 5년인지 알 수 없다.하지만 구글의 트루타임(TrueTime) 같은 API는 시계의 신뢰구간을 명시적으로 보고한다. API 요청 시 [earliest, latest]를 받는다.
전역 스냅숏용 동기화된 시계
스냅숏 격리
에서 전역 단조 증가 트랜잭션ID가 필요하다. 그러나 분산 시스템에서 모든 파티션에 걸쳐 트랜잭션 ID를 생성하는 것이 쉽지 않다. 동기화된 일 기준 시계 타임스탬프를 기준으로 트랜잭션ID를 생성하는 것은 위험하다.(이미 많이 설명했다)스패너는 트루타임 API의 신뢰구간을 비교함으로써 스냅숏 격리를 구현한다. 두 구간이 겹치지 않는다면 순서를 보장하는 것이다. A = [A earliest, A latest] and B = [B earliest, B latest]) A earliest < A latest < B earliest < B latest 문제는 두 구간이 겹칠 시 순서를 보장할 수 없다는 것이다. 스패너는 읽기 쓰기 트랜잭션을 커밋하기 전에 의도적으로 신뢰구간만큼 기다림으로써 이를 해결한다. 너무 많은 지연이 되지 않기 위해서 구글은 각 데이터센터 내에 GPS 수신기나 원자 시계를 배치하여 약 7밀리초 이내로 동기화시킨다.
프로세스 중단
프로세스는 종종 다음과 같은 이유로 중단된다
이 경우 실행 중인 쓰레드는 어떤 시점에 선점하고 얼마간의 시간이 흐른 후 재개할 수 있는데, 선점된 쓰레드는 이를 알지 못한다. 이런 경우 멈춘 시간 동안 불필요하게 변화가 발생할 수 있다 (리더의 선점권 잃음 등)
단일 장비에서는 프로세스 중단을 대비할 많은 방법이 있으나 분산 시스템은 이를 바로 적용할 수 없다.
응답 시간 보장
위의 프로세스 중단은 충분히 많은 노력을 들여 원인을 제거할 수는 있지만 이는 리얼타임 운영체제가 필요하고, 우리가 사용하는 시스템에서 원하지 않는 결과를 갖는다.
가비지 컬렉션의 영향을 제한하기
가비지 컬렉션으로 인해 stop-the-world 가 발생하기 전에 애플리케이션에 알려줘서 요청을 중지하거나 하는 방법이 있다.
지식, 진실, 그리고 거짓말
다음주에..