PostgreSQL'i yalnızca bellekte çalıştırma


107

Yazdığım her birim test için sadece bellekte çalışan küçük bir PostgreSQL veritabanı çalıştırmak istiyorum. Örneğin:

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

İdeal olarak, birim testinin kullanacağı sürüm kontrolüne kontrol edilen tek bir postgres yürütülebilir dosyası alacağım.

Gibi bir şey HSQL, ama postgres için. Bunu nasıl yapabilirim?

Böyle bir Postgres sürümü alabilir miyim? Diski kullanmamasını nasıl isteyebilirim?

Yanıtlar:


49

Postgres ile bu mümkün değildir. HSQLDB veya MySQL gibi bir işlem içi / bellek içi motor sunmaz.

Eğer kendi kendine yeten bir ortam yaratmak isterseniz yapabilirsiniz SVN'de içine Postgres ikilileri koymak (ama daha sadece tek yürütülebilir daha var).

Bununla herhangi bir şey yapmadan önce test veritabanınızı kurmak için initdb'yi çalıştırmanız gerekecektir . Bu, bir toplu iş dosyasından veya Runtime.exec () kullanılarak yapılabilir. Ancak initdb'nin hızlı bir şey olmadığını unutmayın. Bunu her test için kesinlikle çalıştırmak istemeyeceksiniz. Yine de test süitinizden önce bunu çalıştırmaktan kurtulabilirsiniz.

Ancak bu yapılabilse de, testlerinizi çalıştırmadan önce test veritabanınızı yeniden oluşturduğunuz özel bir Postgres kurulumuna sahip olmanızı tavsiye ederim.

