4화

429

한유리 시점

유리 회의실에서 트래픽 대시보드를 열어 둔 채 옛 동료에게 전화하는 유리

1

3월 4일 목요일 오후 한 시 오십삼 분, 한유리는 판교의 사무실에서 온콜 알림을 받았다.

[P2] account paybrick-prod · 429 ratio 79% (30s) · shard kr-b latency p99 2.1s

스위치보드에 입사한 지 석 달째였다. 피처 플래그 SaaS의 솔루션 엔지니어. 고객사가 SDK를 잘못 쓰면 고쳐주고, 고객사가 장애라고 하면 우리 장애인지 고객 장애인지 가려내는 일이었다. 면접에서 전 직장 퇴사 사유를 물었을 때 유리는 “제가 낸 장애 때문에요”라고 대답했고, 면접관은 “그런 사람이 이 일을 잘합니다”라고 했다. 농담인 줄 알았는데 아니었다.

paybrick-prod. 유리는 그 계정 이름을 알았다. 반년 전까지 그 계정의 sre-admin이었으니까.

대시보드를 열었다. 계정의 초당 요청 수가 13:52:30에 300에서 4,800으로 뛰어 있었다. 계정 한도는 1,000. 한도를 넘는 요청은 전부 429 Too Many Requests로 튕긴다. 그리고 429를 받은 SDK는 마지막으로 캐시한 값을 쓰다가, 캐시 TTL이 끝나면 코드에 박힌 기본값으로 떨어진다. 결제 서비스의 플래그 기본값은 대부분 false였다. 유리가 만든 규칙이었다. “모르면 끄는 쪽이 안전하다.”

요청의 출처는 SDK 키별로 나뉘어 있었다. 4,800 중 4,500이 키 하나에서 오고 있었다. 키 이름은 atlas-gateway. 생성일 2월 20일. 유리가 퇴사한 뒤에 생긴 키였다.

나머지 300은 유리가 아는 키들이었다. payments-api, coupon-api, settlement-batch. 그 키들의 최근 갱신 요청도 연달아 429를 받고 있었다. 계정 한도 안에 서비스별 몫은 없었다. atlas-gateway가 매 제한 구간의 앞부분을 먼저 채우고 있었다.

그러니까 지금 그 회사에서는 결제 플래그가 전부 기본값으로 떨어지고 있을 것이었다. coupon-inventory-cache가 false로. 11월에 31분 동안 무슨 일이 있었는지 유리는 도현의 짧은 메시지로 들어서 알고 있었다.

2

14:05, 유리의 팀장 오태경이 상태 페이지를 업데이트했다.

kr-b 리전 일부 고객 API 지연 · 조사 중

거짓말은 아니었다. paybrick-prod가 붙어 있는 샤드 kr-b는 실제로 느려져 있었다. 4,800 요청 중 3,800을 429로 튕기는 것도 일이었고, 그 일이 같은 샤드의 다른 고객 셋에게 지연으로 새어 나가고 있었다. 벤더 입장에서는 degraded가 맞았다. 원인이 고객이라는 것과 벤더가 느리다는 것은 동시에 참일 수 있었다.

14:11, 티켓이 들어왔다.

[긴급] paybrick-prod 플래그 평가 전면 실패 · 귀사 상태 페이지 확인함. 당사 결제 서비스 전면 장애 중. 복구 예상 시각과 SLA 크레딧 산정 기준 회신 바람. · 강민석, 플랫폼본부장

유리는 그 문장을 읽으면서 민석의 얼굴을 떠올렸다. 9월 회의실에서 “블레임리스”를 말하던 얼굴과, 1월에 자기가 쓴 포스트모템을 읽었다는 그 얼굴. 후자는 도현에게 들었다. 민석은 지금 벤더 장애라고 믿고 있었다. 상태 페이지가 그렇게 말하고 있으니까.

“유리 씨, 이 티켓 유리 씨가 받아요.” 태경이 말했다. “우리 degraded 맞으니까 그렇게 답해요. 조사 중이고, 복구 중이고. 원인은 나중에.”

“원인이 고객이에요.”

“알아요. 근데 지금 그 얘기 하면 싸움 나요. 우리 상태 페이지에 노란불 켜져 있는데 ‘사실 당신들 때문입니다’라고 하면 어떻게 되는지 알죠? 확인되고 나서, 인시던트 끝나고 나서, 정식 리포트로 하는 거예요.”

