仅在内存中运行 PostgreSQL
我想为我编写的每个单元测试运行一个仅在内存中运行的小型 PostgreSQL 数据库。例如:
@Before
void setUp() {
String port = runPostgresOnRandomPort();
connectTo("postgres://localhost:"+port+"/in_memory_db");
// ...
}
理想情况下,我会将单个 postgres 可执行文件签入版本控制,单元测试将使用它。
类似 HSQL
,但用于 postgres。我怎样才能做到这一点?
我可以获得这样的 Postgres 版本吗?我怎样才能指示它不使用磁盘?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(11)
(将我的答案从 使用内存中 PostgreSQL 移至并概括它):
您无法在进程中运行 Pg -记忆
不,这是不可能的。 PostgreSQL 用 C 实现并编译为平台代码。与 H2 或 Derby 不同,您不能只加载
jar
并将其作为一次性内存数据库启动。它的存储是基于文件系统的,并且没有任何允许您使用纯内存数据存储的内置存储抽象。不过,您可以将其指向 ramdisk、tempfs 或其他临时文件系统存储。
与同样用 C 语言编写并编译为平台代码的 SQLite 不同,PostgreSQL 也不能在进程内加载。它需要多个进程(每个连接一个),因为它是一种多处理架构,而不是多线程架构。多处理要求意味着您必须将 postmaster 作为独立进程启动。
使用一次性容器
自从我最初写这篇文章以来,容器的使用已经变得广泛、易于理解和容易。
只需在 Docker 容器中配置一个一次性的 postgres 实例以供测试使用,然后在最后将其拆除,这应该是理所当然的事情。您可以通过
LD_PRELOAD
inglibeatmydata
禁用那个讨厌的“崩溃时不要严重损坏我的数据”功能;)。对于您想要的任何测试套件和语言或工具链,有很多包装器可以为您自动执行此操作。
替代方案:预配置连接
(在简单容器化之前编写;不再推荐)
我建议简单地编写测试以期望特定的主机名/用户名/密码正常工作,并让测试工具
CREATE DATABASE
一次性数据库,然后在运行结束时DROP DATABASE
。从属性文件、构建目标属性、环境变量等中获取数据库连接详细信息。使用您已经拥有关心的数据库的现有 PostgreSQL 实例是安全的,只要您提供给单元测试的用户是 不是超级用户,只是具有
CREATEDB</code>权限的用户。最坏的情况是您会在其他数据库中造成性能问题。出于这个原因,我更喜欢运行完全隔离的 PostgreSQL 安装进行测试。
相反:启动一个一次性的 PostgreSQL 实例进行测试
或者,如果您真的热衷于拥有您的测试工具找到
initdb
和postgres
二进制文件,运行initdb
创建数据库,将pg_hba.conf
修改为trust
,运行postgres
在随机端口上启动它,创建用户,创建数据库,然后运行测试。您甚至可以将多个架构的 PostgreSQL 二进制文件捆绑在一个 jar 中,并在运行测试之前将当前架构的二进制文件解压到临时目录。我个人认为这是一个应该避免的重大痛苦;配置测试数据库会更容易。然而,随着
postgresql.conf
中include_dir
支持的出现,事情变得更容易了;现在您只需附加一行,然后为其余所有内容编写生成的配置文件。使用 PostgreSQL 进行更快的测试
有关如何安全提高 PostgreSQL 性能以进行测试的更多信息,请参阅我之前就该主题撰写的详细答案:优化 PostgreSQL 以实现快速测试
H2 的 PostgreSQL 方言并不是真正的替代品
有些人转而使用 PostgreSQL 方言模式下的 H2 数据库来运行测试。我认为这几乎和 Rails 人员使用 SQLite 进行测试和 PostgreSQL 进行生产部署一样糟糕。
H2 支持一些 PostgreSQL 扩展并模拟 PostgreSQL 方言。然而,这只是一个模拟。 您会发现 H2 接受查询但 PostgreSQL 不接受的区域以及行为不同的区域等。在撰写本文时,您还会发现 PostgreSQL 在很多地方支持执行 H2 无法执行的操作,例如窗口函数。
如果您了解这种方法的局限性并且您的数据库访问很简单,那么 H2 可能没问题。但在这种情况下,您可能是抽象数据库的 ORM 的更好候选者,因为无论如何您都没有使用它有趣的功能 - 在这种情况下,您不必再关心数据库兼容性。
表空间不是答案!
不要使用表空间创建“内存中”数据库。它不仅是不必要的,因为无论如何它都不会显着提高性能,而且它也是中断对同一 PostgreSQL 安装中您可能关心的任何其他内容的访问的好方法。 9.4 文档现在包含以下警告:
因为我注意到太多人这样做并遇到了麻烦。
(如果你已经这样做了,你可以
mkdir
丢失的表空间目录来让PostgreSQL重新启动,然后DROP
丢失的数据库、表等。最好不要这样做它。)(Moving my answer from Using in-memory PostgreSQL and generalizing it):
You can't run Pg in-process, in-memory
No, it is not possible. PostgreSQL is implemented in C and compiled to platform code. Unlike H2 or Derby you can't just load the
jar
and fire it up as a throwaway in-memory DB.Its storage is filesystem based, and it doesn't have any built-in storage abstraction that would allow you to use a purely in-memory datastore. You can point it at a ramdisk, tempfs, or other ephemeral file system storage though.
Unlike SQLite, which is also written in C and compiled to platform code, PostgreSQL can't be loaded in-process either. It requires multiple processes (one per connection) because it's a multiprocessing, not a multithreading, architecture. The multiprocessing requirement means you must launch the postmaster as a standalone process.
Use throwaway containers
Since I originally wrote this the use of containers has become widespread, well understood and easy.
It should be a no-brainer to just configure a throw-away postgres instance in a Docker container for your test uses, then tear it down at the end. You can speed it up with hacks like
LD_PRELOAD
inglibeatmydata
to disable that pesky "don't corrupt my data horribly on crash" feature ;).There are a lot of wrappers to automate this for you for any test suite and language or toolchain you would like.
Alternative: preconfigure a connection
(Written before easy containerization; no longer recommended)
I suggest simply writing your tests to expect a particular hostname/username/password to work, and having the test harness
CREATE DATABASE
a throwaway database, thenDROP DATABASE
at the end of the run. Get the database connection details from a properties file, build target properties, environment variable, etc.It's safe to use an existing PostgreSQL instance you already have databases you care about in, so long as the user you supply to your unit tests is not a superuser, only a user with
CREATEDB
rights. At worst you'll create performance issues in the other databases. I prefer to run a completely isolated PostgreSQL install for testing for that reason.Instead: Launch a throwaway PostgreSQL instance for testing
Alternately, if you're really keen you could have your test harness locate the
initdb
andpostgres
binaries, runinitdb
to create a database, modifypg_hba.conf
totrust
, runpostgres
to start it on a random port, create a user, create a DB, and run the tests. You could even bundle the PostgreSQL binaries for multiple architectures in a jar and unpack the ones for the current architecture to a temporary directory before running the tests.Personally I think that's a major pain that should be avoided; it's way easier to just have a test DB configured. However, it's become a little easier with the advent of
include_dir
support inpostgresql.conf
; now you can just append one line, then write a generated config file for all the rest.Faster testing with PostgreSQL
For more information about how to safely improve the performance of PostgreSQL for testing purposes, see a detailed answer I wrote on this topic earlier: Optimise PostgreSQL for fast testing
H2's PostgreSQL dialect is not a true substitute
Some people instead use the H2 database in PostgreSQL dialect mode to run tests. I think that's almost as bad as the Rails people using SQLite for testing and PostgreSQL for production deployment.
H2 supports some PostgreSQL extensions and emulates the PostgreSQL dialect. However, it's just that - an emulation. You'll find areas where H2 accepts a query but PostgreSQL doesn't, where behaviour differs, etc. You'll also find plenty of places where PostgreSQL supports doing something that H2 just can't - like window functions, at the time of writing.
If you understand the limitations of this approach and your database access is simple, H2 might be OK. But in that case you're probably a better candidate for an ORM that abstracts the database because you're not using its interesting features anyway - and in that case, you don't have to care about database compatibility as much anymore.
Tablespaces are not the answer!
Do not use a tablespace to create an "in-memory" database. Not only is it unnecessary as it won't help performance significantly anyway, but it's also a great way to disrupt access to any other you might care about in the same PostgreSQL install. The 9.4 documentation now contains the following warning:
because I noticed too many people were doing this and running into trouble.
(If you've done this you can
mkdir
the missing tablespace directory to get PostgreSQL to start again, thenDROP
the missing databases, tables etc. It's better to just not do it.)或者您可以创建一个
TABLESPACE
ramfs / tempfs 并在那里创建所有对象。最近有人向我指出一篇关于在 Linux 上执行此操作的文章。原来的链接已经失效了。但它已存档(由 Arsinclair 提供):
警告
这可能会危及整个数据库集群的完整性。
阅读手册中添加的警告。
所以这只是消耗性数据的一个选项。
对于单元测试它应该工作得很好。如果您在同一台计算机上运行其他数据库,请确保使用单独的数据库集群(有自己的端口)以确保安全。
Or you could create a
TABLESPACE
in a ramfs / tempfs and create all your objects there.I recently was pointed to an article about doing exactly that on Linux. The original link is dead. But it was archived (provided by Arsinclair):
Warning
This can endanger the integrity of your whole database cluster.
Read the added warning in the manual.
So this is only an option for expendable data.
For unit-testing it should work just fine. If you are running other databases on the same machine, be sure to use a separate database cluster (which has its own port) to be safe.
这对于 Postgres 来说是不可能的。它不提供像 HSQLDB 或 MySQL 这样的进程内/内存引擎。
如果您想创建一个独立的环境,您可以将 Postgres 二进制文件放入 SVN(但它不仅仅是一个可执行文件)。
您需要运行 initdb 来设置测试数据库,然后再进行可以用这个做任何事。这可以通过批处理文件或使用 Runtime.exec() 来完成。但请注意 initdb 速度并不快。您肯定不想为每个测试都运行它。不过,您可能会在测试套件之前运行它。
然而,虽然可以做到这一点,但我建议安装一个专用的 Postgres,您只需在运行测试之前重新创建测试数据库即可。
您可以使用模板数据库重新创建测试数据库,这使得创建速度非常快(比每次测试运行 initdb 快得多)
This is not possible with Postgres. It does not offer an in-process/in-memory engine like HSQLDB or MySQL.
If you want to create a self-contained environment you can put the Postgres binaries into SVN (but it's more than just a single executable).
You will need to run initdb to setup your test database before you can do anything with this. This can be done from a batch file or by using Runtime.exec(). But note that initdb is not something that is fast. You will definitely not want to run that for each test. You might get away running this before your test-suite though.
However while this can be done, I'd recommend to have a dedicated Postgres installation where you simply recreate your test database before running your tests.
You can re-create the test-database by using a template database which makes creating it quite fast (a lot faster than running initdb for each test run)
现在可以通过 OpenTable 中的嵌入式 PostgreSQL 组件在 JUnit 测试中运行 PostgreSQL 的内存实例: https://github.com/opentable/otj-pg-embedded。
通过将依赖项添加到 otj-pg-embedded 库 (https:// mvnrepository.com/artifact/com.opentable.components/otj-pg-embedded)您可以在 @Before 和 @Afer 中启动和停止您自己的 PostgreSQL 实例hooks:
他们甚至提供 JUnit 规则来自动让 JUnit 为您启动和停止 PostgreSQL 数据库服务器:
Now it is possible to run an in-memory instance of PostgreSQL in your JUnit tests via the Embedded PostgreSQL Component from OpenTable: https://github.com/opentable/otj-pg-embedded.
By adding the dependency to the otj-pg-embedded library (https://mvnrepository.com/artifact/com.opentable.components/otj-pg-embedded) you can start and stop your own instance of PostgreSQL in your @Before and @Afer hooks:
They even offer a JUnit rule to automatically have JUnit starting and stopping your PostgreSQL database server for you:
您可以使用 TestContainers 启动 PosgreSQL docker 容器进行测试:
http://testcontainers.viewdocs.io/testcontainers-java/usage/database_containers/
TestContainers 提供JUnit @Rule/@ClassRule:此模式在测试之前在容器内启动数据库,然后将其拆除。
例子:
You could use TestContainers to spin up a PosgreSQL docker container for tests:
http://testcontainers.viewdocs.io/testcontainers-java/usage/database_containers/
TestContainers provide a JUnit @Rule/@ClassRule: this mode starts a database inside a container before your tests and tears it down afterwards.
Example:
如果您使用 NodeJS,则可以使用 pg-mem (免责声明:我是作者)模拟 postgres 数据库最常见的功能。
您将拥有一个完整的内存中、隔离的、与平台无关的数据库,复制 PG 行为(它甚至运行在浏览器中)。
我写了一篇文章来展示如何将它用于单元测试 此处。
If you are using NodeJS, you can use pg-mem (disclaimer: I'm the author) to emulate the most common features of a postgres db.
You will have a full in-memory, isolated, platform-agnostic database replicating PG behaviour (it even runs in browsers).
I wrote an article to show how to use it for your unit tests here.
如果你可以使用docker,你可以在内存中挂载postgresql数据目录进行测试
If you can use docker you can mount postgresql data directory in memory for testing
现在有一个来自 Russian Search 公司的 PostgreSQL 内存版本,名为 Yandex:https://github。 com/yandex-qatools/postgresql-embedded
它基于 Flapdoodle OSS 的嵌入流程。
使用示例(来自 github 页面):
我正在使用它一段时间。效果很好。
更新:该项目不再得到积极维护
There is now an in-memory version of PostgreSQL from Russian Search company named Yandex: https://github.com/yandex-qatools/postgresql-embedded
It's based on Flapdoodle OSS's embed process.
Example of using (from github page):
I'm using it some time. It works well.
UPDATED: this project is not being actively maintained anymore
您还可以使用 PostgreSQL 配置设置(例如问题和接受的答案中详细说明的设置 这里)来实现性能,而不必求助于内存数据库。
You can also use PostgreSQL configuration settings (such as those detailed in the question and accepted answer here) to achieve performance without necessarily resorting to an in-memory database.
理论上可以通过在 WASM 中运行 PGLite 来运行进程内 postgres(尽管它只有 javascript 的绑定)运行时如 wasmer。这将是一个复杂的系统,因此我建议在适用的情况下模拟 SQLite。
It is theoretically possible to run in-process postgres by running PGLite(it only has bindings for javascript though) in a WASM runtime like wasmer. This will be a complicated system, and so I recommend mocking SQLite instead if at all applicable.
如果可以完全控制您的环境,您可能希望在
zfs
postgreSQL >。If have full control over your environment, you arguably want to run
postgreSQL
onzfs
.