Skip to content

Benchmarks

Model-case suite: one test suite, two execution models

Section titled “Model-case suite: one test suite, two execution models”

bench/modelcase is a small but ordinary Go project: the data layer of a shop (database/sql, five SQL migrations with a trigger and a view, 20 categories, 500 products and 1,000 users as master data), and 70 tests against it, as tables of cases. Five read-only scenarios with ten cases each (order lists per user, order detail, top products, a product search, revenue by category) make 50 tests that only query; five writing scenarios with four cases each (create, cancel and ship an order, delete a user with its orders, apply a discount) make 20 tests that modify tables and check the rows afterwards. Every test starts from the same 40 sample orders. The same test functions run on both models; only the fixture behind them changes.

  • pgmem: TestMain boots a server in the test process, imports the schema and master data, and takes one snapshot. The read-only tests need no copy: they share the template server itself, loaded with the sample data once after the snapshot. Every writing test forks its own copy of the snapshot and loads the sample data into it. All 70 tests call t.Parallel(), so everything after the snapshot runs at once, as many at a time as -parallel allows.
  • One shared server: the official image through testcontainers-go’s PostgreSQL module in memory (PGDATA on tmpfs, fsync, synchronous_commit and full_page_writes off, plus the Ryuk reaper a fresh test process starts), native PostgreSQL 18 from devbox, or the official image with docker run. The driver starts the server, then the test binary imports the schema and master data into the shared database. The read-only tests still run in parallel, on the shared database, but a writing test cannot: it truncates the transactional tables, restores stock and prices, loads the sample data and runs, and the next writer waits its turn. That is what a suite on one server has to do to stay deterministic.

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

  • pgmem204 ms
    196 ms–219 ms over 5 runs
  • Testcontainers, one shared server1.73 s
    1.70 s–1.81 s over 5 runs
  • PostgreSQL, one shared server646 ms
    633 ms–671 ms over 5 runs
  • docker run, one shared server2.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 table
