Регионы¶
Provisa даёт вам два примитива для размещения данных: регион и закрепление за регионом, то есть создание региона и назначение ему таблицы. Ни один из них сам по себе не является политикой. То, для чего вы их используете, делится на три группы:
- Производительность. Задержка до читателей, свежесть их копии и пригодность движка: где находится его центр обработки данных, какое хранилище он читает, сколько у него процессора и памяти.
- Стоимость. Где выполняется работа и на чём: собственное оборудование или облако, большой движок или маленький, и во что обходится перемещение данных между местами (egress).
- Суверенитет. Кто контролирует данные и куда они могут попадать: требования закона о размещении (где персональные данные можно обрабатывать и где хранить) и собственные линии контроля организации, например подразделение или дочерняя компания, которая держит свои данные и инфраструктуру под собственным контролем.
Механика для всех трёх одна и та же. Различаются только правила, которые вы пишете поверх.
Регион — это движок и его хранилища в одном месте¶
Регион — это место, где Provisa выполняет работу и хранит то, что после неё остаётся. У каждого региона свой движок и свои хранилища. (REQ-1922) [tool-verified: provisa/core/regions.py OrgRegion, STORE_ROLES]
| Хранилище | Что в нём |
|---|---|
engine |
Движок федерации, выполняющий инструкции в этом регионе |
replicas |
Реплики таблиц, закреплённых здесь, и таблиц без региона, которые копирует этот регион |
views |
Копии материализованных представлений этого региона |
cache |
Закешированные ответы |
state |
Состояние сборок и обновлений |
record |
Запись каждого обслуженного здесь запроса |
Все регионы регулирует одна модель. Источники, таблицы, связи, роли, правила строк и колонок, представления и настройки свежести объявляются один раз, в одном хранилище модели, и каждый регион обслуживает эту же модель, поэтому определения не могут разойтись между регионами. Изменение доходит до каждого региона с одной и той же меткой, после чего каждый регион к нему сходится. (REQ-1914, REQ-1922)
Платформа объявляет физические регионы, в которых работают её узлы, с адресом, по которому отвечает каждый регион. Модель каждой организации выбирает, какие из этих регионов она использует, и называет своё хранилище для каждой роли в каждом регионе. Узел запускается в регионе командой provisa run --region <id> и обслуживает этот регион для каждой организации, которая его выбрала. Когда регионы объявлены, узел, запущенный без --region, отказывается запускаться и перечисляет регионы, которые мог бы обслуживать. (REQ-1922) [tool-verified: provisa/cli.py --region; provisa/core/regions.py validate_regions]
platform:
regions:
- { id: eu, address: "https://eu.provisa.example" }
- { id: us, address: "https://us.provisa.example" }
stores:
- { id: eu_engine, url: "${secret:eu-trino-url}", kind: trino }
- { id: us_engine, url: "${secret:us-trino-url}", kind: trino }
- { id: eu_data, url: "${secret:eu-pg-url}" }
- { id: us_data, url: "${secret:us-pg-url}" }
- { id: eu_cache, url: "${secret:eu-redis-url}" }
- { id: us_cache, url: "${secret:us-redis-url}" }
regions:
- { id: eu, engine: eu_engine, replicas: eu_data, views: eu_data, cache: eu_cache, state: eu_data, record: eu_data }
- { id: us, engine: us_engine, replicas: us_data, views: us_data, cache: us_cache, state: us_data, record: us_data }
PlatformRegion, StoreConfig and OrgRegion in provisa/core/regions.py]
При загрузке конфигурация отклоняется с указанием причины, если регионы не согласуются. Регион должен назвать объявленное хранилище для каждой роли. Его replicas и views должны быть одним хранилищем, а хранилище движка должно называть kind движка. Если у платформы больше одного региона, встроенный файл движка отклоняется, потому что другие регионы не могут до него добраться. [tool-verified: require_one_materialize_store, require_engine_kind, require_reachable_engine]
Несколько регионов могут делить один экземпляр базы данных, один Redis или один бакет объектного хранилища. Всё, что хранит регион, несёт регион в своём имени, поэтому их хранилища не конфликтуют. (REQ-1922)
Развёртывание, которое не объявляет регионов, имеет один неявный регион и нигде не показывает регион. Нет поля региона и флага --region, а конфигурация, называющая регион, отклоняется. (REQ-1922)
Закрепление за регионом: таблица называет свой домашний регион¶
Таблица может назвать один из регионов своей организации своим домашним регионом. Только он строит и обновляет её реплику и только он может держать её горячей. Все остальные регионы читают таблицу из реплики домашнего региона, на месте, по адресу, который даёт модель. Правила строк и колонок читателя входят в эту инструкцию, поэтому фильтрация происходит в домашнем регионе и границу пересекает только то, что читателю разрешено видеть. (REQ-1921) [tool-verified: provisa/core/regions.py module docstring]
- Ни один другой регион не читает источник таблицы; они читают только реплику домашнего региона. Источник, который вы реплицируете, чтобы защитить его от нагрузки, остаётся защищённым.
- Если реплика не построена или домашний регион недоступен, запрос отклоняется с указанием домашнего региона. Источник вместо этого не читается напрямую.
- Закреплённую таблицу, которая не реплицируется, с
replicateNever или с настройкой, ещё не потребовавшей копии, каждый регион читает напрямую из её источника, как любую нереплицируемую таблицу. - Счётчик горячести таблицы — это трафик только её домашнего региона. (REQ-239)
- Материализованное представление тоже может назвать регион. Тогда оно строится только там, а все остальные регионы читают эту копию, точно так же, как для закреплённой таблицы.
Региону, который читает реплику другого региона, нужен движок, способный её прочитать: duckdb, motherduck, pg, trino или trino-byo. Хранилище replicas домашнего региона должно быть сервером PostgreSQL. Конфигурация называет и то, и другое, когда они не подходят. [tool-verified: require_readable_elsewhere; REGION_READERS in provisa/core/engine_kinds.py]
Источник тоже может назвать регион, но этот регион лишь заполняет форму, когда вы регистрируете новую таблицу источника. Он ничего не решает о том, где находятся данные, и его последующее изменение не перемещает ни одной таблицы. (REQ-1921)
Таблицы без региона копируются там, где их читают¶
Таблицу, не называющую регион, можно копировать в каждый регион. Когда её настройка replicate требует копии, регион, к которому подключился читатель, строит её в своём хранилище. Материализованное представление, не называющее регион, строится в каждом регионе, каждая копия в хранилище представлений этого региона. (REQ-1921)
Поэтому новый регион означает больше копий и больше работы по обновлению. Больше регулирования он не означает никогда. Одна модель регулирует каждую копию, и в каждом регионе действуют одни и те же правила строк и колонок. Сборка представления в регионе регулируется как администратор организации, действующий в этом регионе, поэтому копия каждого региона содержит то, что там можно хранить. (REQ-1921, REQ-1922)
Три причины использовать регионы¶
Производительность¶
Регионы, используемые только ради производительности, не объявляют межрегиональных правил. Таблицы, роли и правила те же, что вы написали бы для одного региона, а закрепление решает, где выполняется работа:
- Задержка. Читатели подключаются к ближайшему региону, и его движок, кеш и копии для них локальны.
- Свежесть. Таблица, закреплённая там, где она меняется, обновляется там, рядом со своим источником.
- Пригодность движка. В каждом регионе работает свой движок, поэтому движок можно подобрать к месту: к центру обработки данных, где он находится, к хранилищу, которое он читает, к его процессору и памяти.
Пример: два одинаковых региона. Объявите east и west в двух центрах обработки данных, каждый с одним и тем же видом движка и одними и теми же видами хранилищ, и не закрепляйте ни одной таблицы. Каждый регион обслуживает ближайших к нему читателей: строит свои копии того, что они читают, и кеширует их ответы. Оба могут стоять за одним балансировщиком нагрузки и делить нагрузку. Расписание обновления одно — расписание модели, — и оба региона начинают обновлять свои копии по нему, но их движки могут закончить не за одно и то же время. Поэтому в каждый момент не гарантировано, что оба находятся в одном состоянии, и читатель, переведённый из одного региона в другой, может увидеть данные другой свежести. Provisa не переводит клиента в другой регион, когда один останавливается; куда направить клиента, решает балансировщик нагрузки. [inferred: no failover or cross-region redirect exists in provisa/; clients connect to a region by its address, and each region builds and refreshes only its own copies, on the model's one schedule (REQ-1922)]
Стоимость¶
Регионы ради стоимости тоже не объявляют межрегиональных правил. Они размещают работу там, где её дешевле выполнять:
- Собственное оборудование и облако. Держите регион на собственной площадке, на оборудовании, которым вы уже владеете, для таблиц и читателей, которые там, а другой регион запускайте в облаке для остального.
- Размер движка. Запустите большой движок в одном регионе для эпизодической тяжёлой работы, а маленький в другом — для повседневного чтения. Читатели выбирают регион по его адресу.
- Egress. Читатели в других регионах получают только отфильтрованные строки из реплики закреплённой таблицы, а большая таблица, закреплённая за одним регионом, не копируется в каждый регион.
Суверенитет¶
Регионы ради суверенитета объявляют межрегиональные правила, будь причина в законе или в организации: дочерняя компания, закрепляющая свои таблицы за собственным регионом, обрабатывает и хранит их на своей инфраструктуре и через эти правила решает, что читает остальная группа. Это обычные правила строк и колонок, написанные на атрибутах, которые говорят, где находится читатель или узел, выполняющий работу:
current_setting('provisa.region')— это регион узла, отвечающего на запрос, когда платформа объявляет регионы. Фильтр строк, написанный на нём, удерживает строки региона в этом регионе. (REQ-1922) [tool-verified:provisa/core/request_context.pysession_vars_for]- Атрибут
regionучастника даёт ему производные ролиregionиregion__<value>, поэтому запись маски или предоставления может назватьanalyst+region__eu. Собственная фоновая работа узла несёт его регион так же. (REQ-2057) См. Роли: предоставленные, производные и мета.
Вместе с закреплением эти правила решают, что пересекает границу. Персональные данные закреплённой таблицы хранятся и обрабатываются в её домашнем регионе, а читатель в другом месте получает только то, что разрешают его правила, отфильтрованное дома. Представление без региона, построенное в каждом регионе, содержит то, что может видеть администратор этого региона, поэтому копия в одном регионе может включать строки, которые копия в другом регионе пропускает. (REQ-1921)
Кто может закрепить таблицу¶
Установка, изменение или удаление региона таблицы, источника или материализованного представления перемещает данные через границу. Для этого нужно право data_residency в дополнение к владению объектом через его домен. Предоставление называет значения, которые оно покрывает: регионы организации и «без региона». Перемещение объекта между двумя регионами требует предоставления, покрывающего оба. Сохранение за пределами предоставления отклоняется с указанием непокрытого значения. (REQ-1921)
Право существует только тогда, когда платформа объявляет регионы. Кто может изменять всё остальное в объекте, по-прежнему решает владение доменом. Регион не даёт собственных прав; организация, которая хочет, чтобы таблицами региона управляли люди этого региона, даёт им собственные домены. (REQ-1921)
Таблицу или представление можно также перевести в черновик: вне обслуживания и не предлагается ни одному читателю. Таблица, зарегистрированная через администрирование, начинает как черновик. Её ответственный просматривает её только на узлах региона, который она называет (если не называет никакого — там, где находится ответственный), и копия, которую загружает этот предпросмотр, хранится в этом регионе до выпуска таблицы. Копия, которую держит другой регион, оставшаяся с тех пор, когда регион не был задан или был другим, выводится из обращения и удаляется, как у опубликованной таблицы. Перенос между регионами может пройти через черновик, так что в промежутке таблица нигде не обслуживается: сторона первого региона переводит её в черновик, а владелец второго региона забирает объект, задав свой регион. (REQ-1921)
См. также демо с двумя регионами.