IL-0279 Grab · 2019-02-15databaseaws
Redis Cluster 의 슬레이브 노드 하나가 죽자 파이프라인 읽기가 실패해 Grab 의 운송 예약 서비스 Apollo 호출이 1분 동안 21번에 1번꼴로만 성공했다
Apollo 는 Grab 의 기사 쪽 상태 머신이다. 거의 모든 요청의 중요 경로에 있고, 기사별 가용 상태를 AWS ElastiCache Redis Cluster 에 저장해 예약 서비스가 예약을 기사에게 배정하게 한다. 이 Redis Cluster 는 샤드 3개에 샤드마다 슬레이브 2대를 둔다. Apollo 는 Go-Redis 클라이언트를 쓰며 쓰기 질의는 마스터 노드로 보낸다. 읽기에는 ReadOnly 플래그를 켜고 RouteByLatency 플래그를 끈다. 그러면 Grab 의 gredis3 라이브러리에서 ReadOnlyFromSlaves 가 켜져 모든 읽기가 슬레이브 노드로 간다. 마스터 노드의 CPU 부하를 덜기 위해서다.
2월 15일 슬레이브 노드 하나가 죽자 그 노드에 배정된 명령은 모두 실패했다. Grab 의 Redis 라이브러리 코드는 파이프라인 안의 명령 하나가 오류를 내면 파이프라인 전체가 실패했다고 알렸고, 엔지니어들은 err != nil 이면 실패로 처리했다. 그래서 HMGET 파이프라인 호출 대부분이 실패해 Apollo 호출이 21번에 1번꼴로만 성공했다.
Redis Cluster 서버는 노드에 닿을 수 없게 되면 곧바로 다른 노드에 알리지만, Go-Redis 의 Redis Cluster 클라이언트는 노드 상태를 1분에 한 번만 새로 읽는다. 그래서 그 1분 동안 클라이언트가 닿을 수 없는 슬레이브 노드로 질의를 계속 보냈고, Apollo 가 회복하는 데 1분 가까이 걸렸다. 쓰기 질의는 마스터 노드로만 가고 클라이언트가 마스터 노드를 감시하므로 그 1분 동안 새 데이터를 잃지는 않았다.
- 심각도
- –
- 영향 범위
- Apollo, Grab's driver-side state machine on the critical path of transport bookings: about 1 in 21 calls succeeded for one minute and Grab rides were significantly down
- 영향 사용자
- –
- 지역
- –
- 감지까지
- –
- 복구까지
- 1m
증상
2019년 2월 15일 Apollo 가 쓰는 Redis Cluster 의 슬레이브 노드 하나가 죽어 교체해야 했다. 그동안 운송 예약의 핵심 서비스인 Apollo 로 가는 호출이 대략 21번에 1번꼴로만 성공했고, Redis Cluster 가 스스로 복구하는 1분 동안 Grab 의 승차 이용이 크게 줄었다.
슬레이브 노드 하나가 죽었는데 Redis Cluster 로 가는 호출 가운데 95% 넘는 호출이 실패했다. 샤드마다 복제본을 여럿 둔 의도와 완전히 어긋나는, 전혀 예상하지 못한 동작이었다.
장애 동안 일부 코드가 파이프라인 GET 호출에서 오류를 많이 냈다. Apollo 가 Redis Cluster 에 HMGET 호출을 파이프라인으로 보내면 파이프라인이 오류를 냈다.
Redis Cluster 서버는 노드 하나에 닿을 수 없게 되면 곧바로 다른 노드에 알린다. 이론대로라면 Redis Cluster 는 100% 가용해야 했으므로 Grab 은 서버 쪽은 원인이 아니라고 결론 내리고 클라이언트 쪽과 Apollo 의 질의를 살폈다.
원인
- 촉발
- 2019년 2월 15일 Apollo 가 쓰는 AWS ElastiCache Redis Cluster 의 슬레이브 노드 하나가 죽었다.
감지
–
조치
–
후속 조치
–
교훈
Redis Cluster 는 서비스 성능을 높이는 좋은 도구지만 쓰는 데 위험이 있다. 파이프라인 전용 Redis Cluster 클라이언트를 따로 만들고 RouteByLatency 를 true 로 두면, 지연이 1ms 아래일 때 파이프라인 읽기가 마스터 노드로 가서 파티션의 과반이 살아 있는 한 기대한 결과를 받는다. 이렇게 하면 마스터로 가는 부하가 늘어나므로 마스터 노드의 CPU 사용량을 살펴야 한다. 마스터가 그만한 트래픽을 감당하지 못하면, 파이프라인 호출에 오류가 나더라도 질의마다 오류를 따로 확인하면 된다. 그러면 성공한 질의는 영향을 받지 않아 피해가 줄어든다. Go-Redis 라이브러리는 오류가 나면 노드 상태를 다시 읽도록 고칠 수 있다. defaultProcessor 에 있는 이 로직을 defaultProcessPipeline 에 적용하면 된다.
출처
관련 사례
같은 원인 · 공통 인메모리 캐시
IL-0216데이터센터 전환 직후 CirrusSearch Elasticsearch 지연이 치솟아 위키 검색이 일부 사용자에게 늦거나 안 됐다Wikimedia Foundation · 2023같은 원인 · 공통 인메모리 캐시
같은 원인 · 공통 인메모리 캐시
같은 원인 · 공통 인메모리 캐시
같은 원인 · 공통 인메모리 캐시
IL-0388Redis 고가용 클러스터의 primary 노드가 내려간 뒤 Discord API 가 느려지고 길드 일부가 오프라인으로 보이며 접속이 실패했다Discord · 2017같은 원인 · 공통 인메모리 캐시
내 클러스터에도 있는지 확인하기
AI 도구와 스크립트용
- 이 사례의 JSON: https://incidentlib-api.benchlet.com/cases/IL-0279
- 가장 비슷한 사례들: https://incidentlib-api.benchlet.com/cases/IL-0279/similar
- MCP 서버(Streamable HTTP, 키 없음):
https://incidentlib-mcp.benchlet.com/mcp
API 는 키가 필요 없습니다. MCP 서버는 search_incidents, get_incident, list_incidents, get_topic, vocab 도구를 내놓습니다. 사용법은 API 와 MCP.