2011년 6월 30일 목요일

Cassandra (5/12)

5. 카산드라 구조

이 장에서 우리는 카산드라가 어떻게 일하는지 이해하기 위해 그 디자인의 여러가지 면을 살펴보겠다. 우리는 peer-to-peer 디자인과 그 프로토콜을 고려하고 카산드라가 읽기, 쓰기 요청에 대해 어떻게 반응하는지 보고 확장성, 내구성, 유용성, 관리성 등의 구조적인 고려사항이 어떻게 영향을 받는지 살펴보겠다. 카산드라의 플랫폼 요청의 위임자로 동작하는Staged Event-Driven Architecture 의 채용도 논의한다.
카산드라의 구조는 정교하고 몇 가지 이론적인 구조에 의지하고 있다. 아직 알지 못하는 몇가지 용어를 참조하지 않고 또 새로운 용어 한 개를 논하는 것이 어렵다. 이는 조금 당황스러울수도 있기 때문에 후반에 Glossary를 넣었다.

5.1. 시스템 키스페이스

카산드라에는 system이라고 불리는 내부적 키스페이스가 있어서 클러스터가 동작에 도움을 주는 메타데이터를 저장하고 있다. Microsoft의 SQL서버에서 두 개의 메타 데이터베이스가 유지되는데 그것은 master와 tempdb이다. Master는 디스크 공간, 사용, 시스템 셋팅, 일반적인 서버 설치 노트 등의 정보가 저장된다. Tempdb는 중간 결과물들이나 일반적인 일들을 저장하는 워크스페이스이다. Oracle 데이터베이스는 항상 SYSTEM이라는 테이블 공간을 가지고 있으며 비슷한 목적으로 쓰인다. 카산드라 시스템의 키스페이스는 이것들과 유사하다.
특별히 시스템 키스페이스는 로컬 노드를 위한 메타데이터를 저장한다. 메타데이터는 다음을 포함한다.
 노드의 토큰
 클러스터의 이름
 키스페이스와 다이나믹 로딩을 지원하기 위한 스키마 정의
 마이그레이션 데이터
 노드가 bootstrapped 되었는지 여부
스키마 정의는 두개의 컬럼군에 저장된다. Schema 컬럼군은 사용자 키스페이스와 스키마 정의를 가지고 있고, Migrations 컬럼군은 키스페이스에 변경사항을 기록한다.
당신은 시스템 키스페이스를 변경하거나 쓸수는 없다.

5.2. Peer-to-Peer

전통적인 데이터베이스에서 MySQL처럼 다수의 기기에 배포될 수 있었다. 새로운 모델인 Google’s Bigtable 등도 마찬가지 였는데 어떤 노드들은 master로 어떤 것들은 slave로 설정되었다. 이것들은 클러스터 상에서 각각 다른 역할을 가지고 있었다. Master는 데이터의 소스이고 slave는 데이터들의 싱크를 master와 맞추었다. master에게 변경이 가해지면 slave에게도 전달이 되었다. 이러한 모델은 데이터를 읽는데 최적화되어있어서 어떤 slave로부터도 데이터를 읽을 수 있었다. 하지만 복사는 일방향으로서 master로부터 slave에게로 였다. 이것은 중요한 영향이 있는데 모든 쓰기는 master에게 보내져야 하며, 그것은 한 지점이 실패할 경우 위험하다는 것을 의미한다. Master/slave 셋팅에서 master 노드는 만약 오프라인이 된다면 많은 영향이 있을 수 밖에 없다.
반면에 카산드라는 peer-to-peer 분산 모델이어서 어느 노드이던지 동등하다. 그래서 master 노드가 별개로 존재하지는 않는다. 카산드라 디자인의 목적은 모든 시스템이 확장성이 쉽고 유용성이다. 만약 어떤 카산드라 노드를 오프라인 시킨다면 잠재적으로 모든 성능에 영향은 있겠지만 서비스를 불가능하게 하지는 않는다. 당신이 적당한 복사 전략을 쓰고 있다고 생각하면 어떤 노드에 문제가 생겼어도 그 데이터는 읽고 쓰기가 가능하다.
그리고 이 디자인은 카산드라에 새로운 노드를 추가하여 확장하기 쉽게한다. 각 노드의 동작은 같기 때문에 새로운 서버를 추가하기 위해서는 그냥 클러스터에 추가하면 된다. 처음에는 데이터를 받아들이고 링의 토폴로지를 배우기 위한 시간이 있어서 추가된 서버는 처음부터 모든 요청에 응답하는 것은 아니다. 이 과정을 마친후에는 링에 조인하고 요청을 받아들이게 된다. 이것은 설정은 최소로 필요로 하고 자동으로 이루어 진다. 이러한 이유 때문에 P2P 디자인은 확장 또는 축소하는 것을 master/slave 구조에서 보다 쉽게 만든다.

