로컬 우선 아키텍처와 기존 Postgres 읽기 경로의 병목
협업 도구 Linear는 즉각적인 사용자 반응성을 제공하기 위해 각 클라이언트에 로컬 데이터베이스를 유지하는 로컬 우선(Local-first) 아키텍처를 채택하고 있습니다. 사용자가 오프라인 상태에서 복귀할 때 전체 워크스페이스를 다시 내려받는 대신, 마지막 체크포인트 이후 변경된 로그만 내려받는 델타 동기화(Delta Sync)를 사용합니다. 각 변경 사항은 Postgres의 쓰기 로그보다 높은 애플리케이션 수준의 불변 순서 로그 형태인 '동기화 액션(Sync Action)'으로 관리되며, 누적 데이터양은 시스템 전체 기준 20TB를 상회합니다.
하지만 대규모 워크스페이스의 경우 하루에만 100만 건에 달하는 동기화 액션이 누적되고, 몇 시간만 자리를 비워도 수십만 건의 변경 사항이 발생하면서 기존 서빙 구조가 한계에 부딪혔습니다. 기존에는 읽기 전용 보조 Postgres 테이블에서 넓은 ID 범위와 권한 및 구독을 확인하는 배열 겹침(Array-overlap) 조건을 결합해 조회했습니다. 이로 인해 Postgres가 수많은 무관한 행을 일일이 읽고 버리는 과정에서 과도한 CPU 연산을 소모했고, 중앙값(p50) 지연시간은 안정적이었으나 꼬리 지연시간(p95/p99)이 워크스페이스 규모에 비례해 급격히 치솟는 문제가 발생했습니다.
역색인 포스팅 리스트를 통한 질의 연산 구조 전환
Linear 엔지니어링 팀은 복제본 증설이나 Postgres GIN 인덱스 적용만으로는 대규모 집합 교차 연산의 쓰기 비용과 질의 구조 한계를 극복할 수 없다고 판단했습니다. 이에 따라 대규모 필터 교차 연산에 최적화된 역색인(Inverted Index) 구조를 지닌 turbopuffer를 읽기 경로에 새로 도입했습니다. turbopuffer는 속성값마다 정렬된 문서 ID 목록인 '포스팅 리스트(Posting List)'를 유지하는 구조로 동작합니다.
새로운 아키텍처에서 문서 ID는 동기화 액션 ID를 의미하며, 워크스페이스의 접근 권한 그룹(Sync Group)과 구독 범위(Sync Subscription)가 각각 포스팅 리스트를 구성합니다. 클라이언트가 변경 사항을 요청하면 시스템은 사용자가 속한 권한 그룹과 구독 항목의 포스팅 리스트를 각각 합집합(Union) 처리한 뒤, 요청된 ID 범위와 최종 교차(Intersection) 연산을 수행합니다. 기존 Postgres가 후보 행을 전수 검사하던 방식에서, 이미 정렬된 ID 집합들을 직접 결합해 작은 교집합을 즉시 도출하는 형태로 질의 형식이 전면 재구성되었습니다.
CDC 파이프라인과 2단계 읽기 및 지연 결합 설계
새로운 읽기 파이프라인은 Postgres 퍼블리케이션에서 커밋된 동기화 액션을 감지하는 자체 CDC(Change Data Capture) 시스템을 기반으로 구성되었습니다. Postgres에 커밋된 액션의 메타데이터가 turbopuffer에 반영되기까지의 복제 지연시간은 p50 기준 약 1초, p95 기준 수 초 수준을 기록합니다. 전체 데이터세트를 메모리에 상주시키지 않고 객체 스토리지 기반으로 인덱스를 확장할 수 있도록, 대용량 실제 페이로드는 Postgres에 남겨두고 필터링에 필요한 메타데이터만 turbopuffer에 색인했습니다.
읽기 단계는 '메타데이터 스캔'과 '후기 페이로드 결합(Late Enrichment)'의 2단계 파이프라인으로 나뉩니다. 먼저 turbopuffer에서 정렬된 ID와 라우팅 메타데이터만 빠르게 스캔해 서버 메모리에서 접근 권한 검사, 구독 필터링, 중복 제거를 완료합니다. 그 후 최종 통과한 동기화 액션 ID들만 배치(Batch)로 묶어 Postgres에서 실제 본문 페이로드를 가져와 클라이언트에 스트리밍합니다. 이를 통해 탈락될 후보 행의 불필요한 I/O와 JSON 역직렬화 오버헤드를 원천 차단했습니다.
데이터 정합성 메커니즘과 아키텍처 트레이드오프 분석
turbopuffer 도입 과정에서 가장 중요한 과제는 CDC 복제 지연 시간 동안 발생하는 최신성 공백을 해결하는 것이었습니다. CDC 지연이 수 초에 달하기 때문에 색인 엔진 단독으로는 방금 발생한 변경 사항을 즉시 반영하지 못합니다. Linear는 최신 커밋이 위치하는 헤드(Head) 구간은 원천 데이터베이스인 Postgres에서 직접 읽고, 과거 대규모 구간은 turbopuffer에서 읽은 뒤 양쪽 결과를 액션 ID 기준으로 중복 제거하여 일관성을 보장했습니다.
또한 색인 시스템 장애나 지원 범위를 벗어난 질의가 발생할 경우 전체 요청을 기존 Postgres 경로로 자동 폴백하는 안전장치를 마련했습니다. 객체 스토리지 기반 분산 색인은 대규모 교차 연산의 CPU 부하를 제거하는 데 탁월하지만, 콜드 캐시 상태에서는 스토리지 왕복 지연이 발생할 수 있는 구조적 특성을 지닙니다. 따라서 무조건적인 지연시간 단축보다는 초대형 워크스페이스에서도 예측 가능한 꼬리 지연시간 상한을 확보했다는 점이 이번 전환의 핵심 성과로 해석됩니다.
실무 적용 시사점 및 미공개 지표에 관한 유의점
Linear의 이번 개편 사례는 관계형 데이터베이스의 단일 테이블로 감당하기 어려운 다차원 권한 집합 연산을 검색 엔진의 역색인으로 분리해 해결한 대표적인 실무 모범 사례입니다. 특히 전체 페이로드를 검색 엔진으로 완전히 이전하지 않고, 메타데이터 필터링과 주 저장소 본문 조회를 분리하는 '지연 결합' 방식을 통해 스토리지 비용과 동기화 복잡성을 크게 줄인 점은 로컬 우선 앱을 구축하는 엔지니어링 조직에 중요한 설계 지침을 제공합니다.
다만 실무 적용 검토 시 확인해야 할 한계와 미공개 영역이 존재합니다. Linear는 대규모 워크스페이스에서의 p95/p99 지연시간 안정성을 그래프로 제시했으나, 구체적인 절대 지연시간(ms 단위 수치)과 델타 동기화 파이프라인 단독의 인프라 비용 절감률 수치는 외부에 구체적으로 밝히지 않았습니다. 또한 이 아키텍처는 데이터가 불변(Immutable) 추가 로그 형태일 때 가장 효율적이므로, 잦은 수정과 삭제가 발생하는 가변 데이터 모델에 동일한 구조를 적용할 때는 추가적인 쓰기 오버헤드를 면밀히 검증해야 합니다.
출처
Linear의 원출처와 본문에 표시한 참고자료를 바탕으로 한국어로 재구성했습니다. ‘ARCH.B 분석’은 자료를 읽고 덧붙인 편집 해석입니다.
Rebuilding Linear’s delta sync read path (새 창)