Menjalankan PostgreSQL hanya di memori

104

Saya ingin menjalankan database PostgreSQL kecil yang hanya berjalan di memori, untuk setiap pengujian unit yang saya tulis. Misalnya:

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

Idealnya saya akan memiliki satu postgres yang dapat dieksekusi diperiksa ke kontrol versi, yang akan digunakan pengujian unit.

Sesuatu seperti HSQL, tapi untuk postgres. Bagaimana saya bisa melakukan itu?

Apakah saya bisa mendapatkan versi Postgres seperti itu? Bagaimana saya bisa menginstruksikannya untuk tidak menggunakan disk?

Chi-Lan
sumber

Jawaban:

49

Ini tidak mungkin dengan Postgres. Itu tidak menawarkan mesin dalam proses / dalam memori seperti HSQLDB atau MySQL.

Jika Anda ingin membuat lingkungan mandiri, Anda dapat meletakkan binari Postgres ke SVN (tetapi ini lebih dari sekadar satu yang dapat dieksekusi).

Anda harus menjalankan initdb untuk menyiapkan database pengujian sebelum Anda dapat melakukan apa pun dengan ini. Ini bisa dilakukan dari file batch atau dengan menggunakan Runtime.exec (). Tetapi perhatikan bahwa initdb bukanlah sesuatu yang cepat. Anda pasti tidak ingin menjalankannya untuk setiap pengujian. Anda mungkin bisa menjalankan ini sebelum test-suite Anda.

Namun sementara ini dapat dilakukan, saya merekomendasikan untuk memiliki instalasi Postgres khusus di mana Anda cukup membuat ulang database pengujian Anda sebelum menjalankan pengujian Anda.

Anda dapat membuat ulang database pengujian dengan menggunakan database template yang membuatnya cukup cepat ( jauh lebih cepat daripada menjalankan initdb untuk setiap pengujian yang dijalankan)

seekor kuda tanpa nama
sumber
8
Sepertinya jawaban kedua oleh Erwin di bawah ini harus ditandai sebagai jawaban yang benar
vfclists
3
@vfclists Sebenarnya, tablespace di ramdisk adalah ide yang sangat buruk. Jangan lakukan itu. Lihat postgresql.org/docs/devel/static/manage-ag-tablespaces.html , stackoverflow.com/q/9407442/398670
Craig Ringer
1
@CraigRinger: Untuk memperjelas pertanyaan khusus ini: Merupakan ide yang buruk untuk menggabungkan dengan data yang berharga (dan terima kasih atas peringatannya). Untuk pengujian unit dengan cluster DB khusus, ramdisk sudah cukup.
Erwin Brandstetter
1
Dengan penggunaan buruh pelabuhan menjadi hal yang biasa, beberapa orang telah berhasil dengan alat seperti testcontainers, yang pada dasarnya memungkinkan pengujian Anda memulai dengan sekali pakai, galangan, postgres-contoh. Lihat github.com/testcontainers/testcontainers-java/blob/master/…
Hans Westerbeek
1
@tokopedia itu bukan versi sebenarnya dari Postgres. Ini hanya pustaka pembungkus untuk membuat memulai instance Postgres (dalam proses terpisah) lebih mudah. Postgres akan tetap berjalan "di luar" aplikasi Java dan tidak "tertanam" dalam proses yang sama yang menjalankan JVM
a_horse_with_no_name
77

(Memindahkan jawaban saya dari Menggunakan PostgreSQL dalam memori dan menggeneralisasikannya):

Anda tidak dapat menjalankan Pg dalam proses, dalam memori

Saya tidak tahu cara menjalankan database Postgres dalam memori untuk pengujian. Apa itu mungkin?

Tidak, tidak mungkin. PostgreSQL diimplementasikan dalam C dan dikompilasi ke kode platform. Tidak seperti H2 atau Derby, Anda tidak bisa hanya memuat jardan menjalankannya sebagai DB dalam memori sekali pakai.

Tidak seperti SQLite, yang juga ditulis dalam C dan dikompilasi ke kode platform, PostgreSQL juga tidak dapat dimuat dalam proses. Ini membutuhkan banyak proses (satu per koneksi) karena ini adalah arsitektur multiprosesing, bukan multithreading. Persyaratan multiprosesing berarti Anda harus meluncurkan postmaster sebagai proses yang berdiri sendiri.

