• altair823 released this 2026-06-02 10:20:07 +00:00 | 62 commits to main since this release

    title, created, status, release_trigger
    title created status release_trigger
    kebab v0.22.0 release notes (draft) 2026-06-01 draft
    신규 config surface (provider=candle, num_threads / KEBAB_EMBED_THREADS) — pre-1.0 minor bump
    임베딩 백엔드 다변화 (NUMA-안전 candle provider 추가, opt-in)

    kebab v0.22.0 — candle 임베딩 provider (NUMA-안전, opt-in)

    v0.21.1 (config 마이그레이션) 후속 minor release. 듀얼소켓 NUMA 서버에서
    onnxruntime 의 스레드 하드코딩이 일으키던 ingest 크래시를 피하기 위해, 같은
    임베딩 모델을 순수 Rust(candle) 로 돌리는 opt-in provider 를 추가한다.
    기본 동작은 그대로다 — 기존 사용자는 아무것도 바꿀 필요가 없다.


    핵심 변경

    candle 임베딩 provider (provider = "candle")

    변경 사실. [models.embedding].provider"candle" 값이 추가됐다.
    "fastembed"(기본, onnxruntime) / "candle"(순수 Rust) / "none"(lexical-only)
    중 하나를 고를 수 있다. candle provider 는 fastembed 와 완전히 같은 모델
    (intfloat/multilingual-e5-large, 1024-dim)을 쓰고, e5 prefix → mean pooling
    → L2 정규화 파이프라인도 동일하다. 첫 사용 시 safetensors(~2GB)를
    {model_dir}/candle/ 아래로 자동 다운로드한다.

    [models.embedding]
    provider = "candle"     # 기본은 "fastembed" — NUMA 서버에서만 candle 권장
    num_threads = 8         # candle CPU 스레드 캡 (0 = auto = #cores)
    
    # env 로도 캡 가능 (config 보다 우선)
    KEBAB_EMBED_THREADS=8 kebab ingest
    

    Trade-off. candle 는 순수 Rust 라 onnxruntime 의 네이티브 SIMD 커널보다
    CPU latency 가 느리다 (Phase 0 스파이크 측정 ~4×). 그래서 기본값은
    fastembed 를 유지
    하고, candle 은 onnxruntime 가 죽는 NUMA 환경에서만 켜는
    opt-in 으로 둔다. 단일 워크스테이션 사용자는 fastembed 가 더 빠르다.

    Mitigation (왜 안전한가). candle 의 CPU 백엔드는 글로벌 rayon 풀 크기로
    스레드를 정한다. num_threads(또는 env KEBAB_EMBED_THREADS)가 그 풀을 한 번
    캡하므로, onnxruntime 가 하드코딩하던 48 intra-op 스레드 → NUMA 힙 손상 →
    double-free 경로를 원천 차단한다. NUMA 노드 바인딩이 더 필요하면 numactl
    과 조합한다.

    Upgrade 절차. 재색인 불필요. candle 과 fastembed 의 벡터는 사실상
    동일(Phase 0 스파이크 코사인 1.000000)해서 embedding_version 을 유지했고,
    기존 LanceDB 색인을 그대로 재사용한다. provider 를 바꿔도 검색 결과는
    바뀌지 않는다. 기존 config.tomlnum_threads 가 자동으로 0(auto)으로
    채워져 그대로 로드된다 — kebab config migrate 도, 수동 편집도 필요 없다.


    그 외

    • 신규 crate kebab-embed-candle (candle 의존성 트리를 이 crate 에 격리,
      kebab-core/kebab-config 외 다른 kebab-* 의존 없음).
    • Phase 0 feasibility 스파이크(spike-embed-candle)는 production 흡수 후 제거.
    • 문서: README Configuration, docs/SMOKE.md config 예시, docs/ARCHITECTURE.md
      crate 그래프/트리에 candle provider 반영.

    검증 / 도그푸딩

    • 패리티 (candle vs onnxruntime): 동일 e5-large 가중치로 cosine_min =
      1.000000, 차원별 max 절대오차 = 2.01e-7. 벡터가 사실상 동일 →
      embedding_version 유지(재색인 0). 재현: crates/kebab-embed-candle/tests/parity.rs
      (--ignored).
    • 전체 도그푸딩 (2026-06-02): provider=candle 로 도그푸딩 코퍼스 전체
      재색인 — 997 docs / 23,151 chunks, 에러 0 완주 (≈9.5 h, 단일소켓 VM).
      candle 가 23k+ 청크를 메모리 오류 없이 처리함을 확인.
    • A1(taskset/numactl) 반증: NUMA 서버에서 taskset -c 0-3 으로 스레드를
      4개로 묶어도 onnxruntime 은 그대로 죽었다(6/5150 segfault). 스레드 축소는
      해법이 아니며, provider=candle 만이 실 해법이다 (candle 은 onnxruntime 을
      호출하지 않음).
    • 최종 인수 게이트 (사용자): 그 듀얼소켓 NUMA 서버에서 provider=candle
      ingest 가 EXIT=0 완주 — 배포·실사용이 이 검증을 겸한다.

    성능 노트 (중요)

    candle CPU 임베딩은 onnxruntime 대비 약 3~4× 느리다 (e5-large/512-tok 의
    순수-Rust 커널 비용). 측정상 ~1.86 s/chunk, CPU 약 4코어 활용. 이는 의도된
    트레이드오프
    — onnxruntime 이 전 코어를 AVX-512 로 빡빡하게 굴리는 바로 그
    경로가 NUMA 에서 힙을 손상시켜 죽기 때문이다. "느려도 완주" > "빨라도 크래시".

    • Intel MKL 가속을 실험했으나 부정 결과: MKL 은 코어를 더 쓰지만(89코어)
      오히려 38
      50% 느렸다(과다구독 + MKL 2020.1 오버헤드). 채택하지 않음.
    • 더 많은 코어/스레드로는 빨라지지 않는다(병목이 코어 수가 아님). 속도가
      critical 하면 청크 길이 단축 / 더 작은 모델 / GPU 가 레버다(별도 검토).
    • 9.5 h 는 최초 전체 색인 1회 비용이며, 이후 증분 ingest 는 새/변경 문서만
      처리해 저렴하다. 단일 워크스테이션(비-NUMA)에서는 기본 fastembed 가 더 빠르니
      candle 은 NUMA 호스트 전용 opt-in 으로 둔다.
    Downloads