Uruchamianie PostgreSQL tylko w pamięci

105

Chcę uruchomić małą bazę danych PostgreSQL, która działa tylko w pamięci, dla każdego testu jednostkowego, który piszę. Na przykład:

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

Najlepiej byłoby, gdyby pojedynczy plik wykonywalny postgres został wpisany do kontroli wersji, którego użyje test jednostkowy.

Coś jak HSQL, ale dla postgresów. Jak mogę to zrobić?

Czy mogę dostać taką wersję Postgres? Jak mogę poinstruować go, aby nie korzystał z dysku?

Chi-Lan
źródło

Odpowiedzi:

49

Nie jest to możliwe w przypadku Postgres. Nie oferuje silnika w procesie / w pamięci, takiego jak HSQLDB lub MySQL.

Jeśli chcesz stworzyć niezależne środowisko, możesz umieścić pliki binarne Postgres w SVN (ale to więcej niż pojedynczy plik wykonywalny).

Będziesz musiał uruchomić initdb, aby skonfigurować swoją testową bazę danych, zanim będziesz mógł cokolwiek z tym zrobić. Można to zrobić z pliku wsadowego lub przy użyciu Runtime.exec (). Ale zauważ, że initdb nie jest czymś, co jest szybkie. Na pewno nie będziesz chciał uruchamiać tego dla każdego testu. Możesz jednak uciec przed uruchomieniem tego zestawu testów.

Chociaż można to zrobić, zalecałbym mieć dedykowaną instalację Postgres, w której po prostu odtworzysz swoją testową bazę danych przed uruchomieniem testów.

Możesz ponownie utworzyć testową bazę danych za pomocą bazy danych szablonów, co sprawia, że ​​tworzenie jej jest dość szybkie ( znacznie szybsze niż uruchamianie initdb dla każdego uruchomienia testu)

koń bez imienia
źródło
9
Wygląda na to, że druga odpowiedź Erwina poniżej powinna być oznaczona jako prawidłowa
vfclists
3
@vfclists Właściwie przestrzeń tabel na ramdysku to naprawdę zły pomysł. Nie rób tego. Zobacz postgresql.org/docs/devel/static/manage-ag-tablespaces.html , stackoverflow.com/q/9407442/398670
Craig Ringer
1
@CraigRinger: Aby wyjaśnić to konkretne pytanie: to zły pomysł, aby mieszać się z cennymi danymi (i dziękuję za ostrzeżenie). Do testów jednostkowych z dedykowanym klastrem DB wystarczy ramdysk.
Erwin Brandstetter
1
Ponieważ korzystanie z testcontainersplatformy docker jest powszechne, niektórym osobom udało się zastosować takie narzędzie , które zasadniczo pozwala testowemu uruchomieniu na jednorazową, zadokeryzowaną instancję postgres. Zobacz github.com/testcontainers/testcontainers-java/blob/master/…
Hans Westerbeek
1
@ekcrisp. to nie jest prawdziwie osadzona wersja Postgres. To tylko biblioteka opakowująca, która ułatwia uruchamianie instancji Postgres (w osobnym procesie). Postgres będzie nadal działał „poza” aplikacją Java i nie będzie „osadzany” w tym samym procesie, który uruchamia
maszynę
77

(Przenoszę moją odpowiedź z używania PostgreSQL w pamięci i generalizuję ją):

Nie możesz uruchomić Pg w trakcie, w pamięci

Nie mogę dowiedzieć się, jak uruchomić bazę danych Postgres w pamięci do testów. Czy to możliwe?

Nie, to niemożliwe. PostgreSQL jest zaimplementowany w C i skompilowany do kodu platformy. W przeciwieństwie do H2 lub Derby nie można po prostu załadować jari uruchomić go jako jednorazowej bazy danych w pamięci.

W przeciwieństwie do SQLite, który jest również napisany w C i skompilowany do kodu platformy, PostgreSQL również nie może być ładowany w procesie. Wymaga wielu procesów (po jednym na połączenie), ponieważ jest to architektura wieloprocesowa, a nie wielowątkowa. Wymóg wieloprocesorowości oznacza, że musisz uruchomić postmastera jako samodzielny proces.

Zamiast tego: skonfiguruj wstępnie połączenie