Sebagai gantinya: prakonfigurasi koneksi

Saya sarankan cukup menulis pengujian Anda untuk mengharapkan nama host / nama pengguna / kata sandi tertentu berfungsi, dan memiliki pengujian memanfaatkan CREATE DATABASEdatabase sekali pakai, lalu DROP DATABASEdi akhir proses. Dapatkan detail koneksi database dari file properti, buat properti target, variabel lingkungan, dll.

Aman untuk menggunakan instance PostgreSQL yang sudah ada yang sudah memiliki database yang Anda minati, selama pengguna yang Anda masukkan ke pengujian unit bukan superuser, hanya pengguna yang memiliki CREATEDBhak. Paling buruk Anda akan membuat masalah kinerja di database lain. Saya lebih suka menjalankan instalasi PostgreSQL yang sepenuhnya terisolasi untuk pengujian karena alasan itu.

Sebagai gantinya: Luncurkan instance PostgreSQL sekali pakai untuk pengujian

Bergantian, jika Anda benar - benar ingin Anda dapat meminta harness pengujian Anda menemukan binari initdband postgres, menjalankan initdbuntuk membuat database, memodifikasi pg_hba.confke trust, menjalankan postgresuntuk memulainya di port acak, membuat pengguna, membuat DB, dan menjalankan pengujian . Anda bahkan dapat memaketkan binari PostgreSQL untuk beberapa arsitektur dalam jar dan mengekstrak yang untuk arsitektur saat ini ke direktori sementara sebelum menjalankan pengujian.

Secara pribadi saya pikir itu adalah rasa sakit besar yang harus dihindari; jauh lebih mudah untuk hanya mengonfigurasi DB pengujian. Namun, ini menjadi sedikit lebih mudah dengan munculnya include_dirdukungan dalam postgresql.conf; sekarang Anda bisa menambahkan satu baris, lalu menulis file konfigurasi yang dihasilkan untuk yang lainnya.

Pengujian lebih cepat dengan PostgreSQL

Untuk informasi lebih lanjut tentang cara aman meningkatkan kinerja PostgreSQL untuk tujuan pengujian, lihat jawaban rinci saya menulis tentang topik ini sebelumnya: Optimalkan PostgreSQL untuk pengujian cepat

Dialek PostgreSQL H2 bukanlah pengganti yang sebenarnya

Beberapa orang malah menggunakan database H2 dalam mode dialek PostgreSQL untuk menjalankan tes. Saya pikir itu hampir seburuk orang-orang Rails yang menggunakan SQLite untuk pengujian dan PostgreSQL untuk penerapan produksi.

H2 mendukung beberapa ekstensi PostgreSQL dan mengemulasi dialek PostgreSQL. Namun, itu hanya itu - sebuah persaingan. Anda akan menemukan area di mana H2 menerima kueri tetapi PostgreSQL tidak, di mana perilakunya berbeda, dll . Anda juga akan menemukan banyak tempat di mana PostgreSQL mendukung melakukan sesuatu yang tidak bisa dilakukan H2 - seperti fungsi jendela, pada saat penulisan.

Jika Anda memahami batasan pendekatan ini dan akses database Anda sederhana, H2 mungkin OK. Tetapi dalam hal ini Anda mungkin adalah kandidat yang lebih baik untuk ORM yang mengabstraksi database karena Anda tidak menggunakan fitur-fiturnya yang menarik - dan dalam hal ini, Anda tidak perlu terlalu peduli tentang kompatibilitas database lagi.

Tablespaces bukanlah jawabannya!

Jangan tidak menggunakan tablespace untuk membuat "di memori database". Tidak hanya itu tidak diperlukan karena itu tidak akan membantu kinerja secara signifikan, tetapi juga cara yang bagus untuk mengganggu akses ke orang lain yang mungkin Anda pedulikan dalam instalasi PostgreSQL yang sama. Dokumentasi 9.4 sekarang berisi peringatan berikut :

PERINGATAN