Test veritabanını, oldukça hızlı oluşturulmasını sağlayan bir şablon veritabanı kullanarak yeniden oluşturabilirsiniz ( her test çalıştırması için initdb'yi çalıştırmaktan çok daha hızlı)


9
Görünüşe göre Erwin'in aşağıdaki ikinci cevabı doğru cevap olarak işaretlenmeli
vfclists

3
@vfclists Aslında, ramdisk üzerindeki tablo alanı gerçekten kötü bir fikirdir. Bunu yapma. Bkz postgresql.org/docs/devel/static/manage-ag-tablespaces.html , stackoverflow.com/q/9407442/398670
Craig Ringer

1
@CraigRinger: Bu özel soruyu netleştirmek için: Değerli verilerle karıştırmak kötü bir fikir (ve uyarı için teşekkürler). Özel bir DB kümesiyle birim testi için bir ramdisk uygundur.
Erwin Brandstetter

1
Docker kullanımının yaygın olmasıyla, bazı insanlar testcontainers, test başlangıcınıza atılabilir, dockerize, postgres-instance'a izin veren gibi bir araçla başarılı oldu . Bkz. Github.com/testcontainers/testcontainers-java/blob/master/…
Hans Westerbeek

1
@ekcrisp. bu Postgres'in gerçek bir yerleşik sürümü değildir. Postgres örneğini (ayrı bir işlemde) başlatmayı kolaylaştıran bir sarmalayıcı kitaplığıdır. Postgres, Java uygulamasının "dışında" çalışacak ve
JVM'yi

79

(Cevabımı Bellek içi PostgreSQL kullanmaktan alıp genelleştirmek):

İşlem içi, bellek içi Pg'yi çalıştıramazsınız

Bellek içi Postgres veritabanını test için nasıl çalıştıracağımı çözemiyorum. Mümkün mü?

Hayır, bu mümkün değil. PostgreSQL, C'de uygulanır ve platform koduna derlenir. H2 veya Derby'den farklı jarolarak, bellek içi DB olarak yükleyip ateşleyemezsiniz.

C dilinde yazılan ve platform koduna derlenen SQLite'ın aksine, PostgreSQL de süreç içinde yüklenemez. Birden çok işlem gerektirir (bağlantı başına bir) çünkü çok iş parçacıklı bir mimari değil, çok işlemlidir. Çoklu işlem gereksinimi , postmaster'ı bağımsız bir işlem olarak başlatmanız gerektiği anlamına gelir .

Bunun yerine: bir bağlantıyı önceden yapılandırın

Testlerinizi yalnızca belirli bir ana bilgisayar adı / kullanıcı adı / parolanın çalışmasını beklemek için yazmanızı ve testin CREATE DATABASEdaha sonra DROP DATABASEçalıştırmanın sonunda kullanılıp atılan bir veritabanına sahip olmasını öneririm . Bir özellikler dosyasından veritabanı bağlantı ayrıntılarını alın, hedef özellikleri oluşturun, ortam değişkeni vb.

Birim testlerinize sağladığınız kullanıcı bir süper kullanıcı olmadığı , yalnızca CREATEDBhaklara sahip bir kullanıcı olduğu sürece, zaten ilgilendiğiniz veritabanlarına sahip olduğunuz mevcut bir PostgreSQL örneğini kullanmak güvenlidir . En kötüsü, diğer veritabanlarında performans sorunları yaratırsınız. Bu nedenle test etmek için tamamen izole edilmiş bir PostgreSQL kurulumu çalıştırmayı tercih ediyorum.

Bunun yerine: Test için kullanabileceğiniz bir PostgreSQL örneği başlatın

Eğer eğer Alternatif olarak, gerçekten sen olabilir düşkün olması, test koşum bulmak initdbve postgresçalıştırmak ikilileri, initdbdeğiştirebilir, bir veritabanı oluşturmak için pg_hba.confiçin trustçalıştırmak, postgresbir DB oluşturma, bir kullanıcı oluşturmak, rastgele bağlantı noktasında bunu başlatmak için, ve testler . Hatta birden fazla mimari için PostgreSQL ikili dosyalarını bir kavanozda toplayabilir ve mevcut mimari için olanları testleri çalıştırmadan önce geçici bir dizine açabilirsiniz.

Şahsen bunun kaçınılması gereken büyük bir acı olduğunu düşünüyorum; yapılandırılmış bir test DB'si olması çok daha kolay. Ancak, include_dirdesteğin gelişiyle biraz daha kolay hale geldi postgresql.conf; şimdi sadece bir satır ekleyebilir, ardından geri kalan her şey için oluşturulan bir yapılandırma dosyası yazabilirsiniz.

PostgreSQL ile daha hızlı test

Test amacıyla PostgreSQL'in performansını güvenli bir şekilde nasıl geliştirebileceğiniz hakkında daha fazla bilgi için, bu konu hakkında daha önce yazdığım ayrıntılı bir cevaba bakın: Hızlı test için PostgreSQL'i optimize edin

H2'nin PostgreSQL lehçesi gerçek bir ikame değildir

Bazı kişiler bunun yerine testleri çalıştırmak için PostgreSQL lehçe modunda H2 veritabanını kullanır. Sanırım bu, test için SQLite ve üretim dağıtımı için PostgreSQL kullanan Rails kadar kötü.

H2, bazı PostgreSQL uzantılarını destekler ve PostgreSQL lehçesini taklit eder. Ancak, sadece bu - bir öykünme. H2'nin bir sorguyu kabul ettiği ancak PostgreSQL'in kabul etmediği, davranışın farklı olduğu vb . Alanlar bulacaksınız . Ayrıca, PostgreSQL'in H2'nin yapamayacağı bir şeyi yapmayı desteklediği pek çok yer bulacaksınız - yazma sırasında pencere işlevlerinden hoşlanmayacaksınız.

Bu yaklaşımın sınırlamalarını anlıyorsanız ve veritabanı erişiminiz basitse, H2 tamam olabilir. Ancak bu durumda, veritabanını özetleyen bir ORM için muhtemelen daha iyi bir aday olursunuz çünkü yine de ilginç özelliklerini kullanmıyorsunuz - ve bu durumda, veritabanı uyumluluğunu artık önemsemeniz gerekmiyor.

Tablo alanları çözüm değil!

Do not bir "bellek" veritabanı oluşturmak için bir tablo kullanın. Sadece performansa önemli ölçüde yardımcı olmayacağı için gereksiz olmakla kalmaz, aynı zamanda aynı PostgreSQL kurulumunda ilginizi çekebilecek diğer herhangi birine erişimi engellemenin harika bir yoludur. 9.4 belgeleri artık aşağıdaki uyarıyı içermektedir :

UYARI

Ana PostgreSQL veri dizininin dışında yer almalarına rağmen, tablo alanları veritabanı kümesinin ayrılmaz bir parçasıdır ve veri dosyalarının otonom bir koleksiyonu olarak ele alınamaz. Ana veri dizininde bulunan meta verilere bağımlıdırlar ve bu nedenle farklı bir veritabanı kümesine eklenemez veya ayrı ayrı yedeklenemezler. Benzer şekilde, bir tablo alanını kaybederseniz (dosya silme, disk arızası, vb.), Veritabanı kümesi okunamaz hale gelebilir veya başlatılamayabilir. Bir ramdisk gibi geçici bir dosya sistemine bir tablo alanı yerleştirmek, tüm kümenin güvenilirliğini riske atar.

çünkü çok fazla insanın bunu yaptığını ve sorunla karşılaştığını fark ettim.

(Bunu yaptıysanız mkdir, PostgreSQL'i yeniden başlatmak için eksik tablo alanı dizinini, ardından DROPeksik veritabanları, tabloları vb. Yapabilirsiniz . Bunu yapmamak daha iyidir.)


1
Burada verilen uyarı hakkında net değilim. Birim Testlerini hızlı çalıştırmaya çalışıyorsam, neden bir küme var? Bunların hepsi benim yerel, atılabilir PG örneğimde olması gerekmez mi? Küme (birinin) bozulmuşsa, bu neden önemli? Yine de silmeyi planlıyordum.
Gates VP

1
@GatesVP PostgreSQL, PostgreSQL örneğine (veri dizini, veritabanları koleksiyonu, postmaster, vb.) Atıfta bulunmak için "küme" terimini biraz garip bir şekilde kullanır. Dolayısıyla, "hesaplama kümesi" anlamında bir "küme" değildir. Evet, bu can sıkıcı ve bu terminolojinin değiştiğini görmek isterim. Ve eğer atılsa, o zaman elbette önemli değil, ancak insanlar düzenli olarak bir PostgreSQL kurulumunda, normalde ilgilendikleri verileri içeren atılabilir bir bellek içi tablo alanına sahip olmaya çalışırlar . Bu bir sorun.
Craig Ringer

Tamam, bu hem "düşündüğüm şey" hem de "çok korkutucu" , RAMDrive çözümü açıkça yalnızca yararlı veri içermeyen yerel bir DB'ye ait. Ama neden birisi kendi makinesi olmayan bir makineye karşı birim testleri yapmak istesin ki? Cevabınıza bağlı olarak, Tablespaces + RamDisk, yalnızca yerel makinenizde çalışan PGSQL'in gerçek Birim Testi örneği için mükemmel şekilde meşru görünüyor.
Gates VP

1
@GatesVP Bazı insanlar önemsedikleri şeyleri yerel makinelerinde tutarlar - bu iyi bir şey, ancak aynı DB kurulumuna karşı birim testleri çalıştırmak biraz aptalca. Yine de insanlar aptaldır. Bazıları da uygun yedekleri tutmuyor. Ağlamalar başlar.
Craig Ringer

Her durumda, eğer ramdisk seçeneğine gidecekseniz, ramdiskte de WAL'ı gerçekten istiyorsunuz, bu yüzden initdborada tamamen yeni bir Pg kurulumu yapabilirsiniz. Ama gerçekte, normal depolamada (fsync = kapalı ve diğer veri dayanıklılığı / güvenlik özellikleri kapalı) hızlı test için ince ayarlanmış bir Pg arasında, en azından Linux'ta bir ramdisk üzerinde çalışmaktan çok az fark vardır.
Craig Ringer

66

Veya ramfs / tempfs içinde bir TABLESPACE oluşturabilir ve tüm nesnelerinizi orada oluşturabilirsiniz.
Geçenlerde bunu Linux'ta yapmakla ilgili bir makaleye işaret edildim .

Uyarı

Bu, tüm veritabanı kümenizin bütünlüğünü tehlikeye atabilir .
Kılavuza eklenen uyarıyı okuyun.
Yani bu yalnızca harcanabilir veriler için bir seçenektir.

İçin ünitenin-test sadece para cezası çalışmalıdır. Aynı makinede başka veritabanları çalıştırıyorsanız, güvenli olmak için ayrı bir veritabanı kümesi (kendi bağlantı noktasına sahip) kullandığınızdan emin olun.


4
Bunun gerçekten kötü bir tavsiye olduğunu düşünüyorum. Bunu yapma. Bunun yerine, initdbtempfs veya ramdisk'te yeni bir postgres örneği. Do not bir tempfs vb tablo alanı kullanmak, kırılgan ve anlamsız. Normal bir tablo alanı kullanmak ve tablo oluşturmak daha iyidir UNLOGGED- benzer şekilde çalışacaktır. Ve tüm DB'nin bütünlüğünü riske atacak eylemlerde bulunmadığınız sürece WAL performansını ve fsync faktörlerini ele almaz (bkz. Stackoverflow.com/q/9407442/398670 ). Yapma.
Craig Ringer

30

Artık, OpenTable'daki Gömülü PostgreSQL Bileşeni aracılığıyla JUnit testlerinizde PostgreSQL'in bir bellek içi örneğini çalıştırmak mümkündür: https://github.com/opentable/otj-pg-embedded .

Otj-pg-gömülü kitaplığa ( https://mvnrepository.com/artifact/com.opentable.components/otj-pg-embedded ) bağımlılığı ekleyerek, @Before'nizde kendi PostgreSQL örneğinizi başlatabilir ve durdurabilirsiniz. @Afer kancalar:

EmbeddedPostgres pg = EmbeddedPostgres.start();

JUnit'in sizin için PostgreSQL veritabanı sunucunuzu otomatik olarak başlatması ve durdurması için bir JUnit kuralı bile sunarlar:

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

1
Altı ay sonraki bu paketle deneyiminiz nasıl? İyi çalışıyor mu yoksa böceklerle dolu mu?
oligofren

@Rubms JUnit5'e geçtiniz mi? Nasıl değiştirilmesini kullanıyorsunuz @Ruleile @ExtendWith? Sadece .start()girişi @BeforeAllmi kullanacaksınız ?
Frankie Drake

JUnit5'e geçmedim, bu yüzden sorunuzu henüz cevaplayamıyorum. Afedersiniz.
2019

Bu iyi çalıştı. Teşekkürler. İsterseniz bahar yapılandırmanızda veri kaynağı oluşturmak için aşağıdakileri kullanın:DataSource embeddedPostgresDS = EmbeddedPostgres.builder().start().getPostgresDatabase();
Sacky San

12

TestContainers'ı testler için bir PosgreSQL docker container'ı döndürmek için kullanabilirsiniz : http://testcontainers.viewdocs.io/testcontainers-java/usage/database_containers/

TestContainers bir JUnit @ Rule / @ ClassRule sağlar : bu mod, testlerinizden önce bir konteyner içinde bir veritabanı başlatır ve daha sonra onu yırtar .

Misal:

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

Artık Yandex adlı Rus Arama şirketinden PostgreSQL'in bellek içi bir sürümü var: https://github.com/yandex-qatools/postgresql-embedded

Flapdoodle OSS'nin yerleştirme sürecine dayanmaktadır.

Kullanım örneği (github sayfasından):

// 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));");

Bir ara kullanıyorum. İyi çalışıyor.

GÜNCELLENDİ : bu proje artık aktif olarak korunmuyor

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
Birden çok iş parçacığı kullanırsanız, bir JVM veya Mono çalışma zamanı katarsanız, kendi alt süreçlerinizi fork () veya buna benzer herhangi bir şey kullanırsanız, bu her türlü yeni ve heyecan verici şekilde patlamalıdır. Düzenleme : Gerçekten gömülü değil, sadece bir paketleyici.
Craig Ringer

3

Bellek içi veritabanına başvurmadan performans elde etmek için PostgreSQL yapılandırma ayarlarını da (soruda ayrıntılı olarak verilenler ve burada kabul edilen yanıtlar gibi ) kullanabilirsiniz.


OP'nin ana sorunu, performans için değil, geliştirme ve CI ortamında önyükleme birimi testlerinde basitlik için bir Postgres örneğini bellek içinde döndürmektir.
üçlü.

0

NodeJS kullanıyorsanız, postgres db'nin en yaygın özelliklerini taklit etmek için pg-mem (sorumluluk reddi: yazarım) kullanabilirsiniz.

PG davranışını çoğaltan bellek içi, izole edilmiş, platformdan bağımsız bir veritabanına sahip olacaksınız ( tarayıcılarda bile çalışır ).

Burada birim testleriniz için nasıl kullanılacağını gösteren bir makale yazdım .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.