5.3. 소문과 실패 검출

비중앙화와 파티션 내구성을 지원하기 위해 카산드라는 gossip protocol 을 써서 링안에서의 통신과 각 노드들이 다른 노드들의 상태 정보를 갖고 있게 한다. Gossiper는 매우 바쁘게 움직인다. Anti-entropy 는 수동의 프로세스이어서 gossip에 의해 작동하지는 않는다.
Gossip 프로토콜(“epidemic protocol”로 불리기도 한다.)은 일반적으로 오류가 많은 네트워크를 가정하여 복사하는 분산 데이터베이스 환경에서 자동 메커니즘을 쓴다. 그것은 peer간에 필요한 정보를 교환하고 통신하기 때문에 그것에서 이름을 따왔다.
카산드라의 gossip 프로토콜은 org.apache.cassandra.gms.Gossiper 에 주로 구현되어 있으며 로컬 노드들간의 gossip을 관리하는게 주요임무이다. 서버 노드가 시작되었을 때 gossiper에 등록하여 각 종단의 상태 정보를 받아볼수 있게한다.
카산드라 gossip이 실패를 검출하는데 사용되기 때문에 Gossiper 클래스는 살아있고 죽은 노드들의 리스트를 관리하게 된다.
여기 gossiper가 작동하는 방식이 있다.
1. 주기적으로 (TimerTask에 설정한대로) G=gossiper 는 무작위로 링안의 노드를 골라서 gossip 세션을 초기화한다. 각 gossip의 주기는 세 개 메시지를 필요로 한다.
2. Gossio 초기화하는 것은 그 고른 친구에게 GossipDigestSynMessage를 보낸다.
3. 친구가 이 메시지를 받았을 때 GossipDigestAckMessage를 반환한다.
4. Ack 메시지를 받으면 GossipDigestAck2Message를 다시 보내서 종료한다.
Gossiper가 다른 종단이 죽었다고 결정되면 그것의 로컬 리스트에 죽었다고 선언하고 로깅한다.
카산드라는 분산컴퓨팅을 위한 유명한 알고리즘인 Phi Accrual Failure Detection으로 실패 검출을 잘 지원한다. 이 실패 검출 방법은 2004년 일본의 Advanced Institute of Science and Technology에 의한 것이다.
Accrual 실패 검출은 두 가지 주된 아이디어에 기반하였다. 첫번째 일반적인 아이디어는 실패 검출은 모니터링하는 애플리케이션에 decoupling 되어 유연해야 한다는 것이다. 두번째로 더욱 참신한 아이디어는 간단히 heartbeat 이라는 것을 두어 그에 따라 노드가 살았는지 죽었는지 판단할 수 있는것으로써 전통적인 실패 검출을 뒤집은 것이다. Accrual 실패 검출은 이 접근방법이 순진하고 죽었다는 것과 살아있다는 것 사이에 suspicion level 이라는 것을 두어 접근한다.
그러므로 실패 모니터링 시스템은 suspicion 레벨을 끊임없이 나타내어 노드가 죽었는지 잘 나타낸다. 이것은 네트워크 환경에서 계정의 변동을 나타내서 바람직하다. 예를 들어 한 개의 연결이 이상이 있다고 해서 전체 노드가 죽었다고 할 필요는 없는것이다. 이것은 단순한 두 가지 상태변화로 보는것보다 heartbeat의 샘플링을 통해 전체적으로 더 강하고 더 약한 실패의 가능성을 따져봄으로써 더 좋다.
실패 검출은 카산드라에서 org.apache.cassandra.gms.FailureDetector 클래스에 구현되어있고 이것은 org.apache.cassandra.gms.IFailureDetector 인터페이스를 구현한다. 이것들은 함께 아래 동작을 지원한다.
isAlive(InetAddress)
주어진 노드가 살아있는지 보고한다.
Interpret(InetAddress)
Suspicion 레벨에 근거해서 gossiper가 어떤 노드가 살았는지 죽었는지 결정하도록 한다.
Report(InetAddress)
어떤 노드가 heartbeat를 받았을 때 이 메서드를 부른다.

