본문 바로가기
WIKI 기술 지식 베이스

Partial sync

원문 보기 위키 갱신

필요한 것만 동기화하는 부분 동기화(partial sync) 기능이에요. 데이터베이스 페이지를 필요할 때마다(lazily) 가져와서 콜드 스타트를 빠르게 하고 대역폭을 줄여 줘요.

출처: 문서

본문

이 사용법은 로컬 Turso 데이터베이스를 동기화하기 위해 Turso Cloud를 사용하며, 계정이 있다고 가정해요.

부분 동기화를 쓰면 파일 전체를 내려받지 않고도 애플리케이션이 데이터베이스를 열고 사용할 수 있어요. 클라이언트는 쿼리가 로컬에 없는 데이터를 만졌을 때, 그 시점에 Turso Cloud에서 데이터베이스 파일의 페이지를 가져와요(lazily fetch). 덕분에 큰 데이터베이스에서 시작 시간과 네트워크 사용량이 줄어들면서도, Turso의 표준 sync 솔루션이 사용하는 push/pull 메서드와 완전히 호환돼요.

* 아직 내려받지 않은 데이터를 읽으면 투명하게 온디맨드 페이지 가져오기가 발동해요. * 쓰기는 여전히 먼저 로컬에 적용되고 논리적 문장(logical statement) 형태로 푸시돼요.

Modes

연결 시점에 로컬에 무엇이 준비될지는 두 가지 부트스트랩 전략으로 정해져요.

  • Prefix bootstrap: 데이터베이스 파일의 첫 N바이트를 내려받아요.
    • 최소한의, 예측 가능한 시작 풋프린트를 원할 때 좋은 기본 선택이에요.
  • Query bootstrap: 서버 쪽 SQL 쿼리를 실행해서 그 쿼리가 만지는 페이지들을 내려받아요.
    • 좁은 작업 집합(예: 특정 사용자의 행, 메타데이터나 참조가 담긴 작은 테이블)만 채워 넣고 싶을 때 이상적이에요.

두 모드 모두 이후에도 없는 페이지를 필요할 때 계속 가져와요.

Prefix bootstrap

```ts TypeScript import { connect } from '@tursodatabase/sync';

const db = await connect({ path: './app.db', url: 'turso://...', authToken: process.env.TURSO_AUTH_TOKEN, partialSyncExperimental: { bootstrapStrategy: { kind: 'prefix', length: 128 * 1024 }, // 128 KiB }, });


```py Python
import os
import turso.sync

conn = turso.sync.connect(
    path="./app.db",
    remote_url="turso://...",
    auth_token=os.environ["TURSO_AUTH_TOKEN"],
    partial_sync_experimental=turso.sync.PartialSyncOpts(
        bootstrap_strategy=turso.sync.PartialSyncPrefixBootstrap(length=128 * 1024),
    ),
)
import (
\tturso "turso.tech/database/tursogo"
)

db, err := turso.NewTursoSyncDb(context.Background(), turso.TursoSyncDbConfig{
  Path:      "./app.db",
  RemoteUrl: "turso://...",
  AuthToken: os.Getenv("TURSO_AUTH_TOKEN"),
  PartialSyncExperimental: turso.TursoPartialSyncConfig{
    BootstrapStrategyPrefix: 128 * 1024, // 128 KiB
  },
})

Query bootstrap

```ts TypeScript import { connect } from '@tursodatabase/sync';

const db = await connect({ path: './app.db', url: 'turso://...', authToken: process.env.TURSO_AUTH_TOKEN, partialSyncExperimental: { bootstrapStrategy: { kind: 'query', query: SELECT * FROM messages WHERE user_id = 'u_123' LIMIT 100, }, }, });


```py Python
import turso.sync

conn = turso.sync.connect(
    path=":memory:",
    remote_url="turso://...",
    partial_sync_experimental=turso.sync.PartialSyncOpts(
        bootstrap_strategy=turso.sync.PartialSyncQueryBootstrap(
            query="SELECT * FROM messages WHERE user_id = 'u_123' LIMIT 100"
        ),
    ),
)
import (
\tturso "turso.tech/database/tursogo"
)

db, err := turso.NewTursoSyncDb(context.Background(), turso.TursoSyncDbConfig{
  Path:      "./app.db",
  RemoteUrl: "turso://...",
  AuthToken: os.Getenv("TURSO_AUTH_TOKEN"),
  PartialSyncExperimental: turso.TursoPartialSyncConfig{
    BootstrapStrategyQuery: "SELECT * FROM messages WHERE user_id = 'u_123' LIMIT 100",
  },
})

Optimizations

Segment size (batched lazy reads)

원본 문서에는 인터랙티브 시각화가 있어요. 페이지 12개 중 3개가 캐시된 상태에서 페이지 7을 읽으면 세그먼트 5-8(새 페이지 3개)을 한 번에 가져오는 모습을 보여 줘요. — "Read page 7 → fetch segment 5-8 (3 new pages)"

클라이언트가 로컬에 없는 페이지가 필요하면 부분 동기화는 원격 데이터베이스에서 온디맨드로 가져와요.

