여러 응용 서비스들에서 서비스 응답 속도를 빠르게 하고 조회 처리량을 늘리기 위하여, 확장 가능한 인메모리 캐시 시스템을 적용하는 것이 일반화되었다. 응용 서비스의 원본 데이터는 DB 같은 영구 저장소에 저장하고, 그 중에 자주 조회되는 데이터는 인메모리 기반의 캐시 시스템에 보관하여 반복된 조회 요청을 빠르게 처리함으로써 응용 서비스의 성능을 확대한다. 이러한 캐시 시스템은 어떤 고장이 발생하더라도 중단 없는 캐시 서비스를 제공하는 fault tolerance 특성을 가져야 한다. 캐시 시스템에 필요한 fault tolerance 의미를 설명하고, ARCUS 캐시 시스템에서 fault tolerance를 어떻게 제공하는 지를 소개한다.
캐시 시스템은 DB 같은 원본 데이터 저장소에서의 조회 결과를 저장하고 반복된 조회 요청을 빠르게 처리하는 고성능의 인메모리 저장소이다. 캐시 시스템의 일부가 고장나서 정상적인 캐시 서비스를 제공하지 못한다면, 해당 응용은 원본 데이터 저장소인 DB로 다시 요청하게 되므로 캐시 사용에 따른 성능 개선 효과가 사라질 수 있다. 따라서, 캐시 시스템은 중단 없는 캐시 서비스를 제공할 수 있는 fault tolerance를 가져야 하며, 이를 위해 아래 2 가지 사항을 반드시 만족해야 한다.
정상 캐시 노드들만으로 클러스터가 구성되도록 그 구성을 즉시 변경하여야 한다.
클러스터의 캐시 노드 목록에서 고장난 캐시 노드를 제거하여 그 캐시 노드로 응용 요청이 들어가지 않도록 한다.
응용에 장착된 캐시 클라이언트들도 수정된 캐시 노드 목록을 인지하고, 정상 캐시 노드에만 요청을 보내야 한다.
위의 첫째 사항은 쉽게 이해할 수 있어서 추가 설명을 생략하고, 둘째 사항은 예를 들어 설명한다. 아래 그림은 3개 캐시 노드로 구성된 클러스터에서 각 캐시 노드는 전체 캐시 데이터의 1/3씩 나누어 캐싱하고 있는 상태이다. 이 클러스터에서 N2 캐시 노드가 고장날 경우, N2 캐시 노드를 (1) 클러스터에 그대로 두는 경우와 (2) 클러스터에서 즉각 제거하는 경우를 구분하여 설명한다. 클러스터에서 고장난 캐시 노드를 자동으로 제거하는 기능을 본 기사에서 automatic failstop이라 부른다. 참고로, fail-remove 용어가 더 적합할 수 있으나, fail-stop 용어가 친숙하고 틀리지 않는 용어라서 사용한다.
캐시 노드 고장은 야간, 주말 등의 임의 시점에 발생할 수 있어, 운영 관점에서 위의 2 경우는 큰 차이를 보인다. 캐시 노드 고장으로 DB에 지속적으로 높은 부하가 발생하여 응용 서비스 성능이 떨어진다면, 운영자가 빠르게 조치해야 한다는 것이 가장 힘든 부담이다. 이를 운영 관점에서 비교하면 다음과 같다.
캐시 시스템에서 고장난 캐시 노드를 제거할 수 있는 이유는 원본 데이터의 영구 저장소가 아니기 때문이다. 즉, 캐시 데이터는 원본 데이터 저장소에 다시 질의하여 얻을 수 있고, 이를 캐시 시스템에 다시 캐싱할 수 있기 때문이다. 참고로, 원본 데이터 저장소가 고장난다면, 데이터 손실이 없어야 하므로 즉각 데이터 회복(recovery)으로 복구하거나 데이터 복제를 통해 존재하는 다른 저장소로 failover한 이후에 복구하여야 한다.
ARCUS 캐시 클러스터는 고가용의 ZooKeeper 시스템을 이용하여 캐시 노드 목록을 안전하게 관리하며, 아래 ZooKeeper 기능을 활용하여 캐시 시스템의 automatic failstop 기능을 구현한다.
위의 ZooKeeper 기능으로 기본적인 automatic failstop 기능을 구현하고, ARCUS 캐시 클러스터의 수평 확장성(horizontal scalability)도 구현한다는 사실을 알 수 있다. ZooKeeper 활용하여 구현한 Scalable and Fault Tolerant ARCUS 캐시 클러스터의 구조를 아래 그림으로 설명하고자 한다.
앞서 기술한 ZooKeeper ephemeral node와 ZooKeeper ping 기능으로 탐지하지 못하는 고장으로 ARCUS 캐시 노드가 정상적으로 서비스할 수 없는 hang 상태가 있다. 이러한 hang 상태를 탐지하기 위하여 ARCUS 캐시 노드는 자신에게 몇가지 캐시 요청을 보내고 그에 대한 응답이 제 시간에 오는 지를 확인하는 mc heartbeat 기능을 가지고 있으며, 이를 주기적으로 수행한다. 참고로, mc heartbeat 이란 용어는 ARCUS 캐시 노드가 memcached 기반으로 확장된 것이고, 이에 대한 heartbeart를 지칭한다.
ARCUS 캐시 노드는 mc heartbeat이 실패하는 경우에도 스스로 ephemeral node를 제거하고 종료하여 automatic failstop 한다.
ARCUS 캐시 클러스터는 수평 확장이 가능하면서 automatic failstop 기능으로 고장난 캐시 노드를 자동 제거한다. 응용에 장착된 ARCUS 캐시 클라이언트는 최신의 캐시 노드 목록을 기반으로 consistent hashing 로직을 수행하여 캐시 데이터를 여러 캐시 노드들에 분배하므로, 캐시 노드의 추가나 제거가 발생하면 자동으로 캐시 데이터의 재배치가 발생한다. 이러한 재배치에 의하여 stale 캐시 데이터가 발생하고 이러한 stale 데이터가 응용에게 노출되는 문제가 발생할 수 있다. 예를 들어, A, B, C 3개 캐시 노드로 구성된 클러스터에서 각 캐시 노드의 해시 위치가 consistent hashing 로직에 의해 아래 그림처럼 정해져 있고, key1의 해시 위치는 C 노드와 A 노드 사이에 존재한다고 가정한다. 이 상태에서 stale 데이터가 발생하고 응용에 노출되는 시나리오는 아래와 같다.
Stale 데이터가 자동으로 소멸되게 하려면, 각 캐시 데이터의 만료 시간(expire time)을 짧게 지정하는 방법이 있다. 하지만, 응용에서 생성하는 캐시 데이터의 용도나 성격에 따라 만료 시간을 짧게 지정할 수 없는 경우도 있으므로, 이러한 stale 데이터는 캐시 시스템에서 제거해 주어야 한다. ARCUS 캐시 시스템은 아래 방식으로 stale 데이터를 자동으로 제거하는 기능을 제공한다.
캐시 노드 고장 시의 fault tolerance를 보장하는 다른 방안으로, 복제(replication)를 통해 캐시 데이터를 이중화하고 master 노드의 고장 시에 slave 노드를 새로운 master 노드로 승격시키는 failover 기능으로 캐시 서비스를 중단 없이 제공할 수 있다. 이러한 복제 기능은 캐시 데이터의 손실(loss) 없이 fault tolerance를 제공하는 장점이 있지만, 이는 앞서 기술한 automatic failstop 기능의 완전한 대체제로 보기는 어렵고 상호 보완하는 기능으로 이해해야 한다. 그 이유는 다음과 같다.
캐시 시스템을 이중화하는 것은 비용이 많이 든다. 캐시 노드 고장으로 DB에서 다시 조회하는 성능이 아주 낮다면, 비용을 들여서라도 이중화할 충분한 이유가 된다. 예를 들어, 복잡한 조인(join) 연산이나 집계 연산으로 조회 결과를 얻어 캐싱하는 경우가 이에 해당된다. DB 조회 성능이 크게 떨어지지 않는 캐시 데이터인 경우는 automatic failstop으로 fault tolerance를 보장하는 것이 더 효율적인 방법이다.
캐시 시스템에서 제공해야 할 fault tolerance 특성을 정리하고, 이러한 특성을 제공하기 위하여 ARCUS 캐시 시스템에 구현한 automatic failstop 기반의 fault tolerance 제공 방식을 설명하였다. 캐시 시스템의 일부가 고장나더라도 응용 서비스의 캐시 사용에 따른 성능 개선 효과를 그대로 유지하면서 캐시 시스템 운영자의 피로도를 제거하기 위하여 automatic failstop 기반의 fault tolerance 제공은 반드시 필요하다고 본다.
ARCUS 캐시 시스템에서 fault tolerance 개선 사항으로, 여러 유형의 고장을 빨리 탐지하는 기능을 추가하여야 한다. ZooKeeper Ping 기능은 timeout 기반의 고장 탐지 기능으로, 고장 탐지에 일정 시간이 소요된다. 네트웍 문제가 아니라 고장 원인이 분명한 경우에는 이를 빠르게 탐지하여 failstop 시키는 것이 응용 서비스에게 유리하다.