pgmem 204 ms totalin the test process; read tests share the template, each writing test forks the snapshot, all in parallel20 forks, one per writing test (20); the 50 read tests use the template database without a fork.
pgmem: Timeline of the representative run, 204 ms0500100015002000total 204 msTestMainTestMain · 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 1CreateOrder/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 2CreateOrder/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 3CreateOrder/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 4CreateOrder/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 5CancelOrder/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 6CancelOrder/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 7CancelOrder/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 8CancelOrder/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 totaltestcontainers-go postgres module on the official image, in memory (tmpfs, no fsync); writing tests take turns after a resetno copies: the 50 read tests and the 20 writing tests share one database.
Testcontainers, one shared server: Timeline of the representative run, 1.73 s0500100015002000total 1.73 sTestMainTestMain · Boot database: 761 ms (0.0 → 761 ms)761TestMain · Import schema + seed master data: 108 ms (761 → 868 ms)108Shared 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 1CreateOrder/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 2TopProducts/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 3TopProducts/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 4TopProducts/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 5TopProducts/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 6TopProducts/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 7TopProducts/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 8TopProducts/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
PostgreSQL, one shared server 646 ms totalnative PostgreSQL (devbox); writing tests take turns after a resetno copies: the 50 read tests and the 20 writing tests share one database.
PostgreSQL, one shared server: Timeline of the representative run, 646 ms0500100015002000total 646 msTestMainTestMain · Boot database: 192 ms (0.0 → 192 ms)192TestMain · Import schema + seed master data: 57 ms (192 → 249 ms)Shared database (read tests)Shared database (read tests) · Reset data: 4.2 ms (588 → 592 ms)Shared database (read tests) · Input sample data: 9.6 ms (592 → 602 ms)parallel slot 1CreateOrder/two_lines · Reset data: 6.7 ms (249 → 256 ms)CreateOrder/two_lines · Input sample data: 21 ms (256 → 277 ms)CreateOrder/two_lines · Test that modifies a table: 1.4 ms (277 → 278 ms)CreateOrder/one_line · Reset data: 7.7 ms (278 → 286 ms)CreateOrder/one_line · Input sample data: 18 ms (286 → 304 ms)CreateOrder/one_line · Test that modifies a table: 0.8 ms (304 → 304 ms)CreateOrder/five_lines · Reset data: 7.0 ms (304 → 311 ms)CreateOrder/five_lines · Input sample data: 12 ms (311 → 324 ms)CreateOrder/five_lines · Test that modifies a table: 0.9 ms (324 → 325 ms)CreateOrder/out_of_stock · Reset data: 6.3 ms (325 → 331 ms)CreateOrder/out_of_stock · Input sample data: 12 ms (331 → 343 ms)CreateOrder/out_of_stock · Test that modifies a table: 0.2 ms (343 → 344 ms)CancelOrder/paid · Reset data: 6.2 ms (344 → 350 ms)CancelOrder/paid · Input sample data: 11 ms (350 → 360 ms)CancelOrder/paid · Test that modifies a table: 0.8 ms (360 → 361 ms)CancelOrder/pending · Reset data: 5.9 ms (361 → 367 ms)CancelOrder/pending · Input sample data: 10 ms (367 → 377 ms)CancelOrder/pending · Test that modifies a table: 0.6 ms (377 → 378 ms)CancelOrder/another_paid · Reset data: 5.9 ms (378 → 384 ms)CancelOrder/another_paid · Input sample data: 9.9 ms (384 → 394 ms)CancelOrder/another_paid · Test that modifies a table: 0.6 ms (394 → 394 ms)CancelOrder/shipped · Reset data: 6.2 ms (394 → 400 ms)CancelOrder/shipped · Input sample data: 10 ms (400 → 411 ms)CancelOrder/shipped · Test that modifies a table: 0.2 ms (411 → 411 ms)ShipOrder/one_paid · Reset data: 5.4 ms (411 → 416 ms)ShipOrder/one_paid · Input sample data: 9.9 ms (416 → 426 ms)ShipOrder/one_paid · Test that modifies a table: 0.3 ms (426 → 426 ms)ShipOrder/pending_rejected · Reset data: 5.9 ms (426 → 432 ms)ShipOrder/pending_rejected · Input sample data: 9.9 ms (432 → 442 ms)ShipOrder/pending_rejected · Test that modifies a table: 0.1 ms (442 → 442 ms)ShipOrder/all_paid · Reset data: 5.8 ms (442 → 448 ms)ShipOrder/all_paid · Input sample data: 9.5 ms (448 → 458 ms)ShipOrder/all_paid · Test that modifies a table: 1.3 ms (458 → 459 ms)ShipOrder/twice · Reset data: 4.9 ms (459 → 464 ms)ShipOrder/twice · Input sample data: 9.7 ms (464 → 474 ms)ShipOrder/twice · Test that modifies a table: 0.3 ms (474 → 474 ms)DeleteUser/user_15 · Reset data: 4.4 ms (474 → 478 ms)DeleteUser/user_15 · Input sample data: 9.3 ms (478 → 488 ms)DeleteUser/user_15 · Test that modifies a table: 0.4 ms (488 → 488 ms)DeleteUser/user_22 · Reset data: 3.9 ms (488 → 492 ms)DeleteUser/user_22 · Input sample data: 9.6 ms (492 → 502 ms)DeleteUser/user_22 · Test that modifies a table: 0.6 ms (502 → 502 ms)DeleteUser/user_29 · Reset data: 4.9 ms (502 → 507 ms)DeleteUser/user_29 · Input sample data: 9.4 ms (507 → 516 ms)DeleteUser/user_29 · Test that modifies a table: 0.3 ms (516 → 517 ms)DeleteUser/user_36 · Reset data: 4.1 ms (517 → 521 ms)DeleteUser/user_36 · Input sample data: 9.4 ms (521 → 530 ms)DeleteUser/user_36 · Test that modifies a table: 0.3 ms (530 → 531 ms)ApplyDiscount/books_10 · Reset data: 3.9 ms (531 → 534 ms)ApplyDiscount/books_10 · Input sample data: 9.9 ms (534 → 544 ms)ApplyDiscount/books_10 · Test that modifies a table: 0.4 ms (544 → 545 ms)ApplyDiscount/music_25 · Reset data: 3.9 ms (545 → 549 ms)ApplyDiscount/music_25 · Input sample data: 10 ms (549 → 559 ms)ApplyDiscount/music_25 · Test that modifies a table: 0.3 ms (559 → 559 ms)ApplyDiscount/movies_50 · Reset data: 4.2 ms (559 → 563 ms)ApplyDiscount/movies_50 · Input sample data: 10 ms (563 → 574 ms)ApplyDiscount/movies_50 · Test that modifies a table: 0.5 ms (574 → 574 ms)ApplyDiscount/games_0 · Reset data: 4.0 ms (574 → 578 ms)ApplyDiscount/games_0 · Input sample data: 9.8 ms (578 → 588 ms)ApplyDiscount/games_0 · Test that modifies a table: 0.3 ms (588 → 588 ms)SearchProducts/product_0 · Test that queries data: 0.5 ms (602 → 602 ms)SearchProducts/product_2 · Test that queries data: 8.8 ms (602 → 611 ms)GetOrder/orders_6_16_26_36 · Test that queries data: 1.3 ms (611 → 613 ms)TopProducts/top_10 · Test that queries data: 0.7 ms (613 → 613 ms)TopProducts/top_20 · Test that queries data: 0.8 ms (613 → 614 ms)parallel slot 2SearchProducts/product_5 · Test that queries data: 7.8 ms (602 → 610 ms)ListOrders/user_64 · Test that queries data: 0.4 ms (610 → 610 ms)ListOrders/user_43 · Test that queries data: 0.3 ms (610 → 610 ms)ListOrders/user_36 · Test that queries data: 0.3 ms (610 → 611 ms)ListOrders/user_22 · Test that queries data: 0.1 ms (611 → 611 ms)ListOrders/user_15 · Test that queries data: 0.4 ms (611 → 611 ms)GetOrder/orders_8_18_28_38 · Test that queries data: 0.9 ms (611 → 612 ms)GetOrder/orders_3_13_23_33 · Test that queries data: 1.6 ms (612 → 614 ms)TopProducts/top_8 · Test that queries data: 1.0 ms (614 → 615 ms)parallel slot 3SearchProducts/product_3 · Test that queries data: 7.8 ms (602 → 610 ms)ListOrders/user_57 · Test that queries data: 1.0 ms (610 → 611 ms)GetOrder/orders_1_11_21_31 · Test that queries data: 1.3 ms (611 → 612 ms)GetOrder/orders_2_12_22_32 · Test that queries data: 1.3 ms (612 → 613 ms)TopProducts/top_3 · Test that queries data: 0.4 ms (613 → 614 ms)TopProducts/top_2 · Test that queries data: 0.8 ms (614 → 615 ms)parallel slot 4SearchProducts/product_9 · Test that queries data: 8.4 ms (602 → 610 ms)ListOrders/user_29 · Test that queries data: 1.0 ms (610 → 611 ms)GetOrder/orders_5_15_25_35 · Test that queries data: 1.7 ms (612 → 613 ms)TopProducts/top_30 · Test that queries data: 1.2 ms (613 → 614 ms)parallel slot 5SearchProducts/product_4 · Test that queries data: 6.7 ms (602 → 609 ms)RevenueByCategory/Movies · Test that queries data: 0.5 ms (609 → 609 ms)ListOrders/user_71 · Test that queries data: 0.3 ms (609 → 610 ms)ListOrders/user_50 · Test that queries data: 1.0 ms (610 → 611 ms)GetOrder/orders_10_20_30_40 · Test that queries data: 4.8 ms (611 → 616 ms)parallel slot 6SearchProducts/product_7 · Test that queries data: 8.7 ms (602 → 611 ms)GetOrder/orders_9_19_29_39 · Test that queries data: 1.5 ms (611 → 612 ms)TopProducts/top_1 · Test that queries data: 1.0 ms (612 → 613 ms)TopProducts/top_15 · Test that queries data: 4.0 ms (613 → 617 ms)parallel slot 7SearchProducts/product_8 · Test that queries data: 0.8 ms (602 → 603 ms)SearchProducts/product_1 · Test that queries data: 0.4 ms (603 → 603 ms)RevenueByCategory/Books · Test that queries data: 0.9 ms (603 → 604 ms)ListOrders/user_8 · Test that queries data: 0.9 ms (604 → 605 ms)RevenueByCategory/Outdoors · Test that queries data: 0.6 ms (605 → 606 ms)RevenueByCategory/Sports · Test that queries data: 0.1 ms (606 → 606 ms)RevenueByCategory/Tools · Test that queries data: 0.1 ms (606 → 606 ms)RevenueByCategory/Kitchen · Test that queries data: 0.1 ms (606 → 606 ms)RevenueByCategory/Garden · Test that queries data: 0.2 ms (606 → 607 ms)RevenueByCategory/Toys · Test that queries data: 1.7 ms (607 → 608 ms)RevenueByCategory/Games · Test that queries data: 0.7 ms (608 → 609 ms)RevenueByCategory/Music · Test that queries data: 2.5 ms (609 → 611 ms)GetOrder/orders_7_17_27_37 · Test that queries data: 1.7 ms (611 → 613 ms)TopProducts/top_50 · Test that queries data: 0.8 ms (613 → 614 ms)parallel slot 8SearchProducts/product_6 · Test that queries data: 9.3 ms (602 → 612 ms)GetOrder/orders_4_14_24_34 · Test that queries data: 1.7 ms (612 → 613 ms)TopProducts/top_5 · Test that queries data: 3.9 ms (614 → 617 ms)ms since the suite started
docker run, one shared server 2.48 s totalofficial image; writing tests take turns after a resetno copies: the 50 read tests and the 20 writing tests share one database.
docker run, one shared server: Timeline of the representative run, 2.48 s0500100015002000total 2.48 sTestMainTestMain · Boot database: 1.55 s (0.0 → 1.55 s ms)1.55 sTestMain · Import schema + seed master data: 97 ms (1.55 s → 1.65 s ms)97Shared database (read tests)Shared database (read tests) · Reset data: 5.9 ms (2.39 s → 2.39 s ms)Shared database (read tests) · Input sample data: 23 ms (2.39 s → 2.42 s ms)parallel slot 1CreateOrder/two_lines · Reset data: 10 ms (1.65 s → 1.66 s ms)CreateOrder/two_lines · Input sample data: 31 ms (1.66 s → 1.69 s ms)CreateOrder/two_lines · Test that modifies a table: 2.5 ms (1.69 s → 1.69 s ms)CreateOrder/one_line · Reset data: 9.3 ms (1.69 s → 1.7 s ms)CreateOrder/one_line · Input sample data: 27 ms (1.7 s → 1.73 s ms)CreateOrder/one_line · Test that modifies a table: 1.6 ms (1.73 s → 1.73 s ms)CreateOrder/five_lines · Reset data: 9.8 ms (1.73 s → 1.74 s ms)CreateOrder/five_lines · Input sample data: 26 ms (1.74 s → 1.76 s ms)CreateOrder/five_lines · Test that modifies a table: 2.3 ms (1.76 s → 1.77 s ms)CreateOrder/out_of_stock · Reset data: 8.4 ms (1.77 s → 1.77 s ms)CreateOrder/out_of_stock · Input sample data: 25 ms (1.77 s → 1.8 s ms)CreateOrder/out_of_stock · Test that modifies a table: 0.4 ms (1.8 s → 1.8 s ms)CancelOrder/paid · Reset data: 7.7 ms (1.8 s → 1.81 s ms)CancelOrder/paid · Input sample data: 26 ms (1.81 s → 1.83 s ms)CancelOrder/paid · Test that modifies a table: 2.7 ms (1.83 s → 1.84 s ms)CancelOrder/pending · Reset data: 7.6 ms (1.84 s → 1.84 s ms)CancelOrder/pending · Input sample data: 28 ms (1.84 s → 1.87 s ms)CancelOrder/pending · Test that modifies a table: 1.9 ms (1.87 s → 1.87 s ms)CancelOrder/another_paid · Reset data: 8.4 ms (1.87 s → 1.88 s ms)CancelOrder/another_paid · Input sample data: 27 ms (1.88 s → 1.91 s ms)CancelOrder/another_paid · Test that modifies a table: 1.7 ms (1.91 s → 1.91 s ms)CancelOrder/shipped · Reset data: 9.3 ms (1.91 s → 1.92 s ms)CancelOrder/shipped · Input sample data: 25 ms (1.92 s → 1.95 s ms)CancelOrder/shipped · Test that modifies a table: 0.4 ms (1.95 s → 1.95 s ms)ShipOrder/one_paid · Reset data: 11 ms (1.95 s → 1.96 s ms)ShipOrder/one_paid · Input sample data: 27 ms (1.96 s → 1.98 s ms)ShipOrder/one_paid · Test that modifies a table: 1.1 ms (1.98 s → 1.98 s ms)ShipOrder/pending_rejected · Reset data: 7.7 ms (1.98 s → 1.99 s ms)ShipOrder/pending_rejected · Input sample data: 26 ms (1.99 s → 2.02 s ms)ShipOrder/pending_rejected · Test that modifies a table: 0.2 ms (2.02 s → 2.02 s ms)ShipOrder/all_paid · Reset data: 8.6 ms (2.02 s → 2.03 s ms)ShipOrder/all_paid · Input sample data: 26 ms (2.03 s → 2.05 s ms)ShipOrder/all_paid · Test that modifies a table: 13 ms (2.05 s → 2.07 s ms)ShipOrder/twice · Reset data: 8.2 ms (2.07 s → 2.07 s ms)ShipOrder/twice · Input sample data: 30 ms (2.07 s → 2.1 s ms)ShipOrder/twice · Test that modifies a table: 0.8 ms (2.1 s → 2.11 s ms)DeleteUser/user_15 · Reset data: 5.9 ms (2.11 s → 2.11 s ms)DeleteUser/user_15 · Input sample data: 41 ms (2.11 s → 2.15 s ms)DeleteUser/user_15 · Test that modifies a table: 1.6 ms (2.15 s → 2.15 s ms)DeleteUser/user_22 · Reset data: 5.9 ms (2.15 s → 2.16 s ms)DeleteUser/user_22 · Input sample data: 26 ms (2.16 s → 2.19 s ms)DeleteUser/user_22 · Test that modifies a table: 1.3 ms (2.19 s → 2.19 s ms)DeleteUser/user_29 · Reset data: 6.9 ms (2.19 s → 2.19 s ms)DeleteUser/user_29 · Input sample data: 25 ms (2.19 s → 2.22 s ms)DeleteUser/user_29 · Test that modifies a table: 1.4 ms (2.22 s → 2.22 s ms)DeleteUser/user_36 · Reset data: 6.6 ms (2.22 s → 2.23 s ms)DeleteUser/user_36 · Input sample data: 27 ms (2.23 s → 2.25 s ms)DeleteUser/user_36 · Test that modifies a table: 1.1 ms (2.25 s → 2.26 s ms)ApplyDiscount/books_10 · Reset data: 5.8 ms (2.26 s → 2.26 s ms)ApplyDiscount/books_10 · Input sample data: 26 ms (2.26 s → 2.29 s ms)ApplyDiscount/books_10 · Test that modifies a table: 1.2 ms (2.29 s → 2.29 s ms)ApplyDiscount/music_25 · Reset data: 5.9 ms (2.29 s → 2.29 s ms)ApplyDiscount/music_25 · Input sample data: 28 ms (2.29 s → 2.32 s ms)ApplyDiscount/music_25 · Test that modifies a table: 1.1 ms (2.32 s → 2.32 s ms)ApplyDiscount/movies_50 · Reset data: 6.0 ms (2.32 s → 2.33 s ms)ApplyDiscount/movies_50 · Input sample data: 26 ms (2.33 s → 2.35 s ms)ApplyDiscount/movies_50 · Test that modifies a table: 1.0 ms (2.35 s → 2.36 s ms)ApplyDiscount/games_0 · Reset data: 5.6 ms (2.36 s → 2.36 s ms)ApplyDiscount/games_0 · Input sample data: 24 ms (2.36 s → 2.39 s ms)ApplyDiscount/games_0 · Test that modifies a table: 1.3 ms (2.39 s → 2.39 s ms)RevenueByCategory/Books · Test that queries data: 2.6 ms (2.42 s → 2.42 s ms)RevenueByCategory/Tools · Test that queries data: 1.0 ms (2.42 s → 2.42 s ms)RevenueByCategory/Music · Test that queries data: 0.6 ms (2.42 s → 2.42 s ms)ListOrders/user_8 · Test that queries data: 2.4 ms (2.42 s → 2.42 s ms)ListOrders/user_43 · Test that queries data: 0.5 ms (2.42 s → 2.42 s ms)ListOrders/user_71 · Test that queries data: 0.4 ms (2.42 s → 2.42 s ms)ListOrders/user_64 · Test that queries data: 0.3 ms (2.42 s → 2.42 s ms)ListOrders/user_57 · Test that queries data: 1.2 ms (2.42 s → 2.43 s ms)ListOrders/user_22 · Test that queries data: 0.7 ms (2.43 s → 2.43 s ms)ListOrders/user_36 · Test that queries data: 0.2 ms (2.43 s → 2.43 s ms)ListOrders/user_29 · Test that queries data: 0.2 ms (2.43 s → 2.43 s ms)ListOrders/user_15 · Test that queries data: 0.2 ms (2.43 s → 2.43 s ms)TopProducts/top_1 · Test that queries data: 1.1 ms (2.43 s → 2.43 s ms)TopProducts/top_15 · Test that queries data: 0.7 ms (2.43 s → 2.43 s ms)TopProducts/top_10 · Test that queries data: 0.6 ms (2.43 s → 2.43 s ms)SearchProducts/product_0 · Test that queries data: 1.1 ms (2.43 s → 2.43 s ms)TopProducts/top_50 · Test that queries data: 0.8 ms (2.43 s → 2.43 s ms)SearchProducts/product_8 · Test that queries data: 1.2 ms (2.43 s → 2.43 s ms)GetOrder/orders_10_20_30_40 · Test that queries data: 4.0 ms (2.43 s → 2.44 s ms)parallel slot 2RevenueByCategory/Garden · Test that queries data: 9.0 ms (2.42 s → 2.43 s ms)ListOrders/user_50 · Test that queries data: 3.4 ms (2.43 s → 2.43 s ms)TopProducts/top_8 · Test that queries data: 2.0 ms (2.43 s → 2.43 s ms)SearchProducts/product_3 · Test that queries data: 1.8 ms (2.43 s → 2.43 s ms)GetOrder/orders_8_18_28_38 · Test that queries data: 3.0 ms (2.43 s → 2.44 s ms)parallel slot 3RevenueByCategory/Outdoors · Test that queries data: 11 ms (2.42 s → 2.43 s ms)TopProducts/top_20 · Test that queries data: 1.6 ms (2.43 s → 2.43 s ms)TopProducts/top_3 · Test that queries data: 0.9 ms (2.43 s → 2.43 s ms)TopProducts/top_2 · Test that queries data: 0.7 ms (2.43 s → 2.43 s ms)SearchProducts/product_2 · Test that queries data: 1.0 ms (2.43 s → 2.43 s ms)SearchProducts/product_6 · Test that queries data: 0.5 ms (2.43 s → 2.43 s ms)GetOrder/orders_9_19_29_39 · Test that queries data: 2.0 ms (2.43 s → 2.43 s ms)GetOrder/orders_3_13_23_33 · Test that queries data: 1.9 ms (2.43 s → 2.44 s ms)parallel slot 4RevenueByCategory/Sports · Test that queries data: 12 ms (2.42 s → 2.43 s ms)TopProducts/top_5 · Test that queries data: 1.5 ms (2.43 s → 2.43 s ms)SearchProducts/product_5 · Test that queries data: 2.5 ms (2.43 s → 2.43 s ms)GetOrder/orders_6_16_26_36 · Test that queries data: 2.8 ms (2.43 s → 2.44 s ms)parallel slot 5RevenueByCategory/Games · Test that queries data: 14 ms (2.42 s → 2.43 s ms)SearchProducts/product_4 · Test that queries data: 1.4 ms (2.43 s → 2.43 s ms)SearchProducts/product_7 · Test that queries data: 0.4 ms (2.43 s → 2.43 s ms)GetOrder/orders_1_11_21_31 · Test that queries data: 6.0 ms (2.43 s → 2.44 s ms)parallel slot 6RevenueByCategory/Movies · Test that queries data: 17 ms (2.42 s → 2.43 s ms)GetOrder/orders_4_14_24_34 · Test that queries data: 3.9 ms (2.43 s → 2.44 s ms)parallel slot 7RevenueByCategory/Toys · Test that queries data: 15 ms (2.42 s → 2.43 s ms)SearchProducts/product_1 · Test that queries data: 1.9 ms (2.43 s → 2.43 s ms)GetOrder/orders_5_15_25_35 · Test that queries data: 2.6 ms (2.43 s → 2.44 s ms)parallel slot 8RevenueByCategory/Kitchen · Test that queries data: 11 ms (2.42 s → 2.43 s ms)TopProducts/top_30 · Test that queries data: 3.0 ms (2.43 s → 2.43 s ms)SearchProducts/product_9 · Test that queries data: 2.0 ms (2.43 s → 2.43 s ms)GetOrder/orders_7_17_27_37 · Test that queries data: 2.9 ms (2.43 s → 2.44 s ms)GetOrder/orders_2_12_22_32 · Test that queries data: 2.8 ms (2.44 s → 2.44 s ms)ms since the suite started