왕복 횟수를 줄이고 가져오기를 빠르게 하려면 **세그먼트 크기(segment size)**를 설정할 수 있어요. 페이지 하나만 요청하는 대신, 클라이언트가 요청 한 번으로 페이지의 세그먼트 전체를 내려받는 방식이에요.

덕분에 네트워크 오버헤드를 분산(amortize)하고 곧 접근할 가능성이 높은 근처 페이지들을 미리 채워 넣을 수 있어요.

How it works

데이터베이스가 다음과 같다고 해 볼게요.

  • page_size = 4 KiB
  • segment_size = 16 KiB

로컬 쿼리가 페이지 6을 만지면 클라이언트는 그 페이지가 속한 세그먼트를 계산해요.

  • 16 KiB 세그먼트 = 4개 페이지
  • 페이지 6을 덮는 세그먼트 = 페이지 5-8

그러면 클라이언트는 네 페이지 전부를 요청 한 번으로 가져와 로컬에 저장해요. 그 페이지들을 다시 읽을 때는 추가 네트워크 비용이 들지 않아요.

Benefits

  • HTTP 요청 수 감소 (페이지 하나씩 여러 번 대신 세그먼트 한 번)
  • 자주 쓰는 범위(hot range)를 더 빠르게 채워 넣기
  • 공간적 지역성(spatial locality)이 있는 워크로드(예: 범위 스캔, 인덱스 조회)에서 더 나은 성능

Default

기본 segment_size는 128 KiB(4 KiB 페이지 크기 기준 보통 32개 페이지)예요. 요청 오버헤드와 전송 총 바이트 사이에서 좋은 균형을 제공해요.

워크로드가 촘촘히 모인 데이터를 만진다면(예: 인접한 여러 행 읽기) 세그먼트 크기를 키우는 것이 성능을 크게 개선할 수 있어요.

반대로 아주 드문드문한/무작위 접근 워크로드라면 세그먼트 크기를 줄이는 편이 유리할 수 있어요.

```ts TypeScript await connect({ ... partialSyncExperimental: { bootstrapStrategy: { kind: 'prefix', length: 128 * 1024 }, // 128 KiB segmentSize: 16 * 1024, }, }); ```
turso.sync.connect(
    ...
    partial_sync_experimental=turso.sync.PartialSyncOpts(
        bootstrap_strategy=turso.sync.PartialSyncPrefixBootstrap(length=128 * 1024),
        segment_size=16 * 1024,
    ),
)
turso.NewTursoSyncDb(context.Background(), turso.TursoSyncDbConfig{
  ...
  PartialSyncExperimental: turso.TursoPartialSyncConfig{
    BootstrapStrategyPrefix: 128 * 1024, // 128 KiB
    SegmentSize: 16 * 1024,
  },
})

Prefetch

원본 문서에는 인터랙티브 시각화가 있어요. 페이지 4를 읽으면 내부 B-트리 노드가 참조하는 자식 페이지 2개(11, 12)를 미리 내려받는 모습을 보여 줘요. — "Read page 4 → prefetch 2 child pages (11, 12)"

Prefetch는 지연 페이지 가져오기 위에 얹는 선택적 최적화예요. 켜 두면 클라이언트가 현재 쿼리에 필요한 페이지만 가져오는 게 아니라, 새로 내려받은 페이지의 구조와 최근 접근 패턴을 살펴서 다음에 필요할 가능성이 높은 페이지를 예측해요.

클라이언트가 접근 패턴의 자연스러운 연속(예: 내부 B-트리 노드가 참조하는 자식 페이지)을 감지하면 그 페이지들을 미리 다운로드해요. 이후의 온디맨드 가져오기 횟수를 줄여 주고, 범위 스캔·인덱스 순회·순차 조회 같은 연산 중 멈춤(stall)을 피하는 데 도움이 돼요.

```ts TypeScript await connect({ ... partialSyncExperimental: { bootstrapStrategy: { kind: 'prefix', length: 128 * 1024 }, // 128 KiB prefetch: true, }, }); ```
turso.sync.connect(
    ...
    partial_sync_experimental=turso.sync.PartialSyncOpts(
        bootstrap_strategy=turso.sync.PartialSyncPrefixBootstrap(length=128 * 1024),
        prefetch=True,
    ),
)
turso.NewTursoSyncDb(context.Background(), turso.TursoSyncDbConfig{
  ...
  PartialSyncExperimental: turso.TursoPartialSyncConfig{
    BootstrapStrategyPrefix: 128 * 1024, // 128 KiB
    Prefetch: true,
  },
})
`segment_size`와 `prefetch`는 서로 보완적이에요.

세그먼트 크기는 가까운 페이지들을 한 번의 온디맨드 가져오기로 묶어 주고, prefetch는 쿼리의 접근 패턴을 보고 다음에 필요할 가능성이 높은 추가 페이지를 미리 가져와요.

둘을 함께 쓰면 실제 워크로드에서 가장 좋은 성능을 낼 수 있어요.

더 알아보기 (Learn more)