“확인됐어요. 13:52:30에 atlas-gateway 키 하나가 4,500을 쏘기 시작했고, 그게 한도를 다 먹어서 나머지 키가 굶고 있어요. 저 화면 지금 보고 있어요.”

“그건 우리 화면이고요. 고객 데이터를 고객한테 ‘당신 서비스 하나가 이렇게 쏘고 있다’고 말하는 건 정식 채널로만 해요. 규정이에요.”

유리는 규정이라는 말에 잠깐 멈췄다. 입사 때 읽은 고객 연락 절차가 떠올랐지만, 지금 태경에게 어느 항목이냐고 묻지는 못했다. 모니터에서는 실패 건수가 계속 올라가고 있었다.

“티켓은 규정대로 쓸게요.” 유리가 말했다.

3

14:19, 유리는 회의실로 들어가 개인 휴대폰을 꺼냈다. 지안의 번호는 지우지 않았었다.

세 번째 신호에 받았다.

“유리 씨.” 지안의 목소리 뒤로 브리지 콜 소리가 들렸다. “지금 좀.”

“압니다. 결제 죽어 있죠. 30초만요.”

“…말해요.”

“저 지금 스위치보드에 있어요. 솔루션 엔지니어요. 그 이상은 말 못 해요. 하나만 물을게요. 아틀라스라는 서비스, 오늘 배포했어요?”

침묵. 브리지 콜 소리가 멀어졌다. 지안이 자리를 옮기는 것 같았다.

“…13:52에요. 그걸 어떻게.”

“SDK 폴링 간격 확인해 보세요. 그리고 계정 한도가 계정 단위라는 것도요.”

유리는 전화를 끊었다. 손이 조금 떨렸다. 요청량 수치는 말하지 않았다. SDK 문서에 있는 설정만 짚었다. 그렇게 생각하려다가, 아틀라스를 골라 물은 이유는 자기 화면에만 있었다는 데서 멈췄다. 티켓에는 통화 기록이 없었다. 유리는 시각과 자기가 한 말을 메모장에 적었다.

14:26, paybrick-prod의 초당 요청 수가 4,800에서 320으로 떨어졌다. atlas-gateway 키의 요청이 0이 됐다. 롤백이었다.

14:29, 429 비율 0%. 14:33, 샤드 kr-b 지연 정상. 14:38, 유리는 상태 페이지에 “복구 완료”를 올려도 되겠냐고 태경에게 물었고, 태경은 올리라고 했다.

그 회사의 장애는 13:53부터 14:40까지, 47분이었다. 그중 유리의 전화 뒤는 21분이었다.

4

저녁 일곱 시, 태경이 유리를 회의실로 불렀다.

“고객이 어떻게 알았어요? 우리 상태 페이지만 보고 있던 고객이 14:26에 자기 서비스를 내렸어요. 티켓에는 아직 아무것도 안 썼는데.”

유리는 잠깐 거짓말을 생각했다. 그러다 9월을 생각했다. 2주 들고 있었더니 실수가 아니게 되더라는 말을 자기 입으로 했었다.

“제가 전화했어요. 전 직장 SRE 팀장한테요. 서비스 이름 하나 묻고, SDK 설정 보라고 했어요.” 유리는 메모장을 돌려 보여줬다. “티켓에는 아직 안 남겼어요.”

태경은 한참 아무 말도 하지 않았다.

“개인 연락은 절차 밖이에요. 등록된 담당자에게 티켓으로 연락하거나, 거기에 브리지 콜을 열었어야 해요. 다만.” 태경이 메모장을 자기 쪽으로 당겼다. “내가 아까 원인은 끝나고 말하라고 했죠. 너무 넓게 막았어요. 요청이 늘어난 시각하고 확인할 설정은 바로 보낼 수 있었는데.”

“조사 중이라고만 쓰라는 뜻인 줄 알았어요.”

“그렇게 들리게 말했어요. 초안을 같이 썼어야 했고요.” 태경은 티켓을 열었다. “지금이라도 통화 내용부터 남겨요. 나는 초기 안내를 보류시킨 경위를 적을게요.”

태경은 일어섰다.

“다음에는 이견 있으면 바로 말해요. 긴급 안내까지 내 검토를 기다리게 할지도 이번 회고에서 바꿉시다.”