Meskipun terletak di luar direktori data utama PostgreSQL, tablespaces adalah bagian integral dari cluster database dan tidak dapat diperlakukan sebagai kumpulan file data yang otonom. Mereka bergantung pada metadata yang terdapat dalam direktori data utama, dan oleh karena itu tidak dapat dilampirkan ke cluster database yang berbeda atau dicadangkan secara individual. Demikian pula, jika Anda kehilangan tablespace (penghapusan file, kegagalan disk, dll), cluster database mungkin menjadi tidak dapat dibaca atau tidak dapat dimulai. Menempatkan tablespace pada sistem file sementara seperti ramdisk berisiko terhadap keandalan seluruh cluster.

karena saya perhatikan terlalu banyak orang yang melakukan ini dan mengalami masalah.

(Jika Anda telah melakukan ini, Anda dapat mkdirmembuka direktori tablespace yang hilang untuk menjalankan PostgreSQL lagi, lalu DROPdatabase yang hilang, tabel, dll. Lebih baik tidak melakukannya.)

Craig Ringer
sumber
1
Saya tidak jelas tentang peringatan yang diberikan di sini. Jika saya mencoba menjalankan Pengujian Unit dengan cepat, mengapa ada cluster yang terlibat? Bukankah ini seharusnya hanya ada di instance PG lokal saya? Jika cluster (satu) rusak mengapa itu penting, saya berencana untuk menghapusnya.
Gates VP
1
@GatesVP PostgreSQL menggunakan istilah "cluster" dengan cara yang agak aneh, untuk merujuk ke instance PostgreSQL (direktori data, kumpulan database, postmaster, dll). Jadi ini bukan "cluster" dalam arti "cluster komputasi". Ya, itu menjengkelkan, dan saya ingin melihat terminologi itu berubah. Dan jika dibuang maka tentu saja itu tidak masalah, tetapi orang secara teratur mencoba untuk memiliki ruang tabel dalam memori sekali pakai pada instalasi PostgreSQL yang berisi data yang mereka pedulikan. Itu masalah.
Craig Ringer
Oke, itu adalah "apa yang saya pikirkan" dan "sangat menakutkan" , solusi RAMDrive jelas hanya dimiliki oleh DB lokal yang tidak berisi data berguna. Tetapi mengapa ada orang yang ingin menjalankan pengujian unit terhadap mesin yang bukan mesin mereka sendiri? Berdasarkan jawaban Anda, Tablespaces + RamDisk terdengar sangat sah untuk instance Unit Test PGSQL yang berjalan hanya di mesin lokal Anda.
Gates VP
1
@GatesVP Beberapa orang menyimpan hal-hal yang mereka pedulikan di mesin lokal mereka - yang tidak masalah, tapi agak konyol menjalankan pengujian unit terhadap instalasi DB yang sama. Orang-orang itu konyol. Beberapa dari mereka juga tidak menyimpan cadangan yang tepat. Ratapan pun terjadi.
Craig Ringer
Bagaimanapun, jika Anda akan menggunakan opsi ramdisk Anda benar-benar menginginkan WAL di ramdisk juga, jadi Anda mungkin juga initdbmenginstal Pg baru di sana. Tapi sungguh, ada sedikit perbedaan antara Pg yang di-tweak untuk pengujian cepat pada penyimpanan normal (fsync = off dan fitur ketahanan / keamanan data lainnya dimatikan) daripada berjalan di ramdisk, setidaknya di Linux.
Craig Ringer
66

Atau Anda bisa membuat TABLESPACE di ramfs / tempfs dan membuat semua objek Anda di sana.
Baru-baru ini saya diarahkan ke sebuah artikel tentang melakukan hal itu di Linux .

Peringatan

Ini dapat membahayakan integritas seluruh cluster database Anda .
Baca peringatan tambahan di manual.
Jadi ini hanya opsi untuk data yang dapat dibuang.

Untuk pengujian unit seharusnya berfungsi dengan baik. Jika Anda menjalankan database lain di mesin yang sama, pastikan untuk menggunakan cluster database terpisah (yang memiliki portnya sendiri) agar aman.

