Qdrant - Vector Database

Мультитенантность в Qdrant

Ваш RAG-сервис растёт: 10 клиентов, 100, 1000. Каждый хочет свои данные - и не хочет видеть чужие. Как организовать хранение, чтобы масштабироваться до тысяч тенантов без деградации производительности и без утечек между клиентами? Разберём три паттерна.

  • **SaaS платформа:** 2000 компаний, у каждой своя база знаний → custom sharding, deleteShardKey при offboarding
  • **Enterprise с отделами:** 50 отделов, нужна изоляция + cross-department search → shared collection с фильтрами + role-based access в API
  • **Marketplace:** 10 крупных клиентов с миллионами документов → отдельная коллекция + dedicated Qdrant node per client

Предварительные знания

  • Filtering: Filter API
  • Distributed Mode and Sharding

Три паттерна мультитенантности

**Мультитенантность** - когда один Qdrant инстанс обслуживает множество клиентов (tenants). Три основных паттерна: отдельная коллекция на тенанта, общая коллекция с payload-фильтром, кастомный шардинг по tenant_id. У каждого - свои trade-offs.

ПаттернИзоляцияСтоимость управленияПроизводительностьКогда использовать
Отдельная коллекцияПолнаяВысокая (N коллекций)Хорошая< 100 tenants, требуется hard isolation
Общая коллекция + filterЛогическаяНизкаяДеградирует при большом N1000+ tenants, небольшие объёмы
Custom shardingФизическая по шардуСредняяОтличная при любом NProduction: 10-10000 tenants

**Критическое предупреждение по паттерну 2:** payload-фильтр обеспечивает логическую изоляцию, но не физическую. Если в коде забыть передать `filter: { must: [{ key: 'tenant_id', match: ... }] }` - один тенант получит данные другого. Это реальная угроза безопасности в multi-tenant SaaS. Всегда инкапсулируйте поиск в сервис, который автоматически добавляет фильтр.

Ваш SaaS: 5000 клиентов, у каждого по 1000-5000 документов. Требования: изоляция данных, управляемость, хорошая производительность. Какой паттерн?

Custom Sharding: физическая изоляция по tenant_id

**Custom Sharding** - лучший подход для production мультитенантности. Одна коллекция, но данные физически разделены по шардам на основе `shard_key`. Qdrant автоматически роутит запросы к правильным шардам. Масштабируется на тысячи tenants.

**Defence in depth:** даже при использовании custom sharding - дублируйте `tenant_id` в payload и добавляйте payload-фильтр в поисковый запрос. Если в коде ошибочно передан неверный shard_key, payload-фильтр будет последней линией защиты. Двойная защита = безопаснее.

Тенант удаляет аккаунт. Как эффективно удалить все его данные при custom sharding?

Безопасность: риски и митигации при мультитенантности

**Qdrant не имеет встроенной аутентификации на уровне тенанта.** API ключ даёт доступ ко всем коллекциям. Изоляция данных - ответственность приложения. Разберём реальные векторы атак и как их закрыть.

РискВектор атакиМитигация
Data leakageЗабытый/неверный filterTenantScopedRepository + defence in depth
Cross-tenant accessПрямой доступ к Qdrant APIQdrant за firewall, только через backend API
Tenant enumerationУгадать shard_key другого тенантаUUID v4 как tenant_id (непредсказуемы)
GDPR violationДанные не удаляются при деактивацииdeleteShardKey() при удалении аккаунта + аудит-лог

«Payload-фильтр по tenant_id обеспечивает такую же безопасность как отдельные коллекции»

Payload-фильтр - логическая изоляция. Qdrant не гарантирует на уровне API что запросы без tenant_id фильтра не вернут чужие данные. Custom sharding + TenantScopedRepository - production-ready подход.

В отличие от PostgreSQL Row Level Security (где изоляция встроена в движок), Qdrant применяет фильтры в runtime запроса. Ошибка в приложении = потенциальная утечка. Архитектурная инкапсуляция в TenantScopedRepository + custom sharding устраняет этот риск.

Клиент требует: «Докажите что наши данные физически изолированы от других клиентов». Какой паттерн и аргументы?

Ключевые идеи

  • **Паттерн 1** (отдельная коллекция): полная изоляция, но < 100 тенантов — иначе управленческий кошмар
  • **Паттерн 2** (общая коллекция + filter): простой, масштабируется до тысяч, но логическая изоляция — риск при ошибке в коде
  • **Паттерн 3** (custom sharding): физическая изоляция по шарду, deleteShardKey для быстрого offboarding, лучший выбор для production
  • **Defence in depth:** shard_key + payload filter в одном запросе — двойная защита
  • **TenantScopedRepository** — инкапсулируй логику изоляции, не дай коду напрямую вызывать Qdrant без tenant контекста

Что дальше

Мультитенантность настроена. Финальный шаг - интегрировать всё в NestJS сервис с proper DI, BullMQ async indexing и тестами.

  • NestJS интеграция — Обернуть TenantScopedRepository в NestJS module/service паттерн
  • Production RAG Pipeline — RAG pipeline с учётом мультитенантности - фильтры в поиске, изоляция при upsert
  • Фильтрация с payload — Полный синтаксис фильтров используемых в мультитенантных запросах

Вопросы для размышления

  • При custom sharding: если у тенанта 100 документов и у тенанта 1,000,000 документов они на разных шардах. Как это влияет на балансировку нагрузки? Как Qdrant распределяет запросы?
  • GDPR требует право на удаление данных ('right to be forgotten'). Как deleteShardKey() соответствует этому требованию лучше чем payload-фильтр soft-delete?
  • Представьте что разработчик по ошибке не передал shard_key при поиске в multitenant коллекции. Что произойдёт? Какой payload-фильтр нужен чтобы предотвратить утечку?

Связанные уроки

  • dist-11-replication
Мультитенантность в Qdrant

0

1

Войти