본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 05. 30. 19:02

우리가 제공한 모든 쿼리 도구를 망가뜨린 Treasure Hunt 데모의 이유

요약

대규모 합성 사용자 여정 데이터를 처리하는 과정에서 발생한 지연 시간 및 메모리 누수 문제를 해결하기 위한 아키텍처 전환 사례를 다룹니다. 사이드카 컨테이너 방식의 실패를 분석하고, S3와 Lambda를 활용한 비동기 처리 및 Snowflake 내부 추론 방식으로의 개선 과정을 설명합니다.

핵심 포인트

  • 사이드카 컨테이너 방식의 토크나이저 지연 및 메모리 누수 문제 식별
  • 실시간 추론 대신 S3와 Lambda를 활용한 비동기 토큰화 아키텍처 도입
  • 데이터 경계를 유지하기 위해 Snowpark Container Services를 통한 내부 추론 수행
  • 운영 범위 내 지연 시간과 데이터 무결성 사이의 트레이드오프 관리

우리가 실제로 해결하고자 했던 문제

우리는 단순히 데모를 만들고 있었던 것이 아닙니다. 우리는 Veltrix 운영자들이 기반이 되는 SQL 데이터 웨어하우스 (SQL warehouse)를 과부하시키지 않으면서, 합성 사용자 여정 (synthetic user journeys)에 대해 A/B 실험을 수행할 수 있도록 해야 했습니다. 진짜 질문은 이것이었습니다: 플래너 (planner)가 술어 (predicates)를 누락시키고 데이터 웨어하우스가 사용자 여정에 맞지 않는 무의미한 행 (rows)을 반환하기 전까지, 데이터 웨어하우스를 AI 추론 계층 (AI inference layer)에 얼마나 가깝게 밀어붙일 수 있는가 하는 점이었습니다.

해당 데이터 웨어하우스는 AWS 상의 Snowflake XL이었으며, 초 단위로 비용이 청구되었습니다. 우리의 합성 사용자 모델은 피크 시간 동안 분당 25만 개의 여정을 생성했습니다. AI 계층은 다음 배치를 처리하기 위해 각 여정에 의도 태그 (intent tags: 쇼핑, 지원, 사기)를 200ms 이내에 주석 처리해야 했습니다. 그것이 영업용 슬라이드가 아닌, 실제 운영 범위 (operating envelope)였습니다.

우리가 처음 시도했던 것 (그리고 실패한 이유)

첫 번째 시도: 여정을 생성하는 Spark 클러스터 옆의 사이드카 컨테이너 (sidecar container)에 의도 모델 (intent model)을 배치했습니다. 지연 시간 (latency) 슬라이드에 30ms라고 명시되어 있었기 때문에, 자체 코퍼스 (corpus)로 미세 조정 (fine-tuned)된 DistilBERT와 ONNX Runtime v1.14를 선택했습니다. 현실은 달랐습니다: ONNX는 토크나이저 (tokenizer)를 별도의 DLL로 패키징했습니다. c6i.large 인스턴스에서 토큰화 (Tokenization) 작업에만 85~110ms가 소요되었고, 데이터 웨어하우스가 콜드 (cold) 상태일 때는 총 추론 시간이 190ms까지 늘어났으며, Snowflake가 웨어하우스 클러스터의 스파이크 (spike)를 일으키기로 결정했을 때는 280ms까지 치솟았습니다. 운영자 대시보드에는 즉시 주황색 핑 (orange pings)이 떴고, 비즈니스 팀에서는 이를 레드 파이어 드릴 (red fire drill, 비상 상황)이라고 불렀습니다.

설상가상으로, 토크나이저 DLL이 메모리 누수 (memory leak)를 일으켰습니다. 64코어 클러스터에서 2시간이 지난 후, 각 포드 (pod)의 RSS는 2.4GB까지 상승했고, Kubernetes 스케줄러 (Kubernetes scheduler)는 다섯 개의 포드를 연속으로 축출 (evict)했습니다. 하류 (downstream)의 데이터 웨어하우스는 의도(intent)가 NULL인 중복 행을 수신하게 되었고, 결과적으로 우리가 내보낸 모든 지표 (metric)는 7~12%의 오차를 보였습니다.

아키텍처 결정

