Syncing: 인덱스 재동기화

Syncing: 인덱스 재동기화

디렉토리에 파일을 추가·수정·삭제했다면, 그 변경을 인덱스에 반영해야 해요. Syncing은 변경된 파일을 다시 파싱하고 갱신된 청크를 벡터 저장소로 다시 내보내는 작업이에요. 인덱스를 처음 만들 때는 초기 동기화가 자동으로 실행되고, 이후 갱신부터는 sync 엔드포인트를 직접 호출해요.

출처: 공식문서

동기화 트리거

await client.beta.indexes.sync("<your-index-id>")

동기화는 비동기로 실행돼요. 완료 시점을 알려면 인덱스를 폴링해야 하는데, 아래 '일반적인 워크플로우'에서 경합에 안전한 폴링 루프를 보여드릴게요.

한 인덱스에는 한 번에 하나의 동기화만 실행될 수 있어요. 이미 진행 중이면 API가 409 Conflict를 반환하고, 연속 호출은 429 Too Many Requests로 제한돼요. 동기화는 한 번만 트리거하고 인덱스 상태를 폴링하세요. 반복문에서 sync 엔드포인트를 부르면 안 돼요.

중요한 함정이 하나 있어요. 동기화 호출이 거부되면(409·429), 이미 돌고 있던 동기화가 파일 추가 전에 시작됐을 수 있어요 — 그 동기화가 끝나도 파일이 여전히 빠져 있을 수 있죠. Index a file cookbook에서 파일 자체를 확인하는 방법을 보여줘요.

일반적인 워크플로우

  1. 디렉토리에 새 파일을 업로드하거나 오래된 파일을 제거해요.
  2. 인덱스의 현재 last_synced_at을 읽고, sync 엔드포인트를 한 번 호출해요.
  3. 인덱스 statusready 이고 last_synced_at이 2단계의 값보다 앞서갈 때까지 폴링한 뒤 검색·채팅을 재개해요.

주의할 점: 동기화를 트리거한 직후 status == "ready"만 폴링하면 안 돼요. 새 동기화가 시작되기 전까지 인덱스는 이전 ready 상태를 계속 보고하므로, 상태 체크만으로는 작업이 일어나기도 전에 성공으로 착각할 수 있어요. 동기화 전에 읽은 last_synced_at 값과 비교해야 새 실행이 실제로 끝났는지 확인할 수 있어요.

status 중에서 readyfailed만 릴리스 간에 안정적이에요. 나머지 값(pending, syncing, exporting …)은 그냥 "아직 안 끝남"으로 취급하면 돼요.

import asyncio
import time

# 디렉토리에 새 파일 추가
with open("new-report.pdf", "rb") as f:
    file_obj = await client.files.create(file=f, purpose="user_data")

await client.beta.directories.files.add(
    directory_id,
    file_id=file_obj.id,
)

# 직전 last_synced_at 기록. 갓 트리거된 동기화는 시작 전까지
# 이전 "ready" 상태를 계속 보고하므로, ready + 더 새로운 last_synced_at
# 둘 다 기다린다.
before = (await client.beta.indexes.get(index_id)).last_synced_at

# 동기화 트리거 (한 번만 — 엔드포인트가 rate limit 걸림)
await client.beta.indexes.sync(index_id)

# 이 동기화가 끝날 때까지 폴링, 시도 사이에 백오프, 데드라인 후 포기
deadline = time.monotonic() + 600  # 10 minutes
delay = 2
while True:
    idx = await client.beta.indexes.get(index_id)
    status = idx.metadata["status"] if idx.metadata else "unknown"

    if status == "ready" and idx.last_synced_at != before:
        print("Sync complete!")
        break
    if status == "failed":
        print("Sync failed:", idx.metadata["error_message"])
        break
    if time.monotonic() >= deadline:
        raise TimeoutError("Sync did not complete within 10 minutes")

    await asyncio.sleep(delay)
    delay = min(delay * 2, 30)  # exponential backoff, capped at 30s

인덱스 관리

인덱스 조회는 client.beta.indexes.get("<your-index-id>"), 삭제는 client.beta.indexes.delete("<your-index-id>")로 해요. 인덱스를 삭제하면 동기화·내보내기 설정만 제거되고, 소스 디렉토리와 그 파일은 영향받지 않아요.

더 알아보기