Запуск PostgreSQL лише в пам'яті


104

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

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

В ідеалі у мене буде перевірено один виконаний файл postgres у контролі версій, який використовуватиме блок-тест.

Щось подібне HSQL, але для постгресів. Як я можу це зробити?

Чи можу я отримати таку версію Postgres? Як я можу доручити йому не використовувати диск?

Відповіді:


49

Це неможливо з Postgres. Він не пропонує механізм вбудованої / пам’яті, як HSQLDB або MySQL.

Якщо ви хочете створити автономне середовище, ви можете помістити бінарні файли Postgres у SVN (але це більше, ніж лише один виконуваний файл).

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

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

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


8
Схоже, другу відповідь Ервіна внизу слід позначати як правильну відповідь
vfclists

3
@vfclists Насправді, табличний простір на ramdisk - це дійсно погана ідея. Не робіть цього. Дивіться postgresql.org/docs/devel/static/manage-ag-tablespaces.html , stackoverflow.com/q/9407442/398670
Крейг Рінгер

1
@CraigRinger: Для уточнення цього конкретного питання: Неправильно поєднувати цінні дані (і дякую за попередження). Для тестування одиниць із виділеним кластером БД добре підійде рамковий диск.
Erwin Brandstetter

1
Оскільки використання докера є звичайним явищем, деякі люди досягли успіху з таким інструментом testcontainers, який, по суті, дає змогу вашому тестовому запуску викидати, докерізований, екземпляр postgres. Дивіться github.com/testcontainers/testcontainers-java/blob/master/…
Hans Westerbeek

1
@ekcrisp. це не справжня вбудована версія Postgres. Це просто бібліотека обгортки, щоб спростити запуск примірника Postgres (в окремий процес). Postgres як і раніше буде працювати "поза" програми Java і не "вбудовуватися" в той самий процес, що запускає JVM
a_horse_with_no_name

77