유리는 회의실에 혼자 남아 티켓을 열었다. 민석의 문장 아래에 답을 썼다. 13:52:30, 계정 한도, 키 하나의 요청량, SDK 문서의 폴링 권장값, 스트리밍 모드 안내, 그리고 kr-b 샤드가 실제로 느려졌던 것에 대한 사과. 마지막 줄에는 “SLA 크레딧은 샤드 지연에 한해 산정하겠습니다”라고 썼다. 태경이 승인했다.

5

다음 주 화요일, 도현에게서 메시지가 왔다. 언제나처럼 짧았다.

포스트모템 끝. 아틀라스 폴링 100ms, 파드 450개. SDK 문서 안 읽음. 아틀라스 팀장이 직접 씀. 본부장님은 자기 줄 하나 추가함. “원인 확인 전에 SLA 티켓 보낸 사람은 나다.” 액션 아이템에 “벤더 계정 한도를 서비스별로 분리” 있음. 그거 유리 씨네 기능임?

유리는 답을 썼다.

있어요. 키별 한도. 티켓으로 요청하면 제가 받아요.

그리고 조금 뒤에 한 줄 더.

1월에 월 계산 수정 내용 보내준 거, 이제야 제대로 읽었어요. 고마워요.

답은 오지 않았다. 도현의 메시지는 대개 작업 얘기에서 끝났다. 유리도 더 쓰지 않았다.

목요일 오후에 지안에게서 메시지가 왔다.

열한 시 반. 그 집. 온콜 아니면.

유리는 그 메시지를 한참 봤다. 그러고는 답을 썼다.

이번 주 온콜이에요.

지안의 답은 1분 뒤에 왔다.

그럼 사이다.

6

목요일 밤 열한 시 사십 분, 유리는 회사 뒷골목 술집의 유리문 앞에 서 있었다. 회사라고 해도 이제는 남의 회사였다.

안쪽 테이블에 셋이 보였다. 지안, 도현, 그리고 민석. 민석이 있을 줄은 몰랐다. 도현이 잔을 채우고 있었다. 지안 먼저, 민석, 자기.

들어가지 말까, 하고 유리는 생각했다. 9월 회의실 이후로 지안을 본 적이 없었다. 민석은 더 없었다.

유리문을 열었다.

셋이 동시에 그녀를 봤다. 지안이 사장님에게 손짓을 했고, 사이다 한 병과 잔이 왔다. 유리가 자리에 앉기 전에 이미 와 있었다.

“온콜이라면서요.” 지안이 말했다.

“네.”

“그럼 마시지 마요. 규정이에요.”

“저희 회사 규정에는 없는데요.”

“그럼 내 규정.”

민석이 사이다 병을 집어 유리의 잔을 채웠다. 유리는 두 손으로 잔을 받았다. 도현은 자기 앞의 빈 접시를 옆으로 옮겨 그녀가 앉을 자리를 냈다. 눈은 마주치지 않았다.

“도현 씨.” 유리가 말했다. “그때 이름 찍히는 거 알면서 썼어요. 토큰이 그것밖에 없었다는 말, 회의에서 하지 말았어야 했어요.”

도현은 내려놓으려던 소주병을 잠깐 들고 있었다.

“로그에서 제 이름 보면 아직도 한 번 더 열어봐요.”

“…네.”

“오늘 오는 건 괜찮아요. 그 일은 아직 좀 그래요.”

유리는 알겠다고 했다. 사이다의 기포가 잔 벽에 붙어 있었다. 도현이 수저통을 그녀 앞으로 밀어줬다. 유리는 자기 젓가락 한 벌을 꺼냈다.

“티켓 잘 썼더라고요.” 민석이 말했다. “SLA 크레딧 샤드 지연에 한해서. 그거 우리 잘못 아니라는 말을 그렇게 하는 거더라고요.”

“처음부터 그렇게 썼으면 됐는데요.” 유리가 말했다. “통화한 것도 뒤늦게 붙였어요. 다음번 담당자는 티켓만 보고 알 수 있게요.”

지안이 웃었다. 유리가 처음 보는 웃음이었다.

TV에서는 소리를 죽인 야구 재방송이 나오고 있었다. 지난 시즌 경기였다. 이제 곧 올해 경기가 시작될 것이었다.