Временная схема на соединение?

8

Я пытаюсь перенести свои юнит-тесты с H2 на Postgresql.

В настоящее время H2 предоставляет мне схему в памяти, так что каждое соединение сопоставляется с уникальной схемой, создает таблицы, запускает тест и удаляет схему. Создание и уничтожение схемы автоматически обрабатывается H2.

Модульные тесты запускаются одновременно.

Каков наилучший способ сделать это в Postgresql? В частности,

  1. Как получить уникальную схему для каждого соединения?
    • Должна ли среда тестирования генерировать уникальные имена или есть встроенный механизм для этого?
  2. Как я могу гарантировать, что схема будет отброшена при разрыве соединения?
    • Я не хочу заканчивать висящими схемами, когда модульные тесты убиты.
  3. Какой подход даст максимальную производительность?
    • Мне нужно создавать / отбрасывать десятки схем в секунду.

ОБНОВЛЕНИЕ : я нашел связанный ответ здесь, но он не может отбросить схемы в случае, если процесс, выполняющий модульные тесты, будет убит.

Гили
источник

Ответы:

13

pg_temp псевдоним для временной схемы текущего сеанса

Если вы SET search_path TO pg_tempвыполняете тест перед запуском тестов, все должно работать (если явно ничего не ссылается на схему).

Если вы вообще не хотите менять свой сценарий, установите search_pathдля пользователя, который входит в тесты:

> ALTER ROLE testuser SET search_path = pg_temp;

Тогда все, что создает пользователь, будет в pg_temp, если это не указано явно.

Вот пример из psql, показывающий фактическую схему (для этого соединения), в которую разрешается псевдоним:

> SET search_path TO pg_temp;
SET
> create table test();
CREATE TABLE
> \dt test
          List of relations
  Schema   | Name | Type  |  Owner
-----------+------+-------+----------
 pg_temp_4 | test | table | postgres
(1 row)

И, как и следовало ожидать, эта схема отличается для каждого одновременного подключения и исчезает после закрытия подключения.

Обратите внимание, что это также работает для функций, хотя вам придется явно ссылаться на схему pg_temp при их вызове.

HBN
источник
Но pg_tempединственная схема правильна? Поэтому, когда я запускаю параллельные модульные тесты, не будут ли они забивать таблицы / данные друг друга?
Гили
1
Нет. Это псевдоним для временной схемы текущего сеанса. Я обновлю ответ с примером.
HBN
Имейте в виду, что если вы просто закроете и откроете соединение, у вас может получиться такая же временная схема, но она будет очищена. Откройте 2 одновременно, чтобы увидеть, как распределяются разные. Вы не можете видеть временную схему другого сеанса, если вы не являетесь суперпользователем.
августа
Конечно, я видел комментарий от вас, спрашивающий о том, когда установить это. В любом случае - это устанавливается для каждой сессии, если вы просто делаете SET search_path; используйте SET LOCAL search_pathдля установки на каждую субтранзакцию, или, если хотите, вы можете установить на уровне пользователя с помощью ALTER USER mytestuser SET search_path = 'pg_temp'или на уровне базы данных с помощьюALTER DATABASE mytestdb SET search_path = 'pg_temp'
hbn
Из любопытства, есть ли способ заставить это работать для функций без явной ссылки на схему? Или это невозможно для pg_tempсхемы?
Гили
3

Вы можете получить имя текущей временной схемы (после создания первой временной таблицы), как показано в добавленной вами ссылке:

SELECT nspname
FROM   pg_namespace
WHERE  oid = pg_my_temp_schema();

Но ваш текущий план все равно не будет иметь большого смысла. Чтобы создать таблицы в текущей временной схеме, просто создайте временные таблицы. Это все. По умолчанию search_pathопределяется так, что временные таблицы видны первыми. Один никогда не нужно схемы-квалифицироваться временными таблицы. Вы никогда не должны иметь для решения текущей временной схемы непосредственно в любом случае - что это деталь реализации.

Эрвин Брандштеттер
источник
Согласитесь, это хак, но это может быть значительно проще, чем параметризация кода создания, позволяющего создавать временные таблицы.
августа
Хороший вопрос, за исключением того, что, как упомянул @hbn, я хочу, чтобы модульные тесты и производственный код запускали один и тот же сценарий SQL. Первый должен работать против временной схемы, а второй - нет.
Гили
@hbn, из любопытства, как будет выглядеть параметризованный код создания? Я использую flywaydb.org, и он просто выполняет простые файлы SQL (без переменных). Я, вероятно, не хочу идти по этому пути. Мне просто любопытно, что с этим связано.
Гили
Я никогда не использовал flywaydb. На самом базовом уровне вы можете использовать некоторый текстовый язык (например, Jinja2 в Python) для предварительной обработки сценариев создания, при необходимости добавляя «временный» при создании таблицы. Если вы явно создаете функции, хак get-the-временная схема, вероятно, неизбежен, поскольку (насколько я знаю) вы не можете напрямую создать временную функцию.
августа
@hbn If you're explicitly sequences ...: Думаю, твой последний комментарий содержал опечатку. Что ты хотел сказать между explicitlyи sequences?
Гили
1

Ваши тесты включают транзакции? DDL является транзакционным в PostgreSQL, поэтому, если вы создаете свою схему и таблицы, а затем запускаете свои тесты, все в рамках одной транзакции, которая затем откатывается, схема фактически никогда не фиксируется и не видна другим сеансам.

Вам все равно нужно будет использовать, возможно, уникальное имя для вашей схемы (возможно, включить имя хоста и PID), так как CREATE SCHEMAэто немедленно завершится ошибкой, если схема с одинаковым именем уже существует, и заблокируется, если другой сеанс создал схему с таким же именем в незафиксированная транзакция.

Альтернативой может быть просто использование временных таблиц, если вы можете изменить сценарии создания базы данных для этого.

HBN
источник
Хороший трюк, но он не сработает в моем случае, потому что один тест работает с несколькими транзакциями. Каждый метод тестирования - это веб-клиент, который запускает несколько транзакций на стороне сервера. Например, он создает, запрашивает и удаляет пользователя. Каждый вызов является отдельным HTTP-запросом и выполняется в отдельной транзакции.
Гили
Справедливо, мой подход был очень ограничен.
августа
@Gili: Обратите внимание, что эта техника никогда не фиксирует CREATE SCHEMAсебя, единственная, которая может гарантировать, что они исчезнут, когда убит юнит-тест.
Даниэль Верите
0

Я только что получил идею.

Postgresql гарантирует, что сессия не может видеть чужие временные таблицы. Я предполагаю, что это означает, что когда вы создаете временную таблицу, она создает временную схему. Так что, возможно, я мог бы сделать следующее:

  1. Создайте (фиктивную) временную таблицу и найдите ее схему.
  2. Используйте эту схему для теста (создайте таблицы, запустите тест).
  3. Когда соединение закрыто, Postgresql сбросит схему.

Мне не нравится полагаться на детали реализации, но в этом случае это кажется довольно безопасным.

Гили
источник