5.4. Anti-Entropy 와 Read Repair

당신이 gossip 프로토콜을 볼때는 종종 그에 카운터파트인 컴퓨팅의 유행성의 이론에 역시 기반한 anti-entropy 라는 이론을 보게될 것이다. Anti-entropy 는 카산드라에서 각 다른 노드들의 데이터가 최신으로 업데이트 되도록 하는 복사본 싱크 메커니즘이다.
여기 어떻게 작동하는지 본다. 주된 작동동안 서버는 Merkle 트리를 이웃 노드들과 교환하기 위해서 TreeRequest/TreeResponse 대화를 초기화한다. Merkle 트리는 컬럼 군에서 데이터를 표현하는 해쉬이다. 다른 노드의 트리와 맞지 않으면 그것이 셋팅되어야 하는 가장 최근의 데이터 값으로 repair 혹은 reconcile 하게 된다. 이 트리 비교는 org.apache.cassandra.service.AntiEntropyService 클래스가 하게 된다. AntiEntropyService 는 싱글톤 패턴을 구현하며 Differencer 클래스도 정의하는데 이것은 두 트리를 비교하여 무슨 차이점을 발견하면 repair를 시작하게 된다.
Anti-entropy 는 Amazon의 Dynamo에도 사용되며 카산드라 구현은 이를 모델링한다.
Dynamo는 Merkle 트리를 anti-entropy 를 위해 사용한다. 카산드라가 그것들을 역시 사용하며 하지만 구현은 조금 다르다. 카산드라에서 각 컬럼군은 각 고유의 Merkle 트리를 가진다. 트리는 각 비교 동작시에 snapshot 처럼 생성된다. 그리고 링에서 이웃하는 노드에게 보내져야 할 때 까지는 보관된다. 이 구현의 장점은 disk I/O를 줄인다는데 있다.
각 업데이트 후에 anti-entropy 알고리즘이 작동한다. 이것은 데이터베이스에 대하여 checksum을 수행하고 peer간 checksum을 비교한다. 만약 checksum이 다르면 데이터가 교환된다. 이것은 시스템이 불필요한 anti-entropy를 수행하지 않도록 peer들이 가장 최근의 업데이트를 받도록 시간 창을 사용하는 것이 필요하다. 동작을 빠르게 하기 위해 노드는 내부적으로 타임스탬프에 의한 변환된 인덱스를 유지하고 가장 최근의 업데이트만을 교환한다.
카산드라에서 클러스터를 이루기 위해 당신은 여러 개의 노드를 가지고 있고 한 개나 그 이상의 여러 개의 노드는 주어진 데이터의 복사본 처럼 작동한다. 이 데이터를 읽기 위해서 클라이언트는 클러스터 안의 어느 노드든과 연결하고 클라이언트에 의해 보여진 일관성 레벨에 따라 몇 개의 노드를 읽는다. 클라인언트의 일관성 레벨이 맞을 때까지 읽기 동작은 블록되어있다. 만약 어떤 노드가 기간이 지난 값으로 대답을 하면 카산드라는 클라이언트에게 가장 최근의 값을 돌려주게 된다. 반환한 후에는 카산드라는 read repair 라는 것을 백그라운드로 실행하게 된다. 이 동작은 값을 가장 최근의 값으로 유지한다.
카산드라에 의해 수행되는 이 디자인은 Project Voldemort 와 Riak 같은 straight key/value 를 저장한다. 이것은 클라이언트가 모든 노드가 읽혀질때까지 블록하지는 않지만 read repair 가 데이터를 신선하게 유지하는일을 백그라운드로 실행하기 때문에 퍼포먼스를 더 좋게하는 역할을 한다. 만약 클라이언트가 많다면 정족수로부터 읽어오는 것은 적어도 하나는 가장 최근의 값을 가지고 있다는 점을 분명히 하기 위해서 중요하다.
만약 클라이언트가 약한 일관성 레벨을 보여준다면 read repair는 클라이언트로 돌아온 후에 백그라운드로 실행하게 된다. 만약 당신이 더 강한 일관성 레벨을 사용한다면 read repair는 데이터가 클라이언트에게 반환되기 전에 실행된다.
만약 읽기 동작이 같은 타임스탬프에 저장이 되었으나 다른 값을 보여준다면 카산드라는 tie-breaking 메커니즘으로 값을 직접 비교하여 그 읽기가 무한 루프에 빠지지 않도록 해준다. 이런 케이스는 매우 드물다.

