인셔셔RSS 관심 있는 블로그, 뉴스, 기술 정보를 효율적으로 추적하고 읽으세요
원문 읽기 InertiaRSS에서 열기

추천 피드

小众软件
小众软件
博客园 - 叶小钗
有赞技术团队
有赞技术团队
大猫的无限游戏
大猫的无限游戏
博客园_首页
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
L
LangChain Blog
Hugging Face - Blog
Hugging Face - Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
aimingoo的专栏
aimingoo的专栏
Blog — PlanetScale
Blog — PlanetScale
爱范儿
爱范儿
T
Tailwind CSS Blog
Jina AI
Jina AI
量子位
Stack Overflow Blog
Stack Overflow Blog
人人都是产品经理
人人都是产品经理
J
Java Code Geeks
V
Visual Studio Blog
月光博客
月光博客

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python)
추석 탐색 엔진: 우리가 문서를 망치고 실제로 작동하는 시스템을 구축한 방법
Lisa Zulu · 2026-05-28 · via DEV Community

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

사용자들이 의미 검색을 하지 않았습니다. 그들은 보물 탐색을 수행하고 있었습니다: 복잡하고 단계가 많은 쿼리로, 첫 단계는 표현 일치를 위해 200,000개의 후보 문서를 반환하고, 두 번째 단계는 정확한 용어 근접성, 메타데이터 필터링, 사용자 정의 부스팅에 따라 그들을 순위를 매겨야 했습니다. Veltrix 문서는 이를 잊어버린 일로 취급했습니다. 그들의 예제 파이프라인은 단일 단계 인지-순위 흐름을 가정했으며, 커스텀 스코어링 훅이 없었습니다. 로그는 사용자 세션의 73%가 두 번째 단계에서 타이머가 만료되었음을 보여주었습니다. 느린 코사인 스코어러가 필터 캐스케이드에 따라 발맞추지 못했기 때문입니다. 우리는 그것을 비활성화하려고 했지만, API는 명시적으로 설정되지 않은 스코어러가 있는 경우 오류를 발생시켰습니다. 오류 메시지는? Operation not valid: scorer not initialized. 도움이 되셨군요.

처음 시도한 것은 무엇이었고 (왜 실패했는지)

Go로 스코어러를 다시 작성했으며, Veltrix C++ 플러그인 인터페이스를 사용했습니다. 문서에서 인터페이스가 안정적이라고 주장했지만, 6개월 동안 C++ 헤더는 세 번 업데이트되었지만 버전 플래그는 없었습니다. 플러그인은 컴파일되었지만 실행 시에 스택 트레이스가 누락된 심볼을 가리키며 segfault가 발생했습니다: _ZTVN8Veltrix8ScoreAPI8ScorerE. 예제 코드에서는 오류가 발생하지 않았습니다. 예제는 가상 소멸자 오버라이드를 포함하지 않았기 때문입니다. 우리는 이를 디버깅하기 위해 세 날을 보냈지만, 2024년의 GitHub 이슈에서 다른 사용자가 동일한 크래시를 겪었고 소스에서 Veltrix을 재건하라고 알려졌습니다. 재건은 내부 Docker 이미지를 당겨야 했는데, 이 이미지는 12GB였고 45분이 걸렸습니다. 우리의 SLA는 그렇게 하지 않을 수 없었습니다.

그런 다음 파이썬 UDF 경로를 시도해 보았습니다. 문서에 따르면 단일 파이썬 함수를 통해 사용자 지정 점수를 지원한다고 했습니다. 예제는 50줄 미만의 코드를 보여주었습니다. 우리는 부스팅, 필드 가중치, 사용자 지정 메타데이터 필드를 처리하기 위해 500줄의 코드를 작성했습니다. 첫 번째 요청은 파이썬 인터프리터를 초기화하는 데 12초가 걸렸습니다. 그 이후로 각 쿼리는 JIT 오버헤드로 200밀리초를 추가했습니다. 파이썬 타임아웃을 5초로 설정했지만, UDF는 종종 중첩된 JSON 블록 내에서 정규식 검색에 걸려 멈춰 있었습니다. 로그에는 파이썬 트레이스백이 포함되어 있지 않았기 때문에, 우리는 stderr을 사이드카로 전달하고 실시간으로 파싱해야 했습니다. 지연 시간 폭이 예측 불가능해졌습니다. 사용자들은 대시보드가 커피가 식는 것보다 더 느리게 새로고침한다고 불평하기 시작했습니다.

아키텍처 결정