Time per node

NodepgmemTestcontainers, one shared serverPostgreSQL, one shared serverdocker run, one shared server
Boot database39 ms761 ms192 ms1.55 s
Import schema + seed master data47 ms108 ms57 ms97 ms
Snapshot3.2 ms———
Fork (a writing test's own copy)218 ms———
Input sample data288 ms624 ms231 ms570 ms
Reset data—140 ms111 ms161 ms
Test that queries data61 ms161 ms105 ms163 ms
Test that modifies a table16 ms21 ms10 ms38 ms
Wall clock (boot + test binary)204 ms1.73 s646 ms2.48 s

Sums over every copy or test; on pgmem the copies overlap in time, so the sums exceed the wall clock.

Every bar is one node of the execution model, timed inside the test binary (MODELCASE_TRACE); the driver times the external server boot until SELECT 1 succeeds on the app database, and the wall clock of the test binary. The total is boot plus test binary; the binary’s own start-up and exit are the few milliseconds the nodes do not cover. Each timeline is the run whose total was the median; the first execution of a freshly built binary is a warm-up and not recorded. go test runs with -parallel at GOMAXPROCS, its default. Every timeline shares one time axis.

What to read from it:

  • The shared server pays per writing test. Reset plus sample data costs about the same as a pgmem fork plus sample data, but the shared server runs them one after another and the forks run them side by side.
  • pgmem’s fixed cost is the boot and the snapshot, and both are smaller than starting a native server, let alone a container.
  • Forks are cheap and overlap. A fork clones the data directory copy-on-write (the tree is copied, file contents are shared until written) and starts a backend whose shared memory is no longer zeroed twice, so one fork alone takes about 6 ms. With eight in flight they still contend for page faults and take about 10 ms each, and the 20 forks with their sample data fit in about 120 ms of wall clock.
  • Read-only tests on one shared server serialize at transaction boundaries (one backend session per pgmem server), so on pgmem they are a little slower one by one than on a multi-process server. They are short either way.
Terminal window
cd bench/modelcase
RUNS=5 ./run.sh # pgmem, Testcontainers, devbox and docker
TARGETS="pgmem devbox" ./run.sh # without Docker

The script compiles the test binary once, runs the driver for each target and writes results/raw.jsonl (every run with its spans), results/summary.json and website/src/data/modelcase.json, which this page renders. go test ./... in that directory runs the suite on pgmem the ordinary way, without any tracing.

Start → first queryFresh copy per testIndexed SELECTSort 200k rowsBackend + test-process memoryDownload
pgmem (Go library)70 ms15 ms (fork)29 µs9 µs in-process117 ms103 MiBtest-process growth 103 MiB33.1 MB (linked into the test binary)
pgmem binary (Python / Java / Node.js)66 ms15 ms (fork)29 µs117 ms110 MiB16.3 MB (wheel / jar / npm payload)
docker run1.65 s61 ms (CREATE DATABASE … TEMPLATE)93 µs259 ms77 MiBcontainer usage 76 MiB, test-process growth 1 MiB, VM net change 433 MiB, VM absolute 1335 MiB118 MB (image layers)
Testcontainers (Go)1.09 s21 ms (Snapshot / Restore)102 µs258 ms137 MiBcontainer usage 134 MiB, test-process growth 2 MiB, VM net change 295 MiB, VM absolute 1192 MiB120 MB (image layers + Ryuk)
devbox (nix PostgreSQL)411 ms83 ms (CREATE DATABASE … TEMPLATE)28 µs228 ms36 MiBserver process tree113 MB (nix closure)

Median of 5 pgmem and binary runs on 2026-09-18: Apple M3, 8 cores, 16 GB, Darwin 27.0.0; OrbStack 2.2.3 (Docker 29.4.0); arm64. The Docker, Testcontainers and devbox comparison baselines are from 2026-09-14. The memory column compares backend usage and measured test-process growth. Testcontainers includes PostgreSQL and Ryuk container usage. VM net change and absolute footprint are separate context; VM net change can fall when OrbStack reclaims memory.

Backend and test-process memory during the benchmark

Backend and test-process memory during the benchmarkBars show medians; whiskers span the minimum and maximum across memory runs. Docker and Testcontainers sum container usage with benchmark-process growth; Testcontainers includes PostgreSQL and Ryuk. OrbStack VM net changes are separate because the VM can reclaim memory during a run.After setupAfter query and isolation runs0100200300400500pgmem (Go library)103 MiB (103–135)445 MiB (430–478)pgmem binary110 MiB (109–110)docker run77 MiB (76–77)92 MiB (92–93)Testcontainers (Go)137 MiB (136–137)180 MiB (177–180)devbox36 MiB (36–36)54 MiB (50–54)Backend + test-process memory (MiB)
Bars show medians; whiskers span the minimum and maximum across memory runs. Docker and Testcontainers sum container usage with benchmark-process growth; Testcontainers includes PostgreSQL and Ryuk. OrbStack VM net changes are separate because the VM can reclaim memory during a run.

The pgmem library and standalone binary rows were rerun on 2026-09-18 with the export-pruned AOT build: five runs for each timing/memory sample, plus a three-run Go benchmark. The stripped darwin/arm64 binary is 36.7 MB, its zip payload is 16.3 MB, and linking pgmem into a stripped Go test binary adds 33.1 MB. Docker, Testcontainers and devbox were not rerun; their comparison rows remain the 2026-09-14 baselines.

Every run is a fresh process that starts one server, loads the same schema, and then measures. The harness is in bench/alternatives.

  • Start → first query: from invoking the target’s start operation until TCP SELECT 1 succeeds. docker pull and devbox install run before timing; Devbox environment resolution is also done in the untimed setup. docker run starts a fresh container and initializes its cluster. For Devbox, the harness prepares an initialized persistent cluster and app database, then measures devbox services up -b through successful SQL readiness. Testcontainers includes the Ryuk reaper container a fresh test process starts; the pgmem binary is timed until its ready line, which the Python, Java and Node.js wrappers wait for.
  • Fresh copy per test: the median of 20 isolated copies of the prepared database (1,000 users, 10,000 orders), each followed by a connect and a join. pgmem forks a snapshot. docker run and devbox use CREATE DATABASE … TEMPLATE and DROP DATABASE. Testcontainers uses its PostgreSQL module’s Snapshot and Restore, which do the same through a template database.
  • Indexed SELECT: SELECT v FROM bench WHERE id = $1 with pgx, 3,000 iterations after warm-up. For pgmem the second number uses the in-process dialer (Server.Dial) instead of TCP.
  • Sort 200k rows: SELECT count(*) FROM (SELECT md5(i::text) AS h FROM generate_series(1, 200000) i ORDER BY h) t, median of three runs after one warm-up.
  • Memory: OrbStack’s net VM footprint can fall under load, making Testcontainers look smaller than docker run even while its containers use more memory. The chart instead compares backend usage plus measured test-process growth. pgmem uses its process’s phys_footprint increase; the binary row uses the standalone pgmem process. Docker and Testcontainers sum Docker’s usage for their containers with the Go benchmark process’s growth. Testcontainers includes both PostgreSQL and Ryuk, using the memory values reported by docker stats (the Linux CLI subtracts cache from its display). devbox sums the PostgreSQL postmaster and its child processes. The table keeps OrbStack’s net change and absolute footprint as separate host context.
  • Download: what a first run fetches. pgmem as a Go library is the growth of a stripped test binary that links it. The binary row is the compressed binary that the wheel, jar and npm package carry. Images are compressed layers for the measured architecture; the nix closure is what devbox downloads from cache.nixos.org.
  • Defaults differ. The servers use PostgreSQL’s defaults (shared_buffers=128MB). The Testcontainers benchmark mounts PostgreSQL 18’s data directory (/var/lib/postgresql/18/docker) as tmpfs; docker run and devbox use disk-backed storage. pgmem uses shared_buffers=32MB and an in-memory file system, so sorts that spill to temporary files never touch a disk.
  • The chart compares the backend and test process. It shows medians and observed ranges after schema setup and after the query/isolation workload. Testcontainers includes both PostgreSQL and Ryuk container usage. The Linux docker stats CLI subtracts cache from the displayed usage.
  • OrbStack’s net change is separate context. Each container memory run restarts the VM and records its idle footprint. The VM can reclaim memory during a run, so its net change may fall even as container usage rises. The table reports that change and the absolute VM footprint separately.
  • pgmem memory grows under load. WebAssembly linear memory is not returned to the OS while a server runs, and each live fork holds its own copy. Budget roughly the idle figure per live fork and bound the number with MaxForks.
  • The container rows include different setup costs. docker run uses disk-backed storage; Testcontainers uses tmpfs for PGDATA and includes Ryuk, so its result reflects both storage and orchestration choices.
Terminal window
cd bench/alternatives
RUNS=5 MEM_RUNS=3 ./run.sh

The script needs OrbStack and devbox, and restarts OrbStack for the memory pass; it refuses to do so while other containers run. It writes raw samples to results/raw.jsonl, medians to results/summary.json, and copies the summary to website/src/data/benchmarks.json, which is what the tables on this site render.