5.5. Memtables, SSTables, and Commit Logs

쓰기 동작을 수행할 때 이것은 바로 commit log에 기록된다. Commit log는 카산드라의 내구성 목표를 지원하기 위한 crash-recovery 메커니즘이다. 쓰기는 commit log에 기록될 때까지 성공했다고 여겨지지 않는다. 쓰기가 메모리 저장이 아직 안되었다는 것을 확실히 하여 아직 데이터를 복구할 수 있다.
Commit log에 쓰여진 후 값은 memtable이라고 불리는 메모리 데이터 구조에 저장된다. Memtable에 여러 개의 오브젝트가 저장되어 쓰레쉬 홀드를 넘어가면 memtable은 SSTable이라고 불리는 디스크의 한 파일로 배출된다. 새로운 memtable은 생성된다. 이 배출은 블록블럭 않는 동작이다. 한 컬럼군에 대해 여러 개의 memtable이 존재할 수 있다. 하나는 현재 동작하고 나머지는 배출되기를 기다린다. 이것은 보통은 과부하가 걸리지 않은 이상 노드가 그것을 빨리 배출하기 때문에 크게 오래 기다리지는 않는다.
각 commit log는 내부적으로 비트 플래그 가지고 있어서 배출이 필요한지 아닌지 나타낸다. 처음 쓰기 동작이 받아들여 졌을 때 commit log에 쓰여지고 비트 플래그는 1로 셋팅된다. 컬럼군에는 한 개의 비트 플래그가 있다. 왜냐하면 모든 서버에 대하여 한 개의 commit log가 쓰여지기 때문이다. 모든 컬럼군에의 쓰기동작은 같은 commit log로 간다. 그래서 비트 플래그는 특정한 commit log가 특정한 컬럼군에 대해서 배출되지 않았는지의 여부를 담고 있다. 한 번 memtable이 디스크로 잘 배출되면 해당하는 commit log의 비트 플래그는 0으로 셋팅된다. 이것은 commit log가 내구성을 위하여 더 이상 그 데이터를 보관하지 않아도 됨을 말한다. 일반적인 logfile처럼 commit log는 설정가능한 쓰레쉬 홀드를 가지며 한번 이 쓰레쉬 홀드가 도달하면 없어지도 다시 시작한다.
SSTable은 구글의 Bigtable에서 빌려온 개념이다. Memtable이 SSTable로 디스크에 배출되면 애플리케이션에 의해서 바뀔 수가 없다. SSTable이 꽉 압축되었다는 사실에도 불구하고 이 압축은 디스크상의 표현을 바꿀 뿐이다. 이것은 근본적으로 합치는 단계를 거쳐서 새파일을 만들고 기존에 성공했던 예전 파일을 지운다.
각 SSTable은 관련된 Bloom filter를 가진다. 이것은 추가적인 퍼포먼스 향상을 위해 쓰인다. 모든 쓰기 동작은 순차적이어서 카산드라에서 쓰기가 잘 동작되는 이유이다. 카산드라에서 모든 쓰기동작은 추가적인 동작이어서 다른 읽기 찾기 동작은 값을 쓰기 위해서 필요하지 않다. 이것은 한가지 디스크상의 속도 퍼포먼스에 제한사항을 가진다. 압축은 데이터의 재정렬을 분할상환하는데 순차적인 IO를 사용한다. 그래서 퍼포먼스 이득은 각 각 나누는 데서 생긴다. 쓰기 동작은 즉각적인 추가이다 그래서 압축은 차후 읽기의 퍼포먼스를 좋게한다. 카산드라가 값은 최종적으로 속한 곳에 삽입한다면 쓰기 클라이언트는 추후에 찾을 것이다.
읽기 동작에서 카산드라는 값을 찾기위해 memtable을 처음 찾을 것이다. Memtable은 org.apache.cassandra.db.Memtable 클래스에 의해 구현된다.