Proponuję po prostu napisać testy, aby oczekiwać, że określona nazwa hosta / nazwa użytkownika / hasło zadziała, a test zaprzęgnie CREATE DATABASEdo jednorazowej bazy danych, a następnie DROP DATABASEpod koniec uruchomienia. Uzyskaj szczegóły połączenia z bazą danych z pliku właściwości, właściwości celu kompilacji, zmiennej środowiskowej itp.

Bezpiecznie jest używać istniejącej instancji PostgreSQL, w której masz już bazy danych, na których Ci zależy, o ile użytkownik, którego dostarczasz do testów jednostkowych, nie jest superużytkownikiem, tylko użytkownikiem z CREATEDBuprawnieniami. W najgorszym przypadku wystąpią problemy z wydajnością w innych bazach danych. Z tego powodu wolę uruchomić całkowicie izolowaną instalację PostgreSQL do testów.

Zamiast tego: uruchom jednorazową instancję PostgreSQL do testowania

Alternatywnie, jeśli naprawdę chcesz, możesz poprosić swoją wiązkę testową, aby zlokalizowała pliki binarne initdbi postgres, uruchom ją, initdbaby utworzyć bazę danych, zmodyfikuj pg_hba.conftrust, uruchom postgresją na losowym porcie, utwórz użytkownika, utwórz bazę danych i uruchom testy . Możesz nawet spakować pliki binarne PostgreSQL dla wielu architektur w jednym pliku jar i rozpakować te dla bieżącej architektury do katalogu tymczasowego przed uruchomieniem testów.

Osobiście uważam, że jest to poważny ból, którego należy unikać; o wiele łatwiej jest po prostu skonfigurować testową bazę danych. Jednak stało się to trochę łatwiejsze wraz z pojawieniem się include_dirwsparcia w postgresql.conf; teraz możesz po prostu dodać jedną linię, a następnie napisać wygenerowany plik konfiguracyjny dla całej reszty.

Szybsze testowanie z PostgreSQL

Aby uzyskać więcej informacji o tym, jak bezpiecznie poprawić wydajność PostgreSQL do celów testowych, zobacz szczegółową odpowiedź, którą napisałem wcześniej na ten temat: Optymalizuj PostgreSQL do szybkiego testowania

Dialekt H2 w PostgreSQL nie jest prawdziwym zamiennikiem

Niektórzy zamiast tego używają bazy danych H2 w trybie dialektu PostgreSQL do uruchamiania testów. Myślę, że to prawie tak złe, jak ludzie z Railsów używający SQLite do testowania i PostgreSQL do wdrożeń produkcyjnych.

H2 obsługuje niektóre rozszerzenia PostgreSQL i emuluje dialekt PostgreSQL. Jednak to tylko to - emulacja. Znajdziesz obszary, w których H2 akceptuje zapytanie, a PostgreSQL nie, gdzie zachowanie jest inne itp . Znajdziesz również wiele miejsc, w których PostgreSQL obsługuje robienie czegoś, czego H2 po prostu nie może - na przykład funkcji okna w momencie pisania.

Jeśli rozumiesz ograniczenia tego podejścia, a dostęp do bazy danych jest prosty, H2 może być w porządku. Ale w takim przypadku prawdopodobnie jesteś lepszym kandydatem na ORM, który abstrahuje bazę danych, ponieważ i tak nie używasz jej interesujących funkcji - iw takim przypadku nie musisz już tak bardzo dbać o zgodność bazy danych.

Przestrzenie tabel nie są odpowiedzią!

Czy nie używać tabel do tworzenia bazy danych „w pamięci”. Nie tylko jest to niepotrzebne, ponieważ i tak nie poprawi wydajności w znaczący sposób, ale jest to również świetny sposób na zakłócenie dostępu do każdego innego, na którym może Ci zależeć w tej samej instalacji PostgreSQL. Dokumentacja 9.4 zawiera teraz następujące ostrzeżenie :

OSTRZEŻENIE

