Мультитенантная архитектура поиска для SaaS-приложений

Alex Chibilyaev

Alex Chibilyaev

5/1/2025

#architecture#multi-tenant#saas#search#security
Мультитенантная архитектура поиска для SaaS-приложений

Каждая SaaS-платформа рано или поздно сталкивается с вопросом мультитенантности. Когда вы обслуживаете 100+ организаций на единой поисковой инфраструктуре, как гарантировать, что Арендатор A никогда не увидит данные Арендатора B? Как выстроить ценообразование? Как предотвратить ситуацию, когда один активный арендатор ухудшает качество поиска для всех остальных?

Этот пост объясняет, как AACsearch решает проблему мультитенантного поиска на уровне инфраструктуры — чтобы код вашего приложения оставался простым.

Наивный подход: один индекс на арендатора

Простейший шаблон мультитенантности — один поисковый индекс на каждого клиента. Каждый арендатор получает собственную коллекцию, собственные API-ключи и полную изоляцию.

Плюсы: Максимальная изоляция данных. Простота отладки. Лёгкий биллинг по количеству индексов. Минусы: Сложность управления индексами в масштабе. Дрейф конфигурации между арендаторами. Сложнее provisioning при регистрации.

Для небольших SaaS-приложений (< 50 арендаторов) этот подход работает отлично. AACsearch поддерживает несколько индексов на аккаунт с отдельными API-ключами на каждый индекс.

# Создание индекса для арендатора через API
curl -X POST "https://api.AACSearch.com/indices" \
  -H "X-API-KEY: ss_admin_main_key" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "acme_corp_products",
    "fields": [
      {"name": "name", "type": "string"},
      {"name": "price", "type": "float"},
      {"name": "sku", "type": "string"}
    ]
  }'

Масштабируемый подход: общий индекс с безопасностью на уровне строк

Для SaaS-платформ с сотнями или тысячами арендаторов общий индекс с фильтрацией по арендатору более экономичен и проще в управлении.

Модель данных

Каждый документ содержит поле tenant_id:

{
	"id": "doc_001",
	"tenant_id": "acme_corp",
	"name": "Wireless Headphones",
	"price": 79.99,
	"category": "Electronics",
	"in_stock": true
}

API-токены с ограниченной областью действия

Scoped API-токены AACsearch автоматически фильтруют каждый запрос к конкретному арендатору. При создании токена вы указываете фильтр, который AND-комбинируется с каждым поисковым запросом:

# Создание scoped-токена для арендатора
curl -X POST "https://api.AACSearch.com/api-keys/scoped" \
  -H "X-API-KEY: ss_admin_main_key" \
  -H "Content-Type: application/json" \
  -d '{
    "filter": "tenant_id:=:acme_corp",
    "description": "Acme Corp search key"
  }'

Возвращённый токен (ss_scoped_*) может искать только документы, где tenant_id == "acme_corp". Даже если злоумышленник перехватит токен, он не сможет получить доступ к данным других арендаторов.

Архитектурная схема

Запрос пользователя → Ваше SaaS-приложение → Создание Scoped-токена → AACsearch → Отфильтрованные результаты
                                    │
                                    └── token_filter: tenant_id:=:acme_corp
                                                ↓
                               Все запросы автоматически фильтруются
                               Никакого middleware, никакого риска SQL-инъекций

Ваше приложение создаёт scoped-токен при входе пользователя в систему. Токен включает фильтр арендатора. Каждый последующий поисковый запрос из браузера пользователя содержит этот токен. AACsearch применяет фильтр на уровне поискового движка — ваше приложение никогда не добавляет WHERE tenant_id = X вручную.

Характеристики производительности

| Архитектура | < 100 арендаторов | 100–1K арендаторов | 1K–10K арендаторов | | ---------------------- | ----------------- | ------------------ | ------------------ | | На индекс | Отлично | Хорошо | Плохо (управление) | | Общий + scoped-токен | Хорошо | Отлично | Отлично | | Гибрид (шардированный) | Избыточно | Хорошо | Отлично |

Производительность общего индекса в масштабе:

  • Задержка запросов: < 50 мс на P95 независимо от количества арендаторов (фильтр — это lookup по битовой карте)
  • Размер индекса: индекс на 10M документов с 5 000 арендаторов работает так же, как одноарендаторный индекс
  • Конкурентные запросы: 500+ QPS на тарифе Scale, 2 000+ на Pro

Ограничение скорости на арендатора

