메모리에서만 PostgreSQL 실행


104

내가 작성한 각 단위 테스트에 대해 메모리에서만 실행되는 작은 PostgreSQL 데이터베이스를 실행하고 싶습니다. 예를 들면 :

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

이상적으로는 단위 테스트에서 사용할 단일 postgres 실행 파일을 버전 제어에 체크인합니다.

같은 것 HSQL 것이지만 postgres를 위해. 어떻게 할 수 있습니까?

그런 Postgres 버전을 구할 수 있었습니까? 디스크를 사용하지 않도록 어떻게 지시 할 수 있습니까?

답변:


49

Postgres에서는 불가능합니다. HSQLDB 또는 MySQL과 같은 in-process / in-memory 엔진을 제공하지 않습니다.

독립적 인 환경을 만들고 싶다면 Postgres 바이너리를 SVN에 넣을 수 있습니다 (단 하나의 실행 파일 이상입니다).

이 작업을 수행하기 전에 initdb 를 실행 하여 테스트 데이터베이스를 설정해야합니다. 이 작업은 배치 파일에서 또는 Runtime.exec ()를 사용하여 수행 할 수 있습니다. 그러나 initdb는 빠른 것이 아닙니다. 각 테스트에 대해 실행하고 싶지는 않을 것입니다. 그래도 테스트 스위트 전에 이것을 실행할 수 있습니다.

그러나 이렇게 할 수는 있지만 테스트를 실행하기 전에 테스트 데이터베이스를 다시 만드는 전용 Postgres 설치를 사용하는 것이 좋습니다.

템플릿 데이터베이스를 사용하여 테스트 데이터베이스를 다시 만들 수 있습니다. 템플릿 데이터베이스를 사용하면 매우 빠르게 생성 할 수 있습니다 ( 각 테스트 실행에 대해 initdb를 실행하는 것보다 훨씬 빠름).


8
아래 Erwin의 두 번째 답변이 정답으로 표시되어야하는 것 같습니다
vfclists

3
@vfclists 사실 램 디스크의 테이블 스페이스는 정말 나쁜 생각입니다. 그러지 마. postgresql.org/docs/devel/static/manage-ag-tablespaces.html , stackoverflow.com/q/9407442/398670
Craig Ringer

1
@CraigRinger :이 특정 질문에 대해 명확히하기 위해 : 귀중한 데이터와 혼합하는 것은 좋지 않습니다 (경고에 감사드립니다). 전용 DB 클러스터를 사용한 단위 테스트의 경우 ramdisk가 좋습니다.
Erwin Brandstetter 2015

1
도커 사용이 일반화되면서 일부 사람들은 testcontainers기본적으로 테스트 시작을 일회용,도 커화, postgres- 인스턴스로 할 수있는 같은 도구로 성공했습니다 . github.com/testcontainers/testcontainers-java/blob/master/…
Hans Westerbeek

1
@ekcrisp. 그것은 Postgres의 진정한 임베디드 버전이 아닙니다. Postgres 인스턴스 (별도의 프로세스에서)를 더 쉽게 시작할 수있는 래퍼 라이브러리입니다. Postgres는 JVM을 실행하는 동일한 프로세스에서 "내장"되지 않고 Java 응용 프로그램의 "외부"에서 계속 실행됩니다
a_horse_with_no_name

77

( 메모리 내 PostgreSQL 사용에서 내 대답을 이동 하고 일반화) :

Pg in-process, in-memory를 실행할 수 없습니다.

테스트를 위해 메모리 내 Postgres 데이터베이스를 실행하는 방법을 알 수 없습니다. 가능할까요?

아니요, 불가능합니다. PostgreSQL은 C로 구현되고 플랫폼 코드로 컴파일됩니다. H2 또는 Derby와는 달리 jar일회용 인 메모리 DB로 로드하고 실행할 수 없습니다 .

C로 작성되고 플랫폼 코드로 컴파일되는 SQLite와 달리 PostgreSQL은 프로세스 내에서도로드 할 수 없습니다. 멀티 스레딩 아키텍처가 아닌 멀티 프로세싱이기 때문에 여러 프로세스 (연결 당 하나)가 필요합니다. 다중 처리 요구 사항 은 포스트 마스터를 독립 실행 형 프로세스로 시작 해야 함을 의미합니다 .

