Запуск PostgreSQL только в памяти

106

Я хочу запустить небольшую базу данных PostgreSQL, которая работает только в памяти, для каждого написанного мной модульного теста. Например:

@Before
void setUp() {
    String port = runPostgresOnRandomPort();
    connectTo("postgres://localhost:"+port+"/in_memory_db");
    // ...
}

В идеале у меня должен быть один исполняемый файл postgres, зарегистрированный в системе контроля версий, который будет использовать модульный тест.

Что-то вроде HSQL, но для postgres. Как я могу это сделать?

Могу ли я получить такую ​​версию Postgres? Как я могу запретить ему использовать диск?

Чи-Лан
источник

Ответы:

49

Это невозможно с Postgres. Он не предлагает движок внутри процесса / в памяти, такой как HSQLDB или MySQL.

Если вы хотите создать автономную среду, вы можете поместить двоичные файлы Postgres в SVN (но это больше, чем просто один исполняемый файл).

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

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

Вы можете воссоздать тестовую базу данных, используя базу данных шаблонов, что делает ее создание довольно быстрым ( намного быстрее, чем запуск initdb для каждого тестового запуска)

a_horse_with_no_name
источник
9
Похоже, второй ответ Эрвина ниже должен быть отмечен как правильный ответ
vfclists
3
@vfclists На самом деле табличное пространство на виртуальном диске - действительно плохая идея. Не делай этого. См. Postgresql.org/docs/devel/static/manage-ag-tablespaces.html , stackoverflow.com/q/9407442/398670
Craig Ringer
1
@CraigRinger: Чтобы прояснить этот конкретный вопрос: плохая идея смешивать с ценными данными (и спасибо за предупреждение). Для модульного тестирования с выделенным кластером БД подойдет ramdisk.
Эрвин Брандштеттер
1
Поскольку использование докеров является обычным явлением, некоторые люди добились успеха с таким инструментом, как testcontainers, по сути, позволяющим вашему тесту запускать одноразовый, dockerized, postgres-instance. См. Github.com/testcontainers/testcontainers-java/blob/master/…
Hans Westerbeek
1
@ekcrisp. это не настоящая встроенная версия Postgres. Это просто библиотека-оболочка, которая упрощает запуск экземпляра Postgres (в отдельном процессе). Postgres по-прежнему будет работать «вне» Java-приложения, а не «встроен» в тот же процесс, который запускает JVM
a_horse_with_no_name
79

(Перемещаю свой ответ из раздела Использование PostgreSQL в памяти и обобщаю его):

Вы не можете запустить Pg в процессе, в памяти

Я не могу понять, как запустить базу данных Postgres в памяти для тестирования. Является ли это возможным?

Нет, это невозможно. PostgreSQL реализован на C и скомпилирован в код платформы. В отличие от H2 или Derby, вы не можете просто загрузить jarи запустить его как одноразовую базу данных в памяти.

В отличие от SQLite, который также написан на C и скомпилирован в код платформы, PostgreSQL также не может быть загружен в процессе. Для этого требуется несколько процессов (по одному на соединение), потому что это многопроцессорная, а не многопоточная архитектура. Требование многопроцессорности означает, что вы должны запускать postmaster как отдельный процесс.

Вместо этого: предварительно настройте соединение

Я предлагаю просто написать ваши тесты, ожидая, что определенное имя хоста / имя пользователя / пароль будет работать, и иметь тестовую привязку CREATE DATABASEк одноразовой базе данных, а затем DROP DATABASEв конце запуска. Получите сведения о подключении к базе данных из файла свойств, целевых свойств сборки, переменной среды и т. Д.

Безопасно использовать существующий экземпляр PostgreSQL, который у вас уже есть базы данных, о которых вы заботитесь, если пользователь, который вы указываете для своих модульных тестов, не является суперпользователем, а только пользователем с CREATEDBправами. В худшем случае вы создадите проблемы с производительностью в других базах данных. По этой причине я предпочитаю запускать полностью изолированную установку PostgreSQL для тестирования.

Вместо этого: запустите одноразовый экземпляр PostgreSQL для тестирования

С другой стороны , если вы действительно острыми вы могли бы иметь свой тест Жгут определить местонахождение initdbи postgresбинарных файлов, запускать , initdbчтобы создать базу данных, изменить , pg_hba.confчтобы trust, запустить , postgresчтобы запустить его на произвольный порт, создать пользователь, создать базу данных, и запустить тесты . Вы даже можете связать двоичные файлы PostgreSQL для нескольких архитектур в банке и распаковать файлы для текущей архитектуры во временный каталог перед запуском тестов.

Лично я считаю, что этого следует избегать; проще настроить тестовую БД. Однако с появлением include_dirподдержки стало немного легче postgresql.conf; теперь вы можете просто добавить одну строку, а затем написать сгенерированный файл конфигурации для всех остальных.

Более быстрое тестирование с PostgreSQL

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