Veltrix에 맞지 않는 역할에 맞춰 넣으려는 시도를 중단했습니다. 대신 파이프라인을 분할했습니다: Veltrix는 재현율을 처리하고, Rust에서 커스텀 랭커를 구축했습니다. Veltrix의 재현율은 여전히 느렸습니다—부정확한 표현 일치에 200ms가 걸렸지만—최상위 10,000개 후보만 기반의 셰이어드 BM25 인덱스를 반환했기 때문에 수용 가능했습니다. 그런 다음 이러한 후보들을 동일 노드에서 실행 중인 gRPC 엔드포인트를 통해 Rust 랭커로 스트리밍했습니다. 랭커는 동적 부스팅, 메타데이터 필터링, 근접성 스코링을 단일 패스에서 적용했습니다. Prost를 코드 생성에 사용하고, Tokio를 비동기 I/O에 사용했습니다. gRPC 엔드포인트는 8ms의 오버헤드를 추가했지만, Rust 랭커는 네트워크 마샬링을 포함하여 10,000개 문서를 45ms에 처리했습니다. 요청당 1,000개 문서의 배치 크기를 조정하여 지연 시간과 처리량을 균형을 맞추었습니다. JSONPath 라이브러리를 깊이嵌套된 필드에서 무한한 스택 성장을 피하기 위해 수작업 바이트 스캐너로 대체한 후 오류율이 0으로 떨어졌습니다.

사용자에게 분할을 보이지 않도록, 우리는 두 서비스를 가벼운 Go 프록시로 프런트했고, 이는 단일 Veltrix 호환 API를 표시했습니다. 프록시는 점수 매개변수를 중단하고 적절히 라우팅했습니다. 매개변수가 기본값이면 Veltrix로 갔고, 우리의 커스텀 _treasurehunt:v1이면 Rust 순위자로 갔습니다. 우리는 이를 제목이 'Veltrix를 사용할 때 울지 않는 방법'인 한 페이지 내부 위키에 문서화했습니다. 위키에는 jemalloc으로 Rust 순위자를 컴파일하는 정확한 CMake 플래그, Go 프록시의 circuit breaker 설정, 그리고 100ms 예산을 가진 gRPC 재시도 정책이 포함되어 있었습니다. 문서는 이 분할을 언급하지 않았습니다. GitHub 스타들은 이 분할을 인정하지 않았습니다. 하지만 지연 시간 백분위 수는 인정했습니다.

숫자들이 무엇을 말했는가

두 주 동안 측정했습니다. 보물 탐색 쿼리의 95번째 백분위수 지연 시간은 4.2초에서 450ms로 낮아졌습니다. 오류율은 0.03%로 안정화되었습니다. 우리는 12%의 개선이 Veltrixs 기본 스코어러를 메모리 내 Bloom 필터로 대체하여 중복 검출을 했기 때문에 나타났다는 것을 발견했습니다. 또 다른 8%는 Rust 순위자의 SIMD 라인을 CPU 캐시 라인 크기와 맞추는 것에서 비롯되었습니다. Go 프록시는 15ms의 오버헤드를 추가했지만 시스템을 관찰할 수 있게 했습니다: 우리는 Prometheus 히스토그램과 OpenTelemetry 트레이스로 이를 장비했습니다. Rust 순위자는 /debug/flush 엔드포인트를 노출시켜 현재 스코링 상태를 Prometheus로 덤프하여 실시간으로 부스 오류를 디버깅할 수 있게 했습니다. 사용자가 낮은 순위의 문서를 불평할 때, 우리는 이전 시간 동안 정확한 스코링 컨텍스트를 재생할 수 있었습니다. Veltrixs 로그는 그렇지 못했습니다.

우리는 또한 JSONPath 라이브러리에 비해 우리의 수동으로 롤된 바이트 스캐너가 2배의 메모리 오버헤드가 있다는 것을 발견했습니다. 하지만 이는 파이썬 UDF가 멈추는 원인이 된 최악의 경우 스택 성장을 제거했습니다. 생산 안정성이 512MB의 RAM보다 더 중요했기 때문에 우리는 트레이드오프를 받아들였습니다. 스캐너의 최악의 경우 할당은 예측 가능했습니다: JSON 레벨마다 하나의 바이트, 최대 64 레벨까지 제한됩니다. 우리는 스캐너에 하드 제한을 추가하고 깊이가 64를 초과하면 422 오류를 반환했습니다. 사용자는 결코 제한을 만나지 않았지만, 이는 실패 모드를 명확하게 만들었습니다.

나는 다르게 할 것 같은 일들

Veltrix 문서는 API 참조를 넘어 신뢰하지 않았을 것이다. 그들의 예제는 연극적이지 실용적이지 않다. 그들은 투자자를 놀라게 하는 데 최적화되어 있지 운영자를 위한 것이 아니다. 트레저 헌트 엔진을 만들고 있다면, 회수 단계를 순위 지정 단계에서 분리해야 한다. Veltrix를 사용하세요.