A small shop’s data layer with 70 tests: 50 that only read, 20 that
write. On a shared PostgreSQL server, here one started with
Testcontainers, the writing tests have to wait for each other, each
behind a reset of the shared data. With pgmem the suite
takes one snapshot after the migrations and seed data; the read-only tests
share that database and every writing test forks its own copy, so
everything after the snapshot runs in parallel.
The same 70 tests (50 read-only, 20 writing) on the same schema, migrations and seed data, run with go test at parallelism 8. Each timeline is the run whose total was the median of 5 runs on Apple M3 (8 cores). Bars are the nodes of each execution model; lower and to the left is faster.
Whole suite, wall clock pgmem 204 ms
196 ms–219 ms over 5 runs
Testcontainers, one shared server 1.73 s
1.70 s–1.81 s over 5 runs
PostgreSQL, one shared server 646 ms
633 ms–671 ms over 5 runs
docker run, one shared server 2.48 s
2.26 s–3.59 s over 5 runs
Tests are drawn in the 8 slots of go test -parallel; a slot shows the tests that ran in it one after another. All timelines share one time axis.
Boot database Import schema + seed master data Snapshot Fork (a writing test's own copy) Input sample data Reset data Test that queries data Test that modifies a tablepgmem 204 ms total in the test process; read tests share the template, each writing test forks the snapshot, all in parallel 20 forks, one per writing test (20); the 50 read tests use the template database without a fork. pgmem: Timeline of the representative run, 204 ms 0 500 1000 1500 total 204 ms TestMain TestMain · Boot database: 39 ms (0.0 → 39 ms) TestMain · Import schema + seed master data: 47 ms (39 → 86 ms) TestMain · Snapshot: 3.2 ms (86 → 89 ms) Shared database (read tests) Shared database (read tests) · Input sample data: 13 ms (122 → 135 ms) parallel slot 1 CreateOrder/two_lines · Fork (a writing test's own copy): 13 ms (89 → 102 ms) CreateOrder/two_lines · Input sample data: 18 ms (103 → 121 ms) CreateOrder/two_lines · Test that modifies a table: 1.2 ms (121 → 122 ms) TopProducts/top_1 · Test that queries data: 0.5 ms (135 → 135 ms) TopProducts/top_30 · Test that queries data: 0.6 ms (135 → 136 ms) TopProducts/top_8 · Test that queries data: 1.1 ms (136 → 137 ms) TopProducts/top_2 · Test that queries data: 1.0 ms (137 → 138 ms) ApplyDiscount/games_0 · Fork (a writing test's own copy): 10 ms (138 → 148 ms) ApplyDiscount/games_0 · Input sample data: 13 ms (148 → 161 ms) ApplyDiscount/games_0 · Test that modifies a table: 0.4 ms (161 → 161 ms) ListOrders/user_71 · Test that queries data: 0.1 ms (161 → 161 ms) ListOrders/user_64 · Test that queries data: 0.1 ms (161 → 161 ms) ListOrders/user_57 · Test that queries data: 0.5 ms (161 → 162 ms) ListOrders/user_22 · Test that queries data: 0.8 ms (162 → 163 ms) ListOrders/user_29 · Test that queries data: 0.5 ms (163 → 163 ms) SearchProducts/product_1 · Test that queries data: 1.0 ms (163 → 164 ms) SearchProducts/product_8 · Test that queries data: 0.8 ms (164 → 165 ms) parallel slot 2 CreateOrder/five_lines · Fork (a writing test's own copy): 14 ms (89 → 103 ms) CreateOrder/five_lines · Input sample data: 12 ms (103 → 116 ms) CreateOrder/five_lines · Test that modifies a table: 0.9 ms (116 → 116 ms) ShipOrder/one_paid · Fork (a writing test's own copy): 8.8 ms (117 → 126 ms) ShipOrder/one_paid · Input sample data: 18 ms (126 → 144 ms) ShipOrder/one_paid · Test that modifies a table: 0.4 ms (144 → 144 ms) GetOrder/orders_6_16_26_36 · Test that queries data: 3.5 ms (145 → 148 ms) RevenueByCategory/Garden · Test that queries data: 0.8 ms (148 → 149 ms) RevenueByCategory/Toys · Test that queries data: 0.3 ms (149 → 150 ms) RevenueByCategory/Games · Test that queries data: 0.3 ms (150 → 150 ms) RevenueByCategory/Music · Test that queries data: 0.4 ms (150 → 150 ms) GetOrder/orders_10_20_30_40 · Test that queries data: 2.0 ms (150 → 152 ms) SearchProducts/product_0 · Test that queries data: 0.8 ms (152 → 153 ms) DeleteUser/user_36 · Fork (a writing test's own copy): 11 ms (153 → 164 ms) DeleteUser/user_36 · Input sample data: 9.6 ms (164 → 173 ms) DeleteUser/user_36 · Test that modifies a table: 0.3 ms (173 → 174 ms) parallel slot 3 CreateOrder/out_of_stock · Fork (a writing test's own copy): 13 ms (89 → 102 ms) CreateOrder/out_of_stock · Input sample data: 20 ms (102 → 122 ms) CreateOrder/out_of_stock · Test that modifies a table: 0.2 ms (122 → 122 ms) TopProducts/top_10 · Test that queries data: 1.5 ms (135 → 136 ms) TopProducts/top_20 · Test that queries data: 1.4 ms (136 → 138 ms) ApplyDiscount/movies_50 · Fork (a writing test's own copy): 9.3 ms (138 → 147 ms) ApplyDiscount/movies_50 · Input sample data: 13 ms (147 → 160 ms) ApplyDiscount/movies_50 · Test that modifies a table: 0.6 ms (160 → 161 ms) ListOrders/user_43 · Test that queries data: 0.2 ms (161 → 161 ms) SearchProducts/product_4 · Test that queries data: 0.3 ms (161 → 162 ms) ListOrders/user_36 · Test that queries data: 0.4 ms (162 → 162 ms) ListOrders/user_15 · Test that queries data: 0.7 ms (162 → 163 ms) SearchProducts/product_2 · Test that queries data: 0.9 ms (163 → 164 ms) SearchProducts/product_6 · Test that queries data: 0.6 ms (164 → 164 ms) SearchProducts/product_5 · Test that queries data: 0.7 ms (164 → 165 ms) parallel slot 4 CreateOrder/one_line · Fork (a writing test's own copy): 16 ms (89 → 106 ms) CreateOrder/one_line · Input sample data: 14 ms (106 → 119 ms) CreateOrder/one_line · Test that modifies a table: 0.5 ms (119 → 120 ms) ShipOrder/twice · Fork (a writing test's own copy): 10 ms (120 → 130 ms) ShipOrder/twice · Input sample data: 10 ms (130 → 141 ms) ShipOrder/twice · Test that modifies a table: 3.5 ms (141 → 144 ms) GetOrder/orders_5_15_25_35 · Test that queries data: 3.1 ms (145 → 148 ms) GetOrder/orders_2_12_22_32 · Test that queries data: 2.3 ms (148 → 151 ms) RevenueByCategory/Outdoors · Test that queries data: 0.6 ms (151 → 151 ms) RevenueByCategory/Sports · Test that queries data: 0.5 ms (151 → 152 ms) GetOrder/orders_8_18_28_38 · Test that queries data: 1.8 ms (152 → 154 ms) DeleteUser/user_22 · Fork (a writing test's own copy): 8.6 ms (154 → 162 ms) DeleteUser/user_22 · Input sample data: 9.8 ms (162 → 172 ms) DeleteUser/user_22 · Test that modifies a table: 0.3 ms (172 → 172 ms) parallel slot 5 CancelOrder/paid · Fork (a writing test's own copy): 12 ms (89 → 102 ms) CancelOrder/paid · Input sample data: 17 ms (102 → 119 ms) CancelOrder/paid · Test that modifies a table: 0.7 ms (119 → 119 ms) ShipOrder/pending_rejected · Fork (a writing test's own copy): 8.3 ms (121 → 129 ms) ShipOrder/pending_rejected · Input sample data: 18 ms (129 → 146 ms) ShipOrder/pending_rejected · Test that modifies a table: 0.2 ms (146 → 147 ms) GetOrder/orders_4_14_24_34 · Test that queries data: 2.3 ms (147 → 150 ms) RevenueByCategory/Movies · Test that queries data: 0.4 ms (150 → 150 ms) GetOrder/orders_9_19_29_39 · Test that queries data: 2.1 ms (150 → 152 ms) DeleteUser/user_15 · Fork (a writing test's own copy): 5.8 ms (152 → 158 ms) DeleteUser/user_15 · Input sample data: 14 ms (158 → 172 ms) DeleteUser/user_15 · Test that modifies a table: 0.3 ms (172 → 172 ms) parallel slot 6 CancelOrder/another_paid · Fork (a writing test's own copy): 15 ms (89 → 105 ms) CancelOrder/another_paid · Input sample data: 12 ms (105 → 116 ms) CancelOrder/another_paid · Test that modifies a table: 2.8 ms (116 → 119 ms) TopProducts/top_50 · Test that queries data: 1.7 ms (135 → 136 ms) TopProducts/top_15 · Test that queries data: 2.1 ms (136 → 139 ms) ApplyDiscount/music_25 · Fork (a writing test's own copy): 11 ms (139 → 149 ms) ApplyDiscount/music_25 · Input sample data: 9.7 ms (149 → 159 ms) ApplyDiscount/music_25 · Test that modifies a table: 0.5 ms (159 → 160 ms) ListOrders/user_50 · Test that queries data: 1.4 ms (162 → 163 ms) SearchProducts/product_3 · Test that queries data: 1.8 ms (163 → 165 ms) SearchProducts/product_9 · Test that queries data: 0.3 ms (165 → 165 ms) parallel slot 7 CancelOrder/shipped · Fork (a writing test's own copy): 14 ms (90 → 104 ms) CancelOrder/shipped · Input sample data: 11 ms (104 → 115 ms) CancelOrder/shipped · Test that modifies a table: 0.5 ms (115 → 116 ms) ShipOrder/all_paid · Fork (a writing test's own copy): 6.0 ms (118 → 124 ms) ShipOrder/all_paid · Input sample data: 12 ms (124 → 136 ms) ShipOrder/all_paid · Test that modifies a table: 0.9 ms (136 → 136 ms) TopProducts/top_3 · Test that queries data: 2.3 ms (137 → 139 ms) GetOrder/orders_1_11_21_31 · Test that queries data: 0.9 ms (139 → 140 ms) GetOrder/orders_7_17_27_37 · Test that queries data: 0.5 ms (140 → 141 ms) RevenueByCategory/Books · Test that queries data: 6.7 ms (141 → 147 ms) GetOrder/orders_3_13_23_33 · Test that queries data: 3.4 ms (147 → 151 ms) RevenueByCategory/Kitchen · Test that queries data: 0.9 ms (151 → 152 ms) RevenueByCategory/Tools · Test that queries data: 0.4 ms (152 → 152 ms) ListOrders/user_8 · Test that queries data: 0.5 ms (152 → 152 ms) SearchProducts/product_7 · Test that queries data: 0.8 ms (153 → 153 ms) DeleteUser/user_29 · Fork (a writing test's own copy): 10 ms (153 → 164 ms) DeleteUser/user_29 · Input sample data: 9.6 ms (164 → 173 ms) DeleteUser/user_29 · Test that modifies a table: 0.3 ms (173 → 174 ms) parallel slot 8 CancelOrder/pending · Fork (a writing test's own copy): 13 ms (90 → 103 ms) CancelOrder/pending · Input sample data: 16 ms (103 → 119 ms) CancelOrder/pending · Test that modifies a table: 0.7 ms (119 → 120 ms) TopProducts/top_5 · Test that queries data: 2.0 ms (135 → 137 ms) ApplyDiscount/books_10 · Fork (a writing test's own copy): 7.6 ms (137 → 145 ms) ApplyDiscount/books_10 · Input sample data: 19 ms (145 → 165 ms) ApplyDiscount/books_10 · Test that modifies a table: 0.4 ms (165 → 165 ms) ms since the suite started
Testcontainers, one shared server 1.73 s total testcontainers-go postgres module on the official image, in memory (tmpfs, no fsync); writing tests take turns after a reset no copies: the 50 read tests and the 20 writing tests share one database. Testcontainers, one shared server: Timeline of the representative run, 1.73 s 0 500 1000 1500 total 1.73 s TestMain TestMain · Boot database: 761 ms (0.0 → 761 ms) 761 TestMain · Import schema + seed master data: 108 ms (761 → 868 ms) 108 Shared database (read tests) Shared database (read tests) · Reset data: 5.1 ms (1.62 s → 1.63 s ms) Shared database (read tests) · Input sample data: 29 ms (1.63 s → 1.65 s ms) parallel slot 1 CreateOrder/two_lines · Reset data: 9.2 ms (869 → 878 ms) CreateOrder/two_lines · Input sample data: 36 ms (878 → 913 ms) CreateOrder/two_lines · Test that modifies a table: 2.0 ms (913 → 915 ms) CreateOrder/one_line · Reset data: 8.1 ms (915 → 923 ms) CreateOrder/one_line · Input sample data: 28 ms (923 → 951 ms) CreateOrder/one_line · Test that modifies a table: 0.9 ms (951 → 952 ms) CreateOrder/five_lines · Reset data: 7.6 ms (952 → 960 ms) CreateOrder/five_lines · Input sample data: 27 ms (960 → 987 ms) CreateOrder/five_lines · Test that modifies a table: 1.9 ms (987 → 989 ms) CreateOrder/out_of_stock · Reset data: 7.5 ms (989 → 996 ms) CreateOrder/out_of_stock · Input sample data: 27 ms (996 → 1.02 s ms) CreateOrder/out_of_stock · Test that modifies a table: 0.4 ms (1.02 s → 1.02 s ms) CancelOrder/paid · Reset data: 7.0 ms (1.02 s → 1.03 s ms) CancelOrder/paid · Input sample data: 26 ms (1.03 s → 1.06 s ms) CancelOrder/paid · Test that modifies a table: 1.7 ms (1.06 s → 1.06 s ms) CancelOrder/pending · Reset data: 6.8 ms (1.06 s → 1.07 s ms) CancelOrder/pending · Input sample data: 27 ms (1.07 s → 1.09 s ms) CancelOrder/pending · Test that modifies a table: 1.3 ms (1.09 s → 1.09 s ms) CancelOrder/another_paid · Reset data: 6.8 ms (1.09 s → 1.1 s ms) CancelOrder/another_paid · Input sample data: 28 ms (1.1 s → 1.13 s ms) CancelOrder/another_paid · Test that modifies a table: 1.4 ms (1.13 s → 1.13 s ms) CancelOrder/shipped · Reset data: 6.9 ms (1.13 s → 1.14 s ms) CancelOrder/shipped · Input sample data: 28 ms (1.14 s → 1.17 s ms) CancelOrder/shipped · Test that modifies a table: 0.4 ms (1.17 s → 1.17 s ms) ShipOrder/one_paid · Reset data: 6.6 ms (1.17 s → 1.17 s ms) ShipOrder/one_paid · Input sample data: 28 ms (1.17 s → 1.2 s ms) ShipOrder/one_paid · Test that modifies a table: 0.5 ms (1.2 s → 1.2 s ms) ShipOrder/pending_rejected · Reset data: 6.1 ms (1.2 s → 1.21 s ms) ShipOrder/pending_rejected · Input sample data: 30 ms (1.21 s → 1.24 s ms) ShipOrder/pending_rejected · Test that modifies a table: 0.2 ms (1.24 s → 1.24 s ms) ShipOrder/all_paid · Reset data: 7.7 ms (1.24 s → 1.24 s ms) ShipOrder/all_paid · Input sample data: 40 ms (1.24 s → 1.28 s ms) ShipOrder/all_paid · Test that modifies a table: 4.2 ms (1.28 s → 1.29 s ms) ShipOrder/twice · Reset data: 8.4 ms (1.29 s → 1.3 s ms) ShipOrder/twice · Input sample data: 37 ms (1.3 s → 1.33 s ms) ShipOrder/twice · Test that modifies a table: 0.5 ms (1.33 s → 1.34 s ms) DeleteUser/user_15 · Reset data: 4.9 ms (1.34 s → 1.34 s ms) DeleteUser/user_15 · Input sample data: 32 ms (1.34 s → 1.37 s ms) DeleteUser/user_15 · Test that modifies a table: 0.8 ms (1.37 s → 1.37 s ms) DeleteUser/user_22 · Reset data: 5.3 ms (1.37 s → 1.38 s ms) DeleteUser/user_22 · Input sample data: 30 ms (1.38 s → 1.41 s ms) DeleteUser/user_22 · Test that modifies a table: 0.6 ms (1.41 s → 1.41 s ms) DeleteUser/user_29 · Reset data: 5.9 ms (1.41 s → 1.41 s ms) DeleteUser/user_29 · Input sample data: 29 ms (1.41 s → 1.44 s ms) DeleteUser/user_29 · Test that modifies a table: 0.6 ms (1.44 s → 1.44 s ms) DeleteUser/user_36 · Reset data: 6.0 ms (1.44 s → 1.45 s ms) DeleteUser/user_36 · Input sample data: 28 ms (1.45 s → 1.48 s ms) DeleteUser/user_36 · Test that modifies a table: 0.6 ms (1.48 s → 1.48 s ms) ApplyDiscount/books_10 · Reset data: 5.7 ms (1.48 s → 1.48 s ms) ApplyDiscount/books_10 · Input sample data: 27 ms (1.48 s → 1.51 s ms) ApplyDiscount/books_10 · Test that modifies a table: 1.0 ms (1.51 s → 1.51 s ms) ApplyDiscount/music_25 · Reset data: 6.0 ms (1.51 s → 1.52 s ms) ApplyDiscount/music_25 · Input sample data: 30 ms (1.52 s → 1.55 s ms) ApplyDiscount/music_25 · Test that modifies a table: 0.6 ms (1.55 s → 1.55 s ms) ApplyDiscount/movies_50 · Reset data: 5.3 ms (1.55 s → 1.55 s ms) ApplyDiscount/movies_50 · Input sample data: 29 ms (1.55 s → 1.58 s ms) ApplyDiscount/movies_50 · Test that modifies a table: 0.6 ms (1.58 s → 1.58 s ms) ApplyDiscount/games_0 · Reset data: 7.1 ms (1.58 s → 1.59 s ms) ApplyDiscount/games_0 · Input sample data: 29 ms (1.59 s → 1.62 s ms) ApplyDiscount/games_0 · Test that modifies a table: 0.6 ms (1.62 s → 1.62 s ms) TopProducts/top_1 · Test that queries data: 1.0 ms (1.65 s → 1.66 s ms) TopProducts/top_3 · Test that queries data: 0.8 ms (1.66 s → 1.66 s ms) TopProducts/top_2 · Test that queries data: 1.6 ms (1.66 s → 1.66 s ms) RevenueByCategory/Books · Test that queries data: 3.5 ms (1.66 s → 1.66 s ms) RevenueByCategory/Outdoors · Test that queries data: 1.9 ms (1.66 s → 1.66 s ms) RevenueByCategory/Sports · Test that queries data: 0.5 ms (1.66 s → 1.66 s ms) RevenueByCategory/Tools · Test that queries data: 0.8 ms (1.66 s → 1.66 s ms) RevenueByCategory/Kitchen · Test that queries data: 0.6 ms (1.66 s → 1.67 s ms) RevenueByCategory/Garden · Test that queries data: 0.8 ms (1.67 s → 1.67 s ms) RevenueByCategory/Games · Test that queries data: 0.6 ms (1.67 s → 1.67 s ms) RevenueByCategory/Movies · Test that queries data: 0.4 ms (1.67 s → 1.67 s ms) ListOrders/user_8 · Test that queries data: 1.0 ms (1.67 s → 1.67 s ms) ListOrders/user_57 · Test that queries data: 0.6 ms (1.67 s → 1.67 s ms) ListOrders/user_43 · Test that queries data: 1.2 ms (1.67 s → 1.67 s ms) GetOrder/orders_7_17_27_37 · Test that queries data: 2.9 ms (1.67 s → 1.67 s ms) SearchProducts/product_7 · Test that queries data: 1.9 ms (1.67 s → 1.67 s ms) parallel slot 2 TopProducts/top_10 · Test that queries data: 14 ms (1.65 s → 1.67 s ms) ListOrders/user_50 · Test that queries data: 1.6 ms (1.67 s → 1.67 s ms) GetOrder/orders_5_15_25_35 · Test that queries data: 2.7 ms (1.67 s → 1.67 s ms) SearchProducts/product_6 · Test that queries data: 0.8 ms (1.67 s → 1.67 s ms) SearchProducts/product_2 · Test that queries data: 1.9 ms (1.67 s → 1.68 s ms) parallel slot 3 TopProducts/top_50 · Test that queries data: 13 ms (1.65 s → 1.67 s ms) ListOrders/user_71 · Test that queries data: 2.0 ms (1.67 s → 1.67 s ms) GetOrder/orders_9_19_29_39 · Test that queries data: 3.1 ms (1.67 s → 1.67 s ms) SearchProducts/product_8 · Test that queries data: 1.4 ms (1.67 s → 1.67 s ms) parallel slot 4 TopProducts/top_30 · Test that queries data: 12 ms (1.65 s → 1.67 s ms) RevenueByCategory/Music · Test that queries data: 2.1 ms (1.67 s → 1.67 s ms) ListOrders/user_36 · Test that queries data: 0.3 ms (1.67 s → 1.67 s ms) GetOrder/orders_1_11_21_31 · Test that queries data: 2.4 ms (1.67 s → 1.67 s ms) GetOrder/orders_3_13_23_33 · Test that queries data: 1.5 ms (1.67 s → 1.67 s ms) SearchProducts/product_5 · Test that queries data: 0.9 ms (1.67 s → 1.67 s ms) SearchProducts/product_1 · Test that queries data: 0.5 ms (1.67 s → 1.67 s ms) parallel slot 5 TopProducts/top_20 · Test that queries data: 14 ms (1.65 s → 1.67 s ms) ListOrders/user_22 · Test that queries data: 0.5 ms (1.67 s → 1.67 s ms) GetOrder/orders_10_20_30_40 · Test that queries data: 2.9 ms (1.67 s → 1.67 s ms) GetOrder/orders_2_12_22_32 · Test that queries data: 1.9 ms (1.67 s → 1.67 s ms) parallel slot 6 TopProducts/top_15 · Test that queries data: 11 ms (1.65 s → 1.67 s ms) RevenueByCategory/Toys · Test that queries data: 3.4 ms (1.67 s → 1.67 s ms) ListOrders/user_29 · Test that queries data: 1.1 ms (1.67 s → 1.67 s ms) GetOrder/orders_6_16_26_36 · Test that queries data: 3.5 ms (1.67 s → 1.67 s ms) SearchProducts/product_4 · Test that queries data: 1.1 ms (1.67 s → 1.67 s ms) parallel slot 7 TopProducts/top_8 · Test that queries data: 13 ms (1.66 s → 1.67 s ms) ListOrders/user_64 · Test that queries data: 1.2 ms (1.67 s → 1.67 s ms) ListOrders/user_15 · Test that queries data: 1.4 ms (1.67 s → 1.67 s ms) GetOrder/orders_4_14_24_34 · Test that queries data: 2.2 ms (1.67 s → 1.67 s ms) SearchProducts/product_0 · Test that queries data: 1.0 ms (1.67 s → 1.67 s ms) SearchProducts/product_3 · Test that queries data: 2.5 ms (1.67 s → 1.68 s ms) parallel slot 8 TopProducts/top_5 · Test that queries data: 15 ms (1.66 s → 1.67 s ms) GetOrder/orders_8_18_28_38 · Test that queries data: 2.9 ms (1.67 s → 1.67 s ms) SearchProducts/product_9 · Test that queries data: 1.5 ms (1.67 s → 1.67 s ms) ms since the suite started
The suite, the harness and every node’s timing are described on the
benchmarks page ; the project is in
bench/modelcase .
The real thing
PostgreSQL 18.3 compiled to WebAssembly and translated to Go. Your SQL, planner behaviour and error codes are PostgreSQL’s, not an emulator’s.
Nothing to install
Install the library and its platform binary. No Docker daemon, no container runtime on CI, no database files written to disk.
Share reads, isolate writes
Run migrations and seed data once, then share a fork across read-only tests and give writing tests isolated copies. Writing tests can run in parallel.
Any driver
pgmem listens on the PostgreSQL wire protocol, so pgx, psycopg, JDBC, node-postgres, ORMs and migration tools work unchanged.
s , err := pgmem . Start ( ctx , pgmem.Options{ Database : " app " })
db , _ := sql . Open ( " pgx " , s . DSN ())
with pgmem. start ( database = " app " ) as pg:
with psycopg. connect ( pg.template.dsn ) as conn:
try ( Pgmem pg = Pgmem . builder () . database ( " app " ) . start () ;
Connection conn = DriverManager . getConnection ( pg . template () . jdbcUrl ()) ) {
conn . createStatement () . execute ( " SELECT 1 " ) ;
import { PgmemServer } from ' @pgmem/core ' ;
const server = await PgmemServer . start ( { database: ' app ' } );
const pool = new pg . Pool ( { connectionString: server . url } );
try { await pool . query ( ' SELECT 1 ' ); }
finally { await pool . end (); }
} finally { await server . close (); }
The same schema, queries and per-test isolation, measured on one machine against the official image with docker run, Testcontainers, and a devbox-installed PostgreSQL.
pgmem values are medians from 5 runs on 2026-09-18; the Docker, Testcontainers and devbox comparison baselines are from 2026-09-14. Docker and Testcontainers memory uses 3 fresh OrbStack runs. Bars scale to the largest value in each card; lower is better.
Start to first query From command start to TCP SELECT 1; downloads are excluded and Devbox uses an initialized cluster.
pgmem · Go library 70 ms
Docker · PostgreSQL 18 1.65 s
Testcontainers · Go 1.09 s
devbox · services up 411 ms
Fresh copy per test Median time to create an isolated database copy and run a join.
Indexed SELECT Warm indexed SELECT over TCP.
pgmem · Go library 29 µs
Docker · PostgreSQL 18 93 µs
Testcontainers · Go 102 µs
devbox · services up 28 µs
Sort 200k rows Median time to sort and count 200k rows.
pgmem · Go library 117 ms
Docker · PostgreSQL 18 259 ms
Testcontainers · Go 258 ms
devbox · services up 228 ms
Backend + test-process memory After setup, before queries. Docker and Testcontainers sum container usage (PostgreSQL, plus Ryuk for Testcontainers) and benchmark-process growth. OrbStack VM changes are reported separately.
pgmem · Go library 103 MiB
Docker · PostgreSQL 18 77 MiB
Testcontainers · Go 137 MiB
devbox · services up 36 MiB
Initial download footprint Same MB scale; Go link growth, compressed image layers, and devbox cache download measure different things.
pgmem · Go library 33.1 MB
Docker · PostgreSQL 18 117.9 MB
Testcontainers · Go 120.0 MB
devbox · services up 112.7 MB
The current stripped darwin/arm64 pgmem binary is 36.7 MB, including the
export-pruned AOT backend. The benchmark page records the latest pgmem-only
rerun and keeps the comparison baselines reproducible.
Query latency, methodology and how to reproduce are on the benchmarks page .