대신 : 연결 사전 구성

특정 호스트 이름 / 사용자 이름 / 암호가 작동 할 것으로 예상하는 테스트를 작성 하고 실행이 끝날 때 테스트 CREATE DATABASE가 일회용 데이터베이스 DROP DATABASE를 사용하도록하는 것이 좋습니다. 속성 파일, 빌드 대상 속성, 환경 변수 등에서 데이터베이스 연결 세부 정보를 가져옵니다.

그것은 당신이 이미 당신이 걱정하는 데이터베이스가 기존의 PostgreSQL 인스턴스를 사용하는 것이 안전 너무 오래 당신이 당신의 단위 테스트에 제공 한 사용자가 같이 하지 슈퍼 유저와 사용자 만 CREATEDB권한. 최악의 경우 다른 데이터베이스에서 성능 문제가 발생합니다. 이러한 이유로 테스트를 위해 완전히 격리 된 PostgreSQL 설치를 실행하는 것을 선호합니다.

대신 : 테스트를 위해 일회용 PostgreSQL 인스턴스 시작

또는 정말 열심이라면 테스트 하네스가 initdbpostgres바이너리를 찾고, initdb데이터베이스를 생성하기 위해 실행 하고,로 수정 pg_hba.conf하고 trust, postgres임의의 포트에서 시작하기 위해 실행 하고, 사용자를 생성하고, DB를 생성하고, 테스트를 실행할 수 있습니다. 여러 아키텍처에 대한 PostgreSQL 바이너리를 jar에 번들로 묶고 테스트를 실행하기 전에 현재 아키텍처에 대한 바이너리를 임시 디렉토리에 압축 해제 할 수도 있습니다.

개인적으로 그것은 피해야 할 큰 고통이라고 생각합니다. 테스트 DB를 구성하는 것이 더 쉽습니다. 그러나에서 include_dir지원 의 출현으로 조금 더 쉬워 졌습니다 postgresql.conf. 이제 한 줄만 추가 한 다음 나머지 모든 구성 파일을 작성할 수 있습니다.

PostgreSQL로 더 빠른 테스트

테스트 목적으로 PostgreSQL의 성능 을 안전하게 개선하는 방법에 대한 자세한 내용은 이전에이 주제에 대해 작성한 자세한 답변 : 빠른 테스트를 위해 PostgreSQL 최적화를 참조하십시오.

H2의 PostgreSQL 언어는 진정한 대체물이 아닙니다.

일부 사람들은 대신 PostgreSQL 언어 모드에서 H2 데이터베이스를 사용하여 테스트를 실행합니다. 그것은 테스트에 SQLite를 사용하고 프로덕션 배포에 PostgreSQL을 사용하는 Rails 사람들만큼이나 나쁘다고 생각합니다.

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 2014

좋아요, 그것은 "내가 생각한""매우 무서운" 둘 다입니다 . RAMDrive 솔루션은 유용한 데이터가없는 로컬 DB에만 속합니다. 하지만 왜 누군가 자신의 컴퓨터가 아닌 컴퓨터에 대해 단위 테스트를 실행하려고할까요? 귀하의 답변에 따르면 Tablespaces + RamDisk는 로컬 컴퓨터에서만 실행되는 PGSQL의 실제 단위 테스트 인스턴스에 대해 완벽하게 합법적으로 들립니다.
Gates VP

1
@GatesVP 어떤 사람들은 관심있는 것을 로컬 머신에 보관합니다. 괜찮지 만 동일한 DB 설치에 대해 단위 테스트를 실행하는 것은 약간 어리석은 일입니다. 하지만 사람들은 어리 석습니다. 그들 중 일부는 또한 적절한 백업을 유지하지 않습니다. 통곡이 계속됩니다.
Craig Ringer 2014

어쨌든, 램 디스크 옵션으로 가려면 램 디스크에서도 WAL을 원하기 때문에 initdb거기에 완전히 새로운 Pg를 설치하는 것이 좋습니다. 그러나 실제로는 일반적인 저장소에서 빠른 테스트를 위해 조정 된 Pg (fsync = off 및 기타 데이터 내구성 / 안전 기능 해제) 간에는 최소한 Linux에서 실행하는 것보다 약간의 차이가 있습니다.
Craig Ringer 2014