5.6. Hinted Handoff
다음의 시나리오를 고려해보자. 쓰기 요청이 카산드라에게 보내졌다. 그러나 네트워크 파티션이나 하드웨어 문제 등 이유로 해당하는 노드가 가용하지 않다. 이런 상황에서 일반적으로 가용성을 확실히 하기 위해서 카산드라는 hinted handoff라는 것을 구현했다. 당신은 hint라는 것이 쓰기 동작에서 어떤 정보를 담은 작은 노트정도로 생각할 수 있다. 쓰기가 되어야 할 노드가 실패한다면 쓰기를 받은 카산드라 노드는 hint를 만든다. 이것은 “나는 노드 B에 해당하는 쓰기 정보를 가지고 있다. 나는 노드 B가 온라인으로 돌아오면 쓰기 요청을 다시 보낼것이다.” 라는 리마인더와 같다. 이것은 노드 A가 노드B에게 쓰기에 대해서 hint를 준것이다.
이것은 카산드라가 항상 쓰기 가능하게 해준다. 그리고 실패한 노드가 온라인으로 되돌아 온 후에 일관성없는 시간을 줄여준다. 우리는 이전에 일관성 레벨에 관해서 논했다. 그리고 일관성 레벨 ANY 는 0.6 버전에서 추가되었고 hinted handoff 가 쓰기 동작의 성공에 대해 충분하다는 것을 기억할 것이다. 이것은 hint가 기록될 수 있어도 쓰기는 여전히 성공적이라고 보이는 것이다.
Hinted handoff에 대한 걱정이 카산드라 커뮤니티 멤버사이에 있었다. 처음에는 전체적인 내구성을 위해서 좋은 디자인이라고 보였다. 그리고 Java Message Service(JMS) 같은 많은 분산형 컴퓨팅 패러다임에 익숙했기 때문에 문제가 없다고 보였다. 배달이 보장된 JMS 큐에서 만약 메시지가 받는 이에게 전달이 되지 못하면 JMS는 잠시의 인터벌 동안 기다리고 받을 때까지 다시 보낸다. 하지만 JMS와 카산드라 hinted handoff 양쪽에는 실제적인 문제점이 있다. 만약 노드가 일정 시간동안 오프라인으로 되면 hint는 다른 노드에 많이 쌓이게 된다. 그리고 다른 노드가 실패한 노드가 다시 온라인으로 돌아온 것을 통보하면 그 노드는 많은 요청의 홍수에 둘러싸이고 위험한 상태가 된다.
이 걱정에 대하여 지금은 hinted handoff를 끌수도 있거나 좀 더 극적이지는 않게 프라이어리티를 줄여서 새로운 쓰기 요청보다 낮게 할 수 있다.

5.7. 압축