Erwin Brandstetter
sumber
4
Saya benar-benar berpikir ini adalah nasihat yang buruk. Jangan lakukan ini. Sebagai gantinya, initdbinstance postgres baru di tempfs atau ramdisk. Jangan tidak menggunakan tablespace di sebuah tempfs dll, itu rapuh dan sia-sia. Anda lebih baik menggunakan tablespace normal dan membuat UNLOGGEDtabel - ini akan bekerja sama. Dan itu tidak akan membahas kinerja WAL dan faktor fsync kecuali Anda mengambil tindakan yang akan mempertaruhkan integritas dari seluruh DB (lihat stackoverflow.com/q/9407442/398670 ). Jangan lakukan itu.
Craig Ringer
29

Sekarang dimungkinkan untuk menjalankan instance dalam memori dari PostgreSQL dalam pengujian JUnit Anda melalui Komponen PostgreSQL Tertanam dari OpenTable: https://github.com/opentable/otj-pg-embedded .

Dengan menambahkan dependensi ke pustaka tertanam otj-pg ( https://mvnrepository.com/artifact/com.opentable.components/otj-pg-embedded ) Anda dapat memulai dan menghentikan instance PostgreSQL Anda sendiri di @Before dan @Afer kait:

EmbeddedPostgres pg = EmbeddedPostgres.start();

Mereka bahkan menawarkan aturan JUnit agar JUnit secara otomatis memulai dan menghentikan server database PostgreSQL untuk Anda:

@Rule
public SingleInstancePostgresRule pg = EmbeddedPostgresRules.singleInstance();
Rubm
sumber
1
Bagaimana pengalaman Anda dengan paket ini enam bulan kemudian? Berfungsi dengan baik, atau penuh dengan bug?
oligofren
@Rubms Apakah Anda bermigrasi ke JUnit5? Bagaimana Anda menggunakan penggantian @Ruledengan @ExtendWith? Hanya menggunakan .start()di @BeforeAll?
Frankie Drake
Saya belum bermigrasi ke JUnit5, jadi saya belum bisa menjawab pertanyaan Anda. Maaf.
Rubm
Ini bekerja dengan baik. Terima kasih. Gunakan berikut ini untuk membuat sumber data di konfigurasi musim semi Anda jika Anda suka:DataSource embeddedPostgresDS = EmbeddedPostgres.builder().start().getPostgresDatabase();
Sacky San
12

Anda dapat menggunakan TestContainers untuk menjalankan kontainer buruh pelabuhan PosgreSQL untuk pengujian: http://testcontainers.viewdocs.io/testcontainers-java/usage/database_containers/

TestContainers menyediakan JUnit @ Rule / @ ClassRule : mode ini memulai database di dalam container sebelum pengujian Anda dan menghentikannya setelahnya.

Contoh:

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
sumber
7

Sekarang ada versi dalam memori PostgreSQL dari perusahaan Pencarian Rusia bernama Yandex: https://github.com/yandex-qatools/postgresql-embedded

Ini didasarkan pada proses penyematan Flapdoodle OSS.

Contoh penggunaan (dari halaman 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));");

Saya menggunakannya beberapa saat. Ini bekerja dengan baik.

DIPERBARUI : proyek ini tidak lagi dipertahankan secara aktif

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.dll
sumber
1
Itu harus meledak dalam segala macam cara baru dan menarik jika Anda menggunakan beberapa utas, menyematkan runtime JVM atau Mono, fork () proses anak Anda sendiri, atau semacamnya. Sunting : Ini tidak benar-benar disematkan, itu hanya pembungkus.
Craig Ringer
3

Anda juga dapat menggunakan pengaturan konfigurasi PostgreSQL (seperti yang dijelaskan dalam pertanyaan dan jawaban yang diterima di sini ) untuk mencapai kinerja tanpa harus menggunakan database dalam memori.

Dan
sumber
Masalah utama OP adalah memutar instance Postgres dalam memori, bukan untuk kinerja, tetapi untuk kesederhanaan dalam pengujian unit bootstrap di lingkungan dev dan CI.
triple.vee
0

Jika Anda menggunakan NodeJS, Anda dapat menggunakan pg-mem (disclaimer: I'm the author) untuk meniru fitur paling umum dari sebuah postgres db.

Anda akan memiliki basis data penuh dalam memori, terisolasi, platform-agnostik yang mereplikasi perilaku PG (bahkan berjalan di browser ).

Saya menulis artikel untuk menunjukkan cara menggunakannya untuk pengujian unit Anda di sini .

Olivier
sumber