규칙이 정반대인 저장소 둘
폰엔 데이터가 두 종류 있고, 둘은 정반대 규칙을 따라. 레슨 3의 outbox는 직접 만든 데이터야. 아빠가 만들었는데 엔진이 아직 받았다고 확인 안 한 접시들. 그건 아무도 몰래 버리면 안 돼. 오프라인 선반엔 엔진이 이미 답한 내용을 담은, 버려도 되는 사본이 들어 있어. 접시 목록, 접시마다 상세, 사진 바이트, 트리, 클래스, 스레드. 선반 자기 문서 주석이 둘 사이 순서를 정해 둬. 사본은 버려도 되고, outbox는 직접 만든 데이터로 남고, 엔진이 확인한 바이트를 outbox가 내려놓으려면 사본 저장 영수증이 먼저 있어야 해.
사본 하나는 파일 하나야. 응답 본문과 저장한 순간이 같이 들어 있어서, 사본을 보여주는 화면은 그 사실을 밝힐 수 있어. Saved on device(기기에 저장됨)라고 쓰고 날짜와 시각을 붙이는 식이야. 깨진 파일은 없는 걸로 읽히고, 다음에 제대로 읽으면 그걸로 바뀌어.
엔진 하나에 묶여
선반 디렉터리 이름은 엔진의 기본 URL에서 만들어. 그래서 한 엔진에서 받은 사본은 폰이 다른 엔진이랑 말할 땐 절대 안 떠. 아빠가 설정에서 엔진 주소를 바꾸면, 앱은 새 주소를 저장하기 전에 옛 선반부터 은퇴시켜. 그리고 새 엔진용 선반을 열고, 이미지 캐시를 비우고, 그 선반이 이미 가진 걸로 접시와 트리를 되살려. 한 번도 안 붙어 본 엔진이면 선반은 비어 있어. 호스티드 테스트 하나가 두 번째 엔진으로 바꿔서 빈 화면을 확인하고, 다시 돌아와서 첫 엔진의 접시를 다시 찾아.
티켓과 세대
진짜 어려운 건 시간이야. 읽기는 느릴 수 있고, 그게 가는 동안 아빠가 바로 그 기록을 바꿀 수 있어. 늦은 답을 도착하는 대로 써 버리면, 기기 사본이 아빠의 수정을 조용히 되돌려 놓겠지. 선반은 카운터 둘과 잠금 하나로 그걸 막아.
begin(path)는 그 경로의 최신 읽기로 새 id를 기록하고, 그 id와 선반의 지금 세대 번호를 담은 티켓을 돌려줘.didMutate()는 세대 번호를 올려.retire()는 선반을 은퇴 상태로 표시하고 번호도 올려.isCurrent는 선반이 은퇴하지 않았고, 세대가 안 바뀌었고, 이 티켓이 아직 그 경로의 최신 읽기일 때만 참이야.- 티켓을 든 쓰기는 그 티켓이 유효할 때만 들어가.
EngineClient는 이 규칙을 두 군데에 써. 미러링하는 경로(접시 목록, 접시, 트리, 클래스, 스레드)를 읽을 땐 요청이 나가기 전에 티켓부터 받아. 답이 와서 디코딩되면 티켓부터 확인해. 낡았으면 취소를 던져서 화면이 원래 보여주던 걸 지키고, 유효하면 사본을 써. 변경은 반대야. 수정이든 채택이든 질문이든 엔진이 답하면, 클라이언트는 세대 번호를 올리고 변경 자기 답을 선반에 바로 써. 바뀐 접시는 저장된 목록 안의 자기 줄도 고쳐.
그래서 가장 새로운 진실은 늘 변경에 대한 답이고, 그보다 먼저 시작한 읽기는 다 거절돼. 같은 경로를 나중에 읽으면 먼저 읽은 것도 밀려나. begin마다 그 경로의 읽기 id를 갈아 끼우니까. 호스티드 테스트 둘이 두 경우를 다 박아 둬. 쓰기 뒤에 끝난 느린 목록 읽기는 기기 기록을 못 지우고, 옛 엔진에서 늦게 온 답은 새 엔진 화면을 못 채워.
동기화는 읽기만 하고 묻지 않아
앱이 활성이면 루프가 60초마다 syncOffline을 돌려. 먼저 outbox를 비워. 접시 목록 읽기가 성공했으면 트리를 읽고, 목록의 접시마다 상세를, 사진마다 썸네일과 화면용 바이트를 읽어. 이어서 트리와 접시 링크에서 닿는 클래스를 부모와 자식을 따라 새 게 없을 때까지 읽고, 마지막으로 스레드를 읽어. 키친, 모든 접시의 스레드, 다녀온 모든 클래스의 스레드. 도착한 건 뒤에서 실패가 나도 하나하나 남아.
그 호출이 전부 GET이야. 오프라인 콜드 스타트를 준비하는 테스트가 바로 그걸 확인해. 동기화가 피파한테 새 읽기나 새 답을 부탁하면 절대 안 되니까. 사진은 한번 저장되면 안 바뀌니까, 저장된 이미지는 바이트가 이미지로 읽히는 한 요청 없이 그대로 돌려줘.
페이지도 거짓말 못 해
접시 목록은 500개씩 페이지로 와. 클라이언트는 짧은 페이지가 올 때까지 계속 넘겨. 뒤 페이지가 이미 가진 id만 되풀이하면, 저장된 사본은 그대로 뒀다는 메시지를 남기고 멈춰. 목록 전체는 모든 페이지가 도착한 뒤에, 그것도 유효한 티켓으로만 선반에 써. 두 번째 페이지가 중간에 실패하면 이전의 완전한 사본이 그대로 남아. 테스트는 접시 501개로 그걸 증명해.
outbox는 사본이 생긴 뒤에 물러나
엔진이 새 접시를 받았다고 확인하면, drain의 보내기는 세대 번호를 올리고, 접시 상세를 저장하고, 올린 사진을 전부 선반에 써. 사진은 캡처 자기 경로에도 들어가고, 엔진 사진 id 아래 썸네일과 화면용 사본으로도 들어가. 그건 폰이 직접 줄인 바이트라서, 새 접시는 바로 오프라인에서도 보여. 그중 하나라도 못 쓰면, 엔진은 접시를 받았는데 기기 사본을 못 저장했다는 일시 실패로 보고해. 접시는 큐에 남고, 다음 패스는 엔진이 저장된 접시로 답하는 재전송이 돼. 웹엔 이런 게 하나도 없어. 살아 있는 답만 보여주고, 로컬 저장소엔 Sidekick 설정만 들어 있어. 열려 있었는지, 어떤 모드였는지, 그리고 프레임 크기 셋(옆 패널 폭, 아래 독 높이, 떠 있는 창 크기). 접시나 읽기는 하나도 안 들어가.