카산드라에서 압축 동작은 SSTable을 합치기 위해서 한다. 압축하는 동안 SSTable에 있는 데이터는 합쳐진다. Key도 합쳐진다. 컬럼도 합쳐지고 tombstone은 버려진다. 그리고 새로운 인덱스가 만들어진다.
압축은 많은 축적된 데이터를 합쳐줌으로써 빈 공간을 만드는 과정이다. 이것은 관계형 세상에서 table을 새로 만드는 과정과 대략 비슷하다. 그러나 Stu Hood가 지적했듯이 카산드라의 주된 다른점은 서버의 생명주기동안 분할 상환될 투명한 동작이다.
압축에서 합쳐진 데이터는 정렬된다. 새로운 인덱스는 정렬된 데이터에 대해 만들어진다. 새로 합쳐지고 정렬되고 인덱스가 만들어진 데이터는 한 개의 SSTable에 쓰여진다. 각 SSTable은 데이터, 인덱스, filter로 이루어 진다. 이 과정은 클래스 org.apache.cassandra.db.CompactionManager에 의해 관리된다. CompactionManager는 MBean을 구현한다.
압축의 또 다른 중요한 기능은 필요한 찾기의 수를 줄여서 퍼포먼스를 향상시키는 것이다. 주어진 key에 대해서 컬럼 데이터를 찾기 위해서는 일정 수의 SSTable을 찾아야 한다. Key가 자주 변형된다면 그 변형은 배출된 SSTable에서 종료될 것이다. 이것은 압축하는 것은 데이터베이스가 찾기를 실행해서 각 SSTable로부터 데이터를 가져올 필요가 없게 해준다.
카산드라에는 여러가지 다른 타입의 압축이 있다. 주된 압축은 한 두가지 방법에 의해 시작된다. 노드 탐색에 의하거나 자동으로 이루어지는것이다. 노드 탐색은 TreeRequest메시지를 타겟 주변의 노드에게 보낸다. 노드가 TreeRequest를 받았을 때 곧바로 read-only 압축을 실행하여 컬럼군을 검사한다.
Read-only 압축은 아래의 단계를 거친다.
1. 컬럼군에서 key 배분을 가져온다.
2. 행이 검사항목에 더해지면 만약 컬럼군이 검사되어야 하면 Merkle tree를 만들어 이웃한 노드들에게 브로드캐스팅한다.
3. Merkle tree가 Differences의 리스트와 함께 가져온다.
4. 비교가 StageManager에 의해서 실행되고 이것은 일을 실행하는데 동시성을 다루는데 책임이 있다. 이 케이스에서 StageManager는 Anti-Entropy Stage를 사용한다. 이것은 org.apache.cassadra.concurrent.JMXEnabledThreadPoolExecutor클래스를 사용하며 한 쓰레드안에서 압축을 실행하여 MBean 에서 처럼 동작이 가능하게 만든다.
당신은 압축 쓰레드의 priority를 줄여서 전반적인 퍼포먼스를 향상할 수 있다.
이것을 하기 위해서는 아래의 플래그를 사용한다.
-Dcassandra.compaction.priority=1
이것은 IO가 아니라 CPU 사용에 영향을 미친다.

5.8. Bloom Filters

Bloom filter는 퍼포먼스 향상기 처럼 사용된다. 그것을 발명한 Burton Bloom의 이름을 따서 이름지어 졌다. Bloom filter는 매우 빠르고 결정적이지 않은 알고리즘으로서 어떤 요소가 셋의 멤버인지 아닌지 테스트할 수 있다. 그것이 결정적이지 않다는 것은 Bloom filter로부터 false-positive 를 읽어올 수 있고 이것은 false-negative가 아니다. Bloom filter는 데이터의 값을 비트 어레이에 매핑하고 큰 데이터셋을 digest string에 매핑한다. Digest는 정의대로 원래의 데이터가 사용하는 것보다 적은 메모리를 쓴다. Filter는 메모리에 저장되며 key 를 찾을 때 데이터 접근을 줄임으로서 퍼포먼스를 향상시키는데 쓰인다. 디스크 접근은 일반적으로 메모리 접근보다 느리다. 그래서 Bloom filter는 cache의 특별한 한 종류이다. 쿼리가 실행될 때 Bloom filter는 디스크를 접근하기 전에 처음 체크된다. False-negative가 나오지 않기 때문에 filter가 그 인자가 셋안에 존재하지 않는다고 하면 그것은 확실히 없다. 그러나 필터가 엘리먼트가 셋안에 있다고 하면 디스크는 꼭 접근된다.
새로운 JMX MBean 이 Nodetool에 추가되어 false-positive의 수를 체크할수 있게 되면 당신의 Bloom filter는 반환된다. 이 동작은 getBloomFilterFalsePositives라고 불리운다.