Без ограничения скорости на арендатора один агрессивный клиент может монополизировать поисковые мощности. AACsearch поддерживает настраиваемые лимиты:

{
	"rate_limits": {
		"acme_corp": {
			"searches_per_second": 100,
			"indexing_per_minute": 1000
		},
		"startup_inc": {
			"searches_per_second": 20,
			"indexing_per_minute": 200
		}
	}
}

Настройка через Dashboard или API. При превышении лимитов возвращается HTTP 429 с заголовком Retry-After, давая арендатору обратную связь без влияния на других клиентов.

Автоматизация provisioning арендаторов

Когда новый клиент регистрируется, автоматизируйте процесс provisioning:

async function provisionTenant(tenantId: string) {
	// 1. Создание scoped API-ключа
	const scopedKey = await AACSearch.createScopedKey({
		filter: `tenant_id:=:${tenantId}`,
		description: `${tenantId} search key`,
	});

	// 2. Сохранение ключа в настройках арендатора
	await db.tenants.update({
		where: { id: tenantId },
		data: { aacsearchKey: scopedKey },
	});

	// 3. Опционально: предзаполнение шаблонными данными
	await AACSearch.importDocuments(
		"products",
		templateData.map((doc) => ({
			...doc,
			tenant_id: tenantId,
		})),
	);

	return scopedKey;
}

Весь этот процесс выполняется менее чем за 500 мс. Ваш клиент может искать сразу после регистрации.

Гибридная архитектура: шардирование по уровню арендатора

Для очень крупных развёртываний (10K+ арендаторов) рассмотрите гибридный подход:

  • Уровень 1 (Free/Starter): Общий индекс с scoped-токенами (500+ арендаторов на индекс)
  • Уровень 2 (Scale): Выделенный индекс на арендатора
  • Уровень 3 (Enterprise): Выделенный кластер AACSearch
function getIndexForTenant(tenant: Tenant): string {
	if (tenant.plan === "enterprise") return `enterprise_${tenant.id}`;
	if (tenant.plan === "scale") return `scale_${tenant.id}`;
	return "free_tier_shared"; // тысячи бесплатных арендаторов делят один индекс
}

Вопросы безопасности

| Проблема | Как AACsearch это решает | | -------------------------------- | -------------------------------------------------------------------- | | Перехват токена | Scoped-токены только для поиска (нет доступа на запись) | | Утечка данных между арендаторами | Фильтр применяется на серверной стороне, а не на клиентской | | Срок действия токена | Токены могут иметь TTL (автоматическое истечение через N часов/дней) | | Аудит | Все API-запросы логируются с ID токена и временной меткой | | Расположение данных | Выбор региона на индекс — данные EU-арендаторов остаются в EU | | Соответствие стандартам | SOC2-совместимая инфраструктура, готовность к GDPR | | Ротация ключей | Перегенерация ключей без downtime (старый ключ работает до TTL) |

Ценообразование в мультитенантном масштабе

AACsearch взимает плату за индекс, а не за арендатора. При использовании общего индекса:

| Тариф | Цена | Макс. арендаторов (общий) | Макс. документов | Поисковые запросы | | ------- | -------- | ------------------------- | ---------------- | ----------------- | | Free | $0 | 50 | 10K | Безлимитно | | Starter | $29/мес | 200 | 50K | Безлимитно | | Scale | $99/мес | 1 000 | 500K | Безлимитно | | Pro | $249/мес | 5 000 | 5M | Безлимитно | | Custom | Custom | Безлимитно | Custom | Безлимитно |

Большинство SaaS-приложений на тарифе Scale могут обслуживать 500–1 000 арендаторов из одного общего индекса. При $99/мес это $0,10–0,20 на арендатора в месяц — значительно меньше, чем стоимость самостоятельного хостинга AACSearch или подписки на операционную модель ценообразования от конкурентов.

Миграция на мультитенантность

Уже есть одноарендаторная настройка? Мигрируйте на общую мультитенантность:

  1. Добавьте поле tenant_id во все документы каждого одноарендаторного индекса
  2. Экспортируйте все одноарендаторные индексы
  3. Импортируйте в один общий индекс с заполненным tenant_id
  4. Создайте scoped API-токены для каждого арендатора
  5. Обновите приложение для создания токенов при входе
  6. Выведите из эксплуатации одноарендаторные индексы
  7. Готово — ваши клиенты не заметят простоя

Следующие шаги

Начните создавать мультитенантный поиск бесплатно →