우리는 사이드카 (sidecar)를 완전히 제거했습니다. 대신, Spark 작업이 60초마다 S3 버킷에 가공되지 않은 이벤트 JSON (raw event JSON)을 작성합니다. Lambda 함수 (Python 3.12 런타임)가 해당 버킷을 가져와 오프라인에서 토큰화 (tokenize)를 수행하고, 토큰화된 블롭 (tokenized blobs)을 다시 S3에 저장합니다. 그런 다음 매일 밤 실행되는 Kubernetes 작업이 토큰화된 청크 (tokenized chunks)를 Snowflake의 임시 테이블로 로드합니다. AI 추론 (AI inference)은 Snowpark Container Services를 통해 Snowflake 내부에서 이루어지므로, 가공되지 않은 데이터가 웨어하우스 (warehouse) 경계를 벗어나지 않습니다.

우리는 여정 생성 (journey generation)과 의도 주석 (intent annotation) 사이에 30초의 지연 (lag)이 발생하는 것을 수용했지만, 이 지연은 고정되어 있으며 예측 가능합니다. Snowpark Container Services는 웨어하우스 클러스터를 공유하는 4 vCPU / 16 GB 컨테이너 내부에서 모델을 실행하므로, 토크나이저 (tokenizer)의 메모리 압박 (memory pressure)이 격리됩니다. 이제 웨어하우스 플래너 (warehouse planner)는 사전 토큰화된 텍스트를 수신하므로, 술어 푸시다운 (predicate pushdown)이 올바르게 작동하며 스캔 크기 (scan sizes)가 40% 감소합니다.

수치로 나타난 결과

지연 시간 (Latency): 이벤트 작성부터 웨어하우스 팩 테이블 (fact table)의 의도 태그 (intent tag)까지 엔드 투 엔드 (end-to-end)로 측정했을 때, 전체 다운스트림 파이프라인 (downstream pipeline)에서 185~205ms로 안정적으로 유지되었습니다. 우리는 클릭에서 의도 태그까지의 p95 델타 (p95 delta)라는 커스텀 지표 (custom metric)를 추가하고, 이를 OpenTelemetry 트레이스 (traces)로 계측 (instrumented)했습니다. 전환 후 관찰된 최악의 스파이크 (spike)는 245ms였으며, 여전히 250ms SLA 미만이었습니다.

비용 (Cost): 기존 사이드카 (sidecar) 모델은 중복된 컴퓨팅 (compute)과 메모리 스필 (spilled memory)로 인해 피크 시 분당 2.1 크레딧 (credits)까지 치솟았던 반면, Snowflake 크레딧 소모량은 피크 시간 동안 분당 1.3 크레딧으로 일정하게 유지되었습니다. Lambda 토크나이저는 AWS 컴퓨팅 비용으로 하루 18달러를 추가했지만, 이는 웨어하우스 크레딧 차이에 비하면 무시할 수 있는 수준 (rounding error)이었습니다.

오류 (Errors): NULL 의도 행 (intent rows)이 7%에서 0.18%로 감소했습니다. Snowflake 쿼리 프로필 (query profile)에 따르면 스캔의 99%가 푸시다운된 필터 (pushed-down filters)를 준수하였으며, 이로 인해 웨어하우스 스캔 바이트 (warehouse scan bytes)가 41% 감소하고 쿼리 동시성 (query concurrency)이 균등해졌습니다.

내가 다르게 했을 일

나는 ONNX Runtime 슬라이드 덱을 신뢰하지 않았을 것입니다. 토크나이저 (tokenizer)를 포함하여 내가 통합했던 모든 프로덕션 시스템 (production system)은 어휘 파일 (vocab files) 로드, 토크나이저 DLL 컴파일, JIT 워밍업 (JIT warm-up)과 같은 숨겨진 초기화 비용을 가지고 있었습니다. 우리는 이를 Spark에 연결하기 전에 동일한 인스턴스 클래스 (instance class)에서 토크나이징 (tokenization)을 단독으로 벤치마킹 (benchmarked)했어야 했습니다.

나는 첫날부터 Snowflake 내부에서 토크나이저를 실행하도록 더 강력하게 추진했을 것입니다. 우리가 시작했을 때 Snowpark Container Services는 프라이빗 프리뷰 (private preview) 상태였습니다. 우리가 그것을 채택했을 때쯤에는 이미 장애가 내재되어 있었습니다.

마지막으로, 나는 토크나이징 지연 시간 (tokenization latency)을 엔드 투 엔드 지연 시간 (end-to-end latency) 안에 묻어두는 대신 별도의 메트릭 (metric)으로 노출했을 것입니다. 그렇게 했다면, ONNX가 새로운 토크나이저 버전을 출시했을 때 웨어하우스 (warehouse)에 영향을 미치기 전에 성능 퇴보 (regression)를 포착할 수 있었을 것입니다.

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0