5.9. Tombstones

관계형 세상에서 soft delete라는 아이디어에 익숙할 것이다. 실제 delete SQL문을 실행하는 대신에 애플리케이션은 delete라고 불리게 값을 업데이트 한다. 예를 들면 프로그래머는 때로 audit trail을 지원하기 위해 이것을 한다.
카산드라에는 tombstone이라는 비슷한 개념이 있다. 이것은 모든 delete가 일하는 방식이고 그래서 당신을 위해 자동으로 이루어질 것이다. 당신이 delete 동작을 실행할 때 데이터는 즉시 지워지지는 않는다. 그 대신 값에 tombstone을 놓는 업데이트 처럼 작동할 것이다. Tombstone은 지우기를 표시한 마크와 같은데 압축이 실행될 때까지 SSTable의 오래된 데이터를 억누를 필요가 있다.
Garbage Collection Grace Seconds라는 관련된 셋팅이 있다. 이것은 서버가 tombstone을 garbage collect 하기까지 기다리는 시간이다. 디폴트로 이것은 864,000초 즉 10일이다. 카산드라는 tombstone의 나이를 따라간다. 그리고 tombstone이 GCGraceSeconds보다 오래되면 garbage collect가 일어난다. 이 딜레이의 목적은 가용하지 않았던 노드에게 깨어날 시간을 주고 만약 노드가 이 시간보다 오래 다운되면 실패로 간주되고 교체된다.
0.7 버전에서 이 셋팅은 컬럼군당 설정이 가능하다.

5.10. Staged Event-Driven Architecture (SEDA)

카산드라는 Staged Event-Driven Architecture를 구현한다. SEDA는 매우 동시적인 인터넷 서비스를 위한 일반적인 구조이다. 원래는 2001년 “SEDA: An Architecture for Well-Conditioned, Scalable Internet Services”라는 논문에서 Matt Welsh, David Culler, Eric Brewer에 의해서 제안되었다.
일반적인 애플리케이션에서 한 가지의 일은 한 쓰레드안에서 이루어진다. 예를 들면 쓰기 동작은 같은 쓰레드 안에서 이루어진다. 그러나 카산드라는 다르다. 이것의 동시성 모델은 SEDA에 기반했다. 그래서 한 동작은 한 쓰레드에서 시작하여 다른 쓰레드로 그 일을 넘겨줄수도 있다. 하지만 현재 수행중인 쓰레드에게 그 책임이 달린 것은 아니고 일은 stage라고 불리는 것으로 나누어졌고 쓰레드 풀 (java.util.concurrent.ExecutorService) 에 의해 결정이 된다. Stage는 기본적인 일의 단위이다. 그리고 한 일은 내부적으로 한 stage에서 다른 것으로 넘어가게 된다. 다른 stage는 다른 쓰레드에 의해서 다루어 질수 있기 때문에, 카산드라는 퍼포먼스에 향상을 보게 되었다. SEDA 디자인은 그 자신의 리소스를 카산드라가 관리할 수 있다는 것을 의미한다. 왜냐하면 다른 동작은 디스크 IO, 혹은 CPU 제약, 네트워크 동작 등을 필요로 할 수 있기 때문에 풀은 자신의 일을 사용가능한 리소스에 따라 관리한다.
Stage는 들어오는 이벤트 큐와 이벤트 핸들러로 이루어 지며 관련된 쓰레드 풀이 있다. Stage는 스케줄링과 쓰레드 배분을 결정하는 콘트롤러에의해 관리된다. 카산드라는 일종의 동시성 모델을 구현하며 쓰레드 풀인 java.util.concurrent.ExecutorService를 사용한다. 이것이 어떻게 일하는 지 보기위해 org.apache.cassandra.concurrent.StageManager 클래스를 보라.
아래의 동작은 카산드라에서 stage로 표현된다.
 Read
 Mutation
 Gossip
 Response
 Anti-Entropy
 Load Balance
 Migration
 Streaming