Диалект PostgreSQL H2 не является настоящей заменой

Некоторые люди вместо этого используют базу данных H2 в режиме диалекта PostgreSQL для запуска тестов. Я думаю, что это почти так же плохо, как и люди из Rails, использующие SQLite для тестирования и PostgreSQL для производственного развертывания.

H2 поддерживает некоторые расширения PostgreSQL и имитирует диалект PostgreSQL. Однако это всего лишь эмуляция. Вы найдете области, где H2 принимает запросы, а PostgreSQL - нет, где поведение отличается и т . Д. Вы также найдете множество мест, где PostgreSQL поддерживает выполнение того, что H2 просто не может - например, оконных функций на момент написания.

Если вы понимаете ограничения этого подхода и у вас простой доступ к базе данных, H2 может подойти. Но в этом случае вы, вероятно, лучший кандидат на ORM, который абстрагирует базу данных, потому что вы все равно не используете ее интересные функции - и в этом случае вам больше не нужно так сильно заботиться о совместимости базы данных.

Табличные пространства - это не ответ!

Вы не использовать табличный для создания базы данных «в памяти». Это не только не нужно, так как это не сильно повлияет на производительность, но и отличный способ нарушить доступ к любому другому, что может вас заинтересовать в той же установке PostgreSQL. Документация 9.4 теперь содержит следующее предупреждение :

ПРЕДУПРЕЖДЕНИЕ

Несмотря на то, что табличные пространства расположены вне основного каталога данных PostgreSQL, они являются неотъемлемой частью кластера базы данных и не могут рассматриваться как автономный набор файлов данных. Они зависят от метаданных, содержащихся в основном каталоге данных, и поэтому не могут быть присоединены к другому кластеру базы данных или для индивидуального резервного копирования. Точно так же, если вы потеряете табличное пространство (удаление файла, сбой диска и т. Д.), Кластер базы данных может стать нечитаемым или не сможет запуститься. Размещение табличного пространства во временной файловой системе, например на виртуальном диске, ставит под угрозу надежность всего кластера.

потому что я заметил, что слишком много людей делали это и сталкивались с проблемами.

(Если вы это сделали, вы можете mkdirуказать отсутствующий каталог табличного пространства, чтобы снова запустить PostgreSQL, а затем DROPнедостающие базы данных, таблицы и т. Д. Лучше просто не делать этого.)

Крэйг Рингер
источник
1
Мне непонятно, какое предупреждение здесь представлено. Если я пытаюсь быстро запустить модульные тесты, зачем нужен кластер? Разве это не должно быть все на моем локальном одноразовом экземпляре PG? Если кластер (из одного) поврежден, почему это имеет значение, я все равно планировал удалить его.
Gates VP
1
@GatesVP PostgreSQL использует термин «кластер» несколько странным образом для обозначения экземпляра PostgreSQL (каталог данных, коллекция баз данных, почтмейстер и т. Д.). Так что это не «кластер» в смысле «вычислительный кластер». Да, это раздражает, и я бы хотел, чтобы эта терминология изменилась. И если это будет одноразовым, то, конечно, это не имеет значения, но люди регулярно пытаются иметь одноразовое табличное пространство в памяти в установке PostgreSQL, которое содержит данные, которые им небезразличны. Это проблема.
Craig Ringer
Хорошо, это одновременно и «то, что я думал», и «очень страшно» , решение RAMDrive явно принадлежит только локальной БД, которая не содержит полезных данных. Но зачем кому-то запускать модульные тесты на машине, которая не является их собственной машиной? Основываясь на вашем ответе, Tablespaces + RamDisk звучит совершенно законно для реального экземпляра PGSQL Unit Test, работающего исключительно на вашем локальном компьютере.
Gates VP
1
@GatesVP Некоторые люди хранят то, что им небезразлично, на своих локальных машинах - это нормально, но тогда немного глупо запускать модульные тесты для той же установки БД. Но люди глупы. Некоторые из них также не хранят надлежащие резервные копии. Раздаются вопли.
Craig Ringer
В любом случае, если вы собираетесь использовать вариант ramdisk, вам действительно нужен WAL на ramdisk, так что вы можете initdbустановить там совершенно новый Pg. Но на самом деле нет особой разницы между Pg, настроенным для быстрого тестирования в обычном хранилище (fsync = off и другие функции обеспечения надежности / безопасности данных отключены), чем у RAM-диска, по крайней мере, в Linux.
Craig Ringer
66

Или вы можете создать TABLESPACE в ramfs / tempfs и создать там все свои объекты.
Недавно мне указали на статью о том, как делать именно это в Linux .

Предупреждение

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

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