Mimo że obszary tabel znajdują się poza głównym katalogiem danych PostgreSQL, stanowią integralną część klastra bazy danych i nie mogą być traktowane jako autonomiczny zbiór plików danych. Są one zależne od metadanych zawartych w głównym katalogu danych i dlatego nie można ich dołączyć do innego klastra bazy danych ani tworzyć ich kopii zapasowych indywidualnie. Podobnie w przypadku utraty obszaru tabel (usunięcie pliku, awaria dysku itp.) Klaster bazy danych może stać się nieczytelny lub nie można go uruchomić. Umieszczenie obszaru tabel w tymczasowym systemie plików, takim jak ramdysk, zagraża niezawodności całego klastra.

ponieważ zauważyłem, że zbyt wielu ludzi to robi i ma kłopoty.

(Jeśli to zrobiłeś, możesz mkdirbrakować katalog obszaru tabel, aby PostgreSQL zaczął się od nowa, a następnie DROPbrakujące bazy danych, tabele itp. Lepiej po prostu tego nie robić.)

Craig Ringer
źródło
1
Nie mam jasności co do zamieszczonego tutaj ostrzeżenia. Jeśli próbuję szybko uruchomić testy jednostkowe, dlaczego jest zaangażowany klaster? Czy to nie powinno być po prostu na mojej lokalnej, jednorazowej instancji PG? Jeśli klaster (jednego) jest uszkodzony, dlaczego ma to znaczenie, i tak planowałem go usunąć.
Wiceprezes Gates
1
@GatesVP PostgreSQL używa terminu „klaster” w nieco dziwny sposób, w odniesieniu do instancji PostgreSQL (katalog danych, zbiór baz danych, postmaster itp.). Nie jest to więc „klaster” w znaczeniu „klaster obliczeniowy”. Tak, to irytujące i chciałbym zobaczyć, jak zmienia się terminologia. A jeśli jest jednorazowy, to oczywiście nie ma to znaczenia, ale ludzie regularnie próbują mieć jednorazowy obszar tabel w pamięci w instalacji PostgreSQL, który zawiera dane, na których inaczej im zależy. To jest problem.
Craig Ringer
OK, to zarówno „to, co myślałem”, jak i „bardzo przerażające” , rozwiązanie RAMDrive z pewnością należy tylko do lokalnej bazy danych, która nie zawiera żadnych użytecznych danych. Ale dlaczego ktoś miałby chcieć przeprowadzać testy jednostkowe na maszynie, która nie jest jego własną maszyną? Opierając się na twojej odpowiedzi, Tablespaces + RamDisk brzmi jak najbardziej uzasadniony w przypadku rzeczywistej instancji PGSQL działającej wyłącznie na komputerze lokalnym.
Wiceprezes Gates
1
@GatesVP Niektórzy ludzie przechowują rzeczy, na których im zależy na swojej lokalnej maszynie - co jest w porządku, ale wtedy uruchamianie testów jednostkowych na tej samej instalacji bazy danych jest trochę głupie. Ale ludzie są głupi. Niektóre z nich nie przechowują również odpowiednich kopii zapasowych. Następuje zawodzenie.
Craig Ringer
W każdym razie, jeśli zamierzasz skorzystać z opcji ramdysku, naprawdę chcesz, aby na ramdysku znajdował się również WAL, więc równie dobrze możesz initdbzainstalować tam całkiem nową instalację Pg. Ale tak naprawdę istnieje niewielka różnica między Pg, który został dostosowany do szybkiego testowania w normalnej pamięci masowej (fsync = off i inne funkcje związane z trwałością / bezpieczeństwem danych wyłączone) a działaniem na ramdysku, przynajmniej w systemie Linux.
Craig Ringer,
66

Lub możesz stworzyć TABLESPACE w ramfs / tempfs i tam utworzyć wszystkie swoje obiekty.
Niedawno wskazano mi artykuł o tym, jak robić dokładnie to w systemie Linux .

Ostrzeżenie

Może to zagrozić integralności całego klastra bazy danych .
Przeczytaj dodane ostrzeżenie w instrukcji.
Jest to więc tylko opcja dla danych, które można wykorzystać.

W przypadku testów jednostkowych powinno działać dobrze. Jeśli używasz innych baz danych na tym samym komputerze, upewnij się, że używasz oddzielnego klastra bazy danych (który ma własny port), aby zachować bezpieczeństwo.