몇 개의 추가적인 동작이 stage로 구현되어 있다. Memtable에 수행이 되는 일들의 단위를 위한 stage가 있고 Consistency Manager는 StorageService의 stage이다.
Stage는 IVerbHandler인터페이스를 구현하여 주어진 동사에 대한 기능을 지원한다. Mutation의 아이디어도 stage로 표현이 되므로 insert와 delete 양쪽 동작에서 역할이 있다.
SEDA 는 강력한 구조이다. 이벤트 드리븐 이기 때문에 이름이 말하듯이 업무들이 매우 뛰어난 동시성을 가지고 수행될 수 있다.

5.11. Manager 와 Service 들
카산드라의 기본 내부적 콘트롤 메커니즘을 구성하는 클래스들이 있다. 나는 여기서 그것들의 간단한 오버뷰를 보여주어 당신이 더 중요한 것들에 익숙해 질 수 있도록 할 것이다. 고려해야할 첫번째 것은 org.apache.cassandra.thrift.CassandraServer 클래스이다. 이 클래스는 Thrift 인터페이스로의 호출을 구현하고 org.apache.cassandra.service.StorageProxy 로 실행하기 위한 쿼리들의 노력을 대리한다.

5.11.1. 카산드라 데몬

Org.apache.cassandra.service.CassandraDaemon인터페이스는 한 노드에서 수행되는 카산드라 서비스의 라이프 사이클을 표현한다. 이것은 당신이 예상할수 있는 일반적인 라이프 사이클로서 start, stop, activate, deactivate, destroy 등이다.

5.11.2. 스토리지 서비스

카산드라 데이터베이스 서비스는 org.apache.cassandra.service.StorageService에 의해 표현된다. 스토리지 서비스는 노드의 토큰을 담고 있는데 그것은 노드가 책임이 있는 데이터의 범위를 나타내준다.
서버는 이 클래스의 initServer 메서드에 호출을 하면서 시작된다. 이때 서버는 SEDA 에 등록을 하고 state에 관한 몇 결정을 하게 된다. 그리고 그 자신을 JMXServer에 MBean으로 등록한다.

5.11.3. 메시징 서비스

Org.apache.cassandra.net.MessagingService 의 목적은 메시지 교환, 서비스의 인바운드 아웃바운드 메시지를 듣는 소켓을 만드는 것이다. MessagingService.listen 메서드는 쓰레드를 만든다. 각 들어오는 연결은 org.apache.cassandra.net.IncomingTcpConnection을 사용하여 ExecutorService 쓰레드를 접근한다. 메시지는 검사되고 이것이 스트리밍 메시지인지 아닌지 결정하게 된다. 메시지 스트리밍은 카산드라가 이 노드에서 다른 노드로 SSTable 파일을 보내기위한 최적화된 길이다. 다른 모든 노드들간의 커뮤니케이션은 직렬화된 메시지를 통해 일어난다. 그것이 만약 스트리밍이면 메시지는 IncomingStreamReader 으로 전달된다. 그것이 스트리밍이 아니면 메시지는 MessagingService 비직렬화 수행기에 의해 다루어진다. 그것은 메시지를 Runnable을 구현한 일의 형태로 다룬다. 이 서버가 많은 stage의 사용을 하고 풀은 MBean으로 쌓여있기 때문에 이 서비스가 어떻게 일하는지 발견할 수 있다.

5.11.4. Hinted Handoff 매니저

이름이 말하듯이 org.apache.cassandra.db.HintedHandoffManager는 hinted handoff를 내부적으로 관리하는 클래스이다. 그렇게 하기위해 쓰레드 풀을 가지고 있으며 그것은 JMX monitoring이 HINTED-HANDOFF-POOL 로 가능하다.

댓글 없음:

댓글 쓰기