(Переміщення моєї відповіді з використання PostgreSQL в пам'яті та узагальнення):

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

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

Ні, це неможливо. PostgreSQL реалізується в C і компілюється до коду платформи. На відміну від Н2 або Дербі, ви не можете просто завантажити jarта запустити його як БД в пам'яті.

На відміну від SQLite, який також написаний на C та скомпільований до коду платформи, PostgreSQL також не може завантажуватися в процесі. Це вимагає декількох процесів (по одному на з'єднання), оскільки це багатопроцесорна, а не багатопотокова архітектура. Вимога багатопроцесорної обробки означає, що ви повинні запустити постмайстра як самостійний процес.

Замість цього: попередньо налаштуйте з'єднання

Я пропоную просто написати свої тести, щоб очікувати, що конкретне ім’я хоста / ім’я користувача / пароль запрацює, і тест використає 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, таблиці просторів є невід'ємною частиною кластеру баз даних і не можуть розглядатися як автономна колекція файлів даних. Вони залежать від метаданих, що містяться в головному каталозі даних, тому не можуть бути приєднані до іншого кластеру баз даних або резервні копії окремо. Аналогічно, якщо ви втратите простір таблиць (видалення файлів, збій диска тощо), кластер бази даних може стати нечитабельним або не може запуститися. Розміщення простору таблиць у тимчасовій файловій системі як ramdisk загрожує надійності всього кластеру.

тому що я помітив, що надто багато людей це робило і стикався з неприємностями.

(Якщо ви це зробили, ви можете mkdirскористатися каталогом відсутнього простору таблиць, щоб змусити PostgreSQL запуститися заново, потім DROPвідсутні бази даних, таблиці тощо. Краще просто не робити цього.)


1
Мені незрозуміло щодо попередження, поданого тут. Якщо я намагаюся швидко запустити тести модулів, чому тут задіяний кластер? Чи не повинно це бути все на моєму локальному, викинутому екземплярі PG? Якщо кластер (з одного) пошкоджений, чому це важливо, я все-таки планував його видалити.
Гейтс ВП

1
@GatesVP PostgreSQL дещо дивно використовує термін "кластер" для посилання на екземпляр PostgreSQL (каталог даних, колекція баз даних, постмайстер тощо). Отже, це не "кластер" у значенні "обчислити кластер". Так, це дратує, і я хотів би, щоб ця термінологія змінилася. І якщо це викидання, то, звичайно, це не має значення, але люди регулярно намагаються мати простір таблиць у пам'яті на установці PostgreSQL, який містить дані, які вони інакше дбають. Це проблема.
Крейг Рінгер

Гаразд, це і "те, що я думав", і "дуже страшно" , рішення RAMDrive явно належить лише до локальної БД, яка не містить корисних даних. Але чому б хто-небудь хотів запускати одиничні тести на машині, яка не є власною машиною? Виходячи з вашої відповіді, Tablespaces + RamDisk звучить цілком законно для фактичного екземпляра Unit Test PGSQL, який працює виключно на вашій локальній машині.
Гейтс ВП

1
@GatesVP Деякі люди зберігають на своїй локальній машині речі, про які вони піклуються, - це добре, але потім трохи нерозумно проводити одиничні тести на тій же установці БД. Хоча люди дурні. Деякі з них також не мають належних резервних копій. Наступає голос.
Крейг Рінгер

У будь-якому випадку, якщо ви збираєтесь перейти на опцію ramdisk, ви теж хочете WAL на ramdisk, так що ви можете також initdbвстановити там новий Pg. Але насправді між Pg, яка налаштована на швидке тестування на нормальному сховищі (fsync = вимкнено та інші функції довговічності / безпеки даних вимкнено), є невелика різниця, ніж робота на ramdisk, принаймні в Linux.
Крейг Рінгер

66

Або ви можете створити TABLESPACE у ramfs / tempfs і створити там усі свої об’єкти.
Нещодавно мені вказували статтю про те, що робити саме в Linux .

Увага

Це може загрожувати цілісності всього вашого кластера баз даних .
Прочитайте додане попередження в посібнику.
Тож це лише варіант для витратних даних.

Для тестування одиниць це повинно працювати чудово. Якщо ви працюєте з іншими базами даних на цій же машині, не забудьте використовувати окремий кластер баз даних (у якого є власний порт).


4
Я справді думаю, що це погана порада. Не роби цього. Натомість initdbновий екземпляр postgres у tempfs або ramdisk. Як НЕ використовувати табличний в tempfs і т.д., це крихке і безглуздо. Вам краще використовувати звичайний простір таблиць і створювати UNLOGGEDтаблиці - це буде працювати аналогічно. І він не стосується факторів продуктивності WAL та факторів fsync, якщо не вживати заходів, які загрожують цілісності всієї БД (див. Stackoverflow.com/q/9407442/398670 ). Не робіть цього.
Крейг Рінгер

29

Тепер можна запустити екземпляр пам'яті 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 для автоматичного запуску та зупинки сервера баз даних PostgreSQL для вас:

@Rule
public SingleInstancePostgresRule pg = EmbeddedPostgresRules.singleInstance();

1
Який ваш досвід роботи з цим пакетом через півроку? Добре працює, або позбавлений помилок?
олігофрен

@Rubms Ви перейшли на JUnit5? Як ви використовуєте заміну @Ruleз @ExtendWith? Просто використовувати .start()в @BeforeAll?
Френкі Дрейк

Я не перейшов на JUnit5, тому поки що не можу відповісти на ваше запитання. Вибачте.
Рубмс

Це добре спрацювало. Дякую. Використовуйте наступне, щоб створити джерело даних у своєму весняному конфігурації, якщо вам подобається:DataSource embeddedPostgresDS = EmbeddedPostgres.builder().start().getPostgresDatabase();
Sacky San

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);
    }
}

7

Зараз існує в пам’яті версія PostgreSQL від російської пошукової компанії під назвою Yandex: 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 (такі, які детально описані у запитанні та прийняті відповіді тут ) для досягнення продуктивності, не обов'язково вдаючись до бази даних пам'яті.


Основна проблема ОП - це розкручування пам’яті екземпляра Postgres не для продуктивності, а для простоти в тестах блоку завантаження в середовищі розробників та CI.
triple.vee

0

Якщо ви використовуєте NodeJS, ви можете використовувати pg-mem (відмова від відповідальності: я автор), щоб імітувати найпоширеніші функції db postgres.

У вас буде повна пам'ять, ізольована платформа-агностична база даних, що реплікує поведінку PG (вона працює навіть у браузерах ).

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.