Эрвин Брандштеттер
источник
4
Я действительно считаю это плохим советом. Не делай этого. Вместо этого initdbновый экземпляр postgres в tempfs или ramdisk. Как не использовать табличный в tempfs и т.д., это хрупкое и бессмысленно. Вам лучше использовать обычное табличное пространство и создавать UNLOGGEDтаблицы - он будет работать аналогично. И это не повлияет на производительность WAL и факторы fsync, если вы не предпримете действий, которые поставят под угрозу целостность всей БД (см. Stackoverflow.com/q/9407442/398670 ). Не делай этого.
Craig Ringer
30

Теперь можно запускать экземпляр PostgreSQL в памяти в ваших тестах JUnit через встроенный компонент PostgreSQL из OpenTable: https://github.com/opentable/otj-pg-embedded .

Добавляя зависимость к встроенной библиотеке otj-pg ( https://mvnrepository.com/artifact/com.opentable.components/otj-pg-embedded ), вы можете запускать и останавливать собственный экземпляр PostgreSQL в ваших @Before и @Afer крючки:

EmbeddedPostgres pg = EmbeddedPostgres.start();

Они даже предлагают правило JUnit для автоматического запуска и остановки JUnit вашего сервера базы данных PostgreSQL:

@Rule
public SingleInstancePostgresRule pg = EmbeddedPostgresRules.singleInstance();
Руб.
источник
1
Каковы ваши впечатления от использования этого пакета шесть месяцев спустя? Хорошо работает или изобилует ошибками?
oligofren
@Rubms Вы перешли на JUnit5? Как вы используете замену @Ruleс @ExtendWith? Просто используйте .start()in @BeforeAll?
Фрэнки Дрейк
Я не перешел на JUnit5, поэтому пока не могу ответить на ваш вопрос. Сожалею.
Rubms
Это сработало. Спасибо. Используйте следующее, чтобы создать источник данных в вашей конфигурации DataSource embeddedPostgresDS = EmbeddedPostgres.builder().start().getPostgresDatabase();
Spring,
12

Вы можете использовать TestContainers, чтобы развернуть контейнер докеров PosgreSQL для тестов: http://testcontainers.viewdocs.io/testcontainers-java/usage/database_containers/

TestContainers предоставляет JUnit @ Rule / @ ClassRule : этот режим запускает базу данных внутри контейнера перед вашими тестами и затем разрывает ее.

Пример:

public class SimplePostgreSQLTest {

    @Rule
    public PostgreSQLContainer postgres = new PostgreSQLContainer();

    @Test
    public void testSimple() throws SQLException {
        HikariConfig hikariConfig = new HikariConfig();
        hikariConfig.setJdbcUrl(postgres.getJdbcUrl());
        hikariConfig.setUsername(postgres.getUsername());
        hikariConfig.setPassword(postgres.getPassword());

        HikariDataSource ds = new HikariDataSource(hikariConfig);
        Statement statement = ds.getConnection().createStatement();
        statement.execute("SELECT 1");
        ResultSet resultSet = statement.getResultSet();

        resultSet.next();
        int resultSetInt = resultSet.getInt(1);
        assertEquals("A basic SELECT query succeeds", 1, resultSetInt);
    }
}
Андрейс
источник
8

Теперь есть версия PostgreSQL в памяти от российской поисковой компании под названием Яндекс: https://github.com/yandex-qatools/postgresql-embedded.

Он основан на процессе встраивания Flapdoodle OSS.

Пример использования (со страницы github):

// starting Postgres
final EmbeddedPostgres postgres = new EmbeddedPostgres(V9_6);
// predefined data directory
// final EmbeddedPostgres postgres = new EmbeddedPostgres(V9_6, "/path/to/predefined/data/directory");
final String url = postgres.start("localhost", 5432, "dbName", "userName", "password");

// connecting to a running Postgres and feeding up the database
final Connection conn = DriverManager.getConnection(url);
conn.createStatement().execute("CREATE TABLE films (code char(5));");

Я пользуюсь им некоторое время. Это работает хорошо.

ОБНОВЛЕНО : этот проект больше не поддерживается

Please be adviced that the main maintainer of this project has successfuly 
migrated to the use of Test Containers project. This is the best possible 
alternative nowadays.
Аквялков
источник
1
Это должно взорваться множеством новых и интересных способов, если вы используете несколько потоков, встраиваете среду выполнения JVM или Mono, fork () ваши собственные дочерние процессы или что-то в этом роде. Изменить : на самом деле он не встроен, это просто оболочка.
Крейг Рингер,
3

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

Дэн
источник
Основная проблема OP - это развертывание экземпляра Postgres в памяти не для производительности, а для простоты начальной загрузки модульных тестов в среде разработки и CI.
Triple.vee,
0

Если вы используете NodeJS, вы можете использовать pg-mem (отказ от ответственности: я являюсь автором) для имитации наиболее распространенных функций базы данных postgres.

У вас будет полная в памяти, изолированная, независимая от платформы база данных, реплицирующая поведение PG (она даже работает в браузерах ).

Я написал статью , чтобы показать , как использовать его для ваших модульных тестов здесь .

Оливье
источник