В одном из прошлых проектов случился «кошмар техлида»: в суматохе хотфикса было забыто добавление фильтра WHERE tenant_id = ? в одну из ручек API. В итоге один клиент увидел отчеты другого. Все быстро откатили, но я навсегда запомнил то холодное чувство в животе.
Когда начали проектировать архитектуру следующего проекта, я понял, что полагаться на внимательность разработчиков на код-ревью - это тупик. Рано или поздно кто-то устанет, ошибется, и данные снова протекут.
Искал способ гарантировать изоляцию данных так, чтобы ее физически нельзя было забыть.
Почему стандартные решения не подошли?
Перебрал классическую тройку вариантов, и у каждого нашлись фатальные минусы для задачи:
1. Логическая изоляция (WHERE в коде)?
Как это работает: Тысячи строк кода, и в каждом запросе ты обязан помнить про tenant_id.
Проблема: Человеческий фактор. Это бомба замедленного действия.
2. Схема на клиента (Schema-per-tenant)
Как это работает: У каждого клиента своя схема (schema_01, schema_02...).
Проблема: Это работает, пока клиентов 100. Когда их становится 10 000, база начинает задыхаться.
Детали: Проблема даже не в миграциях, а в файловой системе. 10 000 клиентов × 50 таблиц = 500 000 файлов. Postgres (и Linux) сходят с ума от такого количества открытых дескрипторов, а VACUUM превращается в ад.
3. Отдельная БД на клиента
Как это работает: Полная физическая изоляция.
Проблема: Ценник на инфраструктуру. Держать тысячи коннектов или инстансов RDS - экономическое самоубийство для стартапа.
Тогда посмотрел в сторону PostgreSQL Row Level Security (RLS). Честно говоря, поначалу было страшно. Отдавать логику безопасности "черному ящику" внутри БД казалось рискованным. Плюс, все вокруг пугали: "RLS убьет производительность".