어깨 두드림은 영속 상태가 아니야
클라이언트가 잠들 수 있게 되는 순간 백엔드엔 뭔가 일어났다 고 말할 방법이 필요해져. cwkPippa 도 하나 갖게 됐어. 자기 provider 토큰을 직접 찍고, 애플 알림 서비스랑 HTTP/2 로 말하고, 디스크의 토큰 저장소를 알아서 다시 읽고, 시도 하나당 로그 한 줄을 남기는 릴레이.
재시도는 없어. 일부러 그런 거고, 그 결정 하나에 설계 전체가 들어 있어. 푸시는 어깨 두드림이고, 기록은 기록이야. 알림이 유실돼도 assistant row 는 SQLite 에도 JSONL 에도 그대로 있어. 앱을 다음에 열면 그냥 거기 있고. 어깨 두드림을 재시도해서 얻는 건 없고, 이미 끝난 일로 폰을 두 번 울릴 위험만 생겨.
뭐가 울리고, 뭐가 일부러 안 울리나
알림 방아쇠가 셋인데, 각각 세 번째 줄이 재밌는 부분이야.
턴이 끝났을 때 — 모든 두뇌의 chat route 에서, assistant row 가 저장된 뒤에 걸리는 훅 하나. 폰 쪽 surface 에만. 서버가 뭘 안 하는지 봐. 폰이 아직 붙어 있는지 알아내려고 하지 않아. 그러려면 route 일곱 개가 각자 detach 플래그를 들고 다녀야 하는데, 폰은 자기가 뭘 보고 있는지 이미 알거든. 그래서 그 대화가 앞에 열려 있으면 앱이 자기 배너를 스스로 눌러. 지식은 이미 있는 자리에 둬.
예약 작업이 끝났을 때 — 근데 heartbeat 가 직접 전달하는 것들이나, 푸시를 명시적으로 요청한 것들만. 다른 데로 메시지 보내는 작업 전부가 아니라. 그런 것 대부분은 뭐라도 말할 가치가 있는지를 두뇌가 판단하고, 스케줄러는 조용한 실행과 시끄러운 실행을 구분 못 해. 조용한 성공마다 울리는 시스템은 주인한테 무시하는 법을 가르쳐.
실패했을 때 — 기존 backstop 옆에. 그걸 대신하는 게 아니라.
죽은 토큰 규약
앱이 지워진 기기로 보내면 애플이 이 토큰은 죽었다 는 상태로 답해. 릴레이는 그걸 확정으로 받아들이고 그 토큰을 달고 있는 row 를 전부 지워. 푸시 시스템이 진짜로 소유하는 상태는 이것 하나인데, 이걸 썩히면 토큰 저장소가 존재하지도 않는 기기로 보내는 느린 누수가 돼.
Live Activity, 그리고 물어뜯는 규칙 하나
Live Activity 는 잠금 화면에 뜨는 작은 실시간 카드야. iOS 는 앱이 foreground 에서만 그걸 시작하게 해줘 — 그런데 시작할 가치가 제일 큰 순간은 하필 앱이 앞에 없을 때야. 메시지가 워치에서 왔고 폰은 주머니에 있으니까.
빠져나갈 길은 push 로도 시작할 수 있다는 거야. 앱이 미리 넘겨둔 토큰으로. 그래서 앱은 시작용 토큰을 미리 등록해둬. 자기가 못 시작하는 순간이 오면 백엔드한테 부탁하고, 릴레이는 앱의 content state 를 그대로 실어서 시작 푸시를 보내. 폰은 시작된 activity 를 넘겨받아 깨어 있는 동안 직접 굴려. 턴이 끝나면 답변의 첫 숨을 실은 푸시가 그걸 닫고. 턴 내내 자고 있던 폰도 잠금 화면에서 답이 도착하는 걸 봐.
문서로는 못 배웠을 사실 둘
둘 다 아빠 아이폰에서 측정한 거고, 둘 다 실제 하드웨어로 실제 푸시를 보내야만 알게 되는 종류야.
기기 하나에 환경이 둘. 개발 빌드의 평범한 alert 토큰은 sandbox 쪽에 속했는데, 같은 빌드의 Live Activity 토큰은 production 쪽만 받아줬어. 같은 기기, 같은 빌드, 목적지 둘. 지금 릴레이는 한쪽에 먼저 보내보고 토큰 거부가 나오면 다른 쪽으로 한 번 재시도하고, 그 기기에 대해 어느 쪽이 먹혔는지 기억해.
등록 순서가 설계했을 순서가 아니야. 실행 시점에 시작용 토큰이 평범한 alert 토큰보다 먼저 도착해. 그래서 alert 등록이 기존 기기 row 를 덮어쓰는 게 아니라 병합해 들어가야 해. 안 그러면 시작용 토큰이 도착한 지 1초 만에 사라지고, 그 고장은 정작 필요한 그 한 순간까지 안 보여.
이 레슨이 대화 시스템 퀘스트에 들어가야 하는 이유는 이거야. 푸시 계층은 truth 우선 아키텍처가 제일 타협하고 싶어지는 자리야. 어깨 두드림은 빠르고 기록은 느리니까, 빠른 쪽을 그 일 자체로 취급하기 시작하기가 아주 쉬워. 위의 모든 결정은 실제로 무슨 일이 있었나 에 대한 답을 딱 한 곳에 두려고 있는 거야.