Erwin Brandstetter
źródło
4
Naprawdę uważam, że to zła rada. Nie rób tego. Zamiast tego initdbnowa instancja postgres w tempfs lub ramdysku. Czy nie używać tabel w sposób tempfs etc, jest kruchy i bezcelowe. Lepiej jest używać normalnego obszaru tabel i tworzenia UNLOGGEDtabel - będzie to działać podobnie. I nie zajmie się wydajnością WAL i czynnikami fsync, chyba że podejmiesz działania, które zagrozią integralności całej bazy danych (patrz stackoverflow.com/q/9407442/398670 ). Nie rób tego.
Craig Ringer,
29

Teraz można uruchomić instancję PostgreSQL w pamięci w testach JUnit za pośrednictwem wbudowanego komponentu PostgreSQL z OpenTable: https://github.com/opentable/otj-pg-embedded .

Dodając zależność do biblioteki otj-pg-embedded ( https://mvnrepository.com/artifact/com.opentable.components/otj-pg-embedded ) możesz uruchamiać i zatrzymywać własne wystąpienie PostgreSQL w swoim @Before i Haczyki @Afer:

EmbeddedPostgres pg = EmbeddedPostgres.start();

Oferują nawet regułę JUnit, która automatycznie uruchamia i zatrzymuje serwer bazy danych PostgreSQL dla JUnit:

@Rule
public SingleInstancePostgresRule pg = EmbeddedPostgresRules.singleInstance();
Rubms
źródło
1
Jakie są Twoje doświadczenia z tym pakietem sześć miesięcy później? Działa dobrze lub jest pełen błędów?
oligofren
@Rubms Czy migrowałeś do JUnit5? Jak używasz zamiany @Rulez @ExtendWith? Po prostu użyj .start()w @BeforeAll?
Frankie Drake
Nie przeprowadziłem migracji do JUnit5, więc nie mogę jeszcze odpowiedzieć na Twoje pytanie. Przepraszam.
Rubms
To działało dobrze. Dzięki. Użyj poniższych, aby utworzyć źródło danych w swojej wiosennej konfiguracji, jeśli chcesz:DataSource embeddedPostgresDS = EmbeddedPostgres.builder().start().getPostgresDatabase();
Sacky San,
12

Możesz użyć TestContainers, aby uruchomić kontener Docker PosgreSQL do testów: http://testcontainers.viewdocs.io/testcontainers-java/usage/database_containers/

TestContainers zapewniają JUnit @ Rule / @ ClassRule : ten tryb uruchamia bazę danych w kontenerze przed testami, a następnie rozrywa ją.

Przykład:

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);
    }
}
Andrejs
źródło
7

Dostępna jest teraz wersja PostgreSQL w pamięci rosyjskiej firmy Yandex: https://github.com/yandex-qatools/postgresql-embedded

Jest oparty na procesie osadzania Flapdoodle OSS.

Przykład użycia (ze strony 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));");

Używam go od jakiegoś czasu. To dobrze działa.

AKTUALIZACJA : ten projekt nie jest już aktywnie zarządzany

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.
akvyalkov
źródło
1
To musi eksplodować na wiele nowych i ekscytujących sposobów, jeśli używasz wielu wątków, osadzasz środowisko wykonawcze JVM lub Mono, rozwidlasz () własne procesy potomne lub cokolwiek w tym stylu. Edycja : nie jest tak naprawdę osadzony, to tylko opakowanie.
Craig Ringer
3

Możesz także użyć ustawień konfiguracyjnych PostgreSQL (takich jak te wyszczególnione w pytaniu i zaakceptowana odpowiedź tutaj ), aby osiągnąć wydajność bez konieczności uciekania się do bazy danych w pamięci.

Dan
źródło
Głównym problemem OP jest uruchomienie instancji Postgres w pamięci, nie ze względu na wydajność, ale ze względu na prostotę ładowania testów jednostkowych w środowisku deweloperskim i CI.
triple.vee
0

Jeśli używasz NodeJS, możesz użyć pg-mem (zastrzeżenie: jestem autorem), aby emulować najbardziej typowe cechy bazy danych postgres.

Będziesz mieć pełną w pamięci, izolowaną, niezależną od platformy bazę danych replikującą zachowanie PG ( działa nawet w przeglądarkach ).

Napisałem artykuł, aby pokazać, jak go używać do testów jednostkowych tutaj .

Olivier
źródło