66

또는 ramfs / tempfs에 TABLESPACE 를 만들고 거기에 모든 개체를 만들 수 있습니다.
나는 최근 에 리눅스에서 정확히 그렇게하는 것에 대한 기사를 봤다 .

경고

이로 인해 전체 데이터베이스 클러스터 의 무결성이 위험해질 수 있습니다 .
설명서에 추가 된 경고를 읽으십시오.
따라서 이것은 소모성 데이터에 대한 옵션 일뿐입니다.

대한 단위 테스트 가 잘 작동합니다. 동일한 시스템에서 다른 데이터베이스를 실행하는 경우 안전을 위해 별도의 데이터베이스 클러스터 (자체 포트가 있음)를 사용해야합니다.


4
정말 나쁜 충고라고 생각합니다. 이러지 마세요. 대신 initdbtempfs 또는 ramdisk의 새 postgres 인스턴스. 마십시오 하지 tempfs 등의 테이블 스페이스를 사용, 그것은 깨지기와 무의미. 일반 테이블 스페이스를 사용하고 테이블을 생성하는 것이 더 낫습니다 UNLOGGED. 비슷하게 수행됩니다. 또한 전체 DB의 무결성을 위험에 빠뜨리는 조치를 취하지 않는 한 WAL 성능 및 fsync 요소를 다루지 않습니다 ( stackoverflow.com/q/9407442/398670 참조 ). 하지마.
Craig Ringer

29

이제 OpenTable ( https://github.com/opentable/otj-pg-embedded )의 Embedded PostgreSQL 구성 요소를 통해 JUnit 테스트에서 PostgreSQL의 메모리 내 인스턴스를 실행할 수 있습니다 .

otj-pg-embedded 라이브러리 ( https://mvnrepository.com/artifact/com.opentable.components/otj-pg-embedded )에 종속성을 추가하여 @Before에서 자체 PostgreSQL 인스턴스를 시작하고 중지 할 수 있습니다. @Afer 후크 :

EmbeddedPostgres pg = EmbeddedPostgres.start();

JUnit이 자동으로 PostgreSQL 데이터베이스 서버를 시작하고 중지하도록하는 JUnit 규칙도 제공합니다.

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

1
6 개월 후이 패키지에 대한 경험은 어떻습니까? 잘 작동합니까, 아니면 버그로 가득 차 있습니까?
oligofren

@Rubms JUnit5로 마이그레이션 했습니까? @Rule와의 대체를 어떻게 사용 @ExtendWith합니까? 그냥 사용 .start()@BeforeAll?
Frankie Drake

JUnit5로 마이그레이션하지 않았으므로 아직 질문에 답변 할 수 없습니다. 죄송합니다.
Rubms 2011 년

이것은 잘 작동했습니다. 감사. 원하는 경우 다음을 사용하여 봄 구성에서 데이터 소스를 만듭니다.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

이제 Yandex라는 러시아어 검색 회사의 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 ()하는 등의 모든 종류의 새롭고 흥미로운 방식으로 폭발해야합니다. 편집 : 그것은 실제로 포함되어 있지 않으며 단지 래퍼입니다.
Craig Ringer

3

또한 PostgreSQL 구성 설정 (예 : 질문에 자세히 설명되어 있고 여기에서 수락 된 답변 )을 사용하여 반드시 인 메모리 데이터베이스에 의존하지 않고도 성능을 달성 할 수 있습니다.


OP의 주요 문제는 성능이 아니라 개발 및 CI 환경에서 단위 테스트를 부트 스트랩하는 단순성을 위해 Postgres 인스턴스를 메모리 내에서 회전시키는 것입니다.
triple.vee

0

NodeJS를 사용하는 경우 pg-mem을 사용할 수 있습니다. (면책 조항 : 저자입니다)을 사용하여 postgres db의 가장 일반적인 기능을 에뮬레이션 할 수 있습니다.

PG 동작을 복제하는 완전한 인 메모리, 격리 된, 플랫폼에 구애받지 않는 데이터베이스를 갖게 됩니다 (브라우저 에서도 실행됩니다. ).

여기에 단위 테스트에 사용하는 방법을 보여주는 기사를 썼습니다 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.