Skip to content

Commit 659d5ec

Browse files
committed
test: add loadtest tracking memory/cpu usage
Ensures performance is maintained. We run call wait_for_many_gets() (runs 10K GET requests) in a Nix wrapper script, net-loadtest, which measures the CPU and memory each second using psrecord. net-loadtest can be used locally too. psrecord can also generate a nice png file, but AFAICT there's no way to nicely render this on Github CI. The png can be attached as an artifact, but you have to download it and open it (too slow for feedback).
1 parent 820a72e commit 659d5ec

File tree

8 files changed

+92
-10
lines changed

8 files changed

+92
-10
lines changed

.github/workflows/main.yml

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
uses: cachix/install-nix-action@v30
1818

1919
- name: Use Cachix Cache
20-
uses: cachix/cachix-action@v10
20+
uses: cachix/cachix-action@v16
2121
with:
2222
name: nxpg
2323
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
@@ -45,7 +45,7 @@ jobs:
4545
#nix_path: nixpkgs=channel:nixos-unstable
4646

4747
#- name: Use Cachix Cache
48-
#uses: cachix/cachix-action@v10
48+
#uses: cachix/cachix-action@v16
4949
#with:
5050
#name: xpg
5151
#authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
@@ -56,6 +56,28 @@ jobs:
5656
#- name: Run tests
5757
#run: nix-shell --run "nxpg -v ${{ matrix.pg-version }} test"
5858

59+
loadtest:
60+
runs-on: ubuntu-latest
61+
steps:
62+
- uses: actions/checkout@v4
63+
64+
- name: Install Nix
65+
uses: cachix/install-nix-action@v30
66+
67+
- name: Use Cachix Cache
68+
uses: cachix/cachix-action@v16
69+
with:
70+
name: nxpg
71+
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
72+
73+
- name: Build
74+
run: nix-shell --run "xpg build"
75+
76+
- name: Run load test
77+
run: |
78+
nix-shell --run "net-loadtest"
79+
cat psrecord.md >> "$GITHUB_STEP_SUMMARY"
80+
5981
coverage:
6082

6183
runs-on: ubuntu-latest
@@ -71,7 +93,7 @@ jobs:
7193
uses: cachix/install-nix-action@v30
7294

7395
- name: Use Cachix Cache
74-
uses: cachix/cachix-action@v10
96+
uses: cachix/cachix-action@v16
7597
with:
7698
name: nxpg
7799
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,4 @@ nginx.pid
2020
tags
2121
net_worker.pid
2222
sql/pg_net--*.sql
23+
psrecord.*

nix/loadtest.nix

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
{ writeShellScriptBin, psrecord, writers, python3Packages } :
2+
3+
let
4+
toMarkdown =
5+
writers.writePython3 "psrecord-to-md"
6+
{
7+
libraries = [ python3Packages.pandas python3Packages.tabulate ];
8+
}
9+
''
10+
import sys
11+
import pandas as pd
12+
import re
13+
14+
HEADER_SPLIT = re.compile(r"\s{2,}")
15+
16+
raw_lines = sys.stdin.read().splitlines()
17+
18+
header_line = next(
19+
(line for line in raw_lines if line.lstrip().startswith("#")), None
20+
)
21+
if header_line is None:
22+
sys.exit("Error: no header line found in input.")
23+
24+
columns = HEADER_SPLIT.split(header_line.lstrip("#").strip())
25+
26+
data_lines = [
27+
line.strip()
28+
for line in raw_lines
29+
if line.strip() and not line.lstrip().startswith("#")
30+
]
31+
32+
data_rows = [HEADER_SPLIT.split(line) for line in data_lines]
33+
34+
df = pd.DataFrame(data_rows, columns=columns, dtype=str)
35+
36+
df.to_markdown(sys.stdout, index=False, tablefmt="github")
37+
'';
38+
in
39+
40+
writeShellScriptBin "net-loadtest" ''
41+
set -euo pipefail
42+
43+
net-with-nginx xpg psql -c "call wait_for_many_gets()" > /dev/null &
44+
45+
# wait for process to start so we can capture it with psrecord
46+
sleep 2
47+
48+
record_log=psrecord.log
49+
record_result=psrecord.md
50+
51+
${psrecord}/bin/psrecord $(cat build-17/bgworker.pid) --interval 1 --log "$record_log" > /dev/null
52+
53+
cat $record_log | ${toMarkdown} > $record_result
54+
55+
echo "generated $record_result"
56+
''

nix/nixops.nix

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ in {
114114
};
115115
initialScript = pkgs.writeText "init-sql-script" ''
116116
create extension pg_net;
117-
${builtins.readFile ./bench.sql}
117+
${builtins.readFile ../test/loadtest/loadtest.sql}
118118
'';
119119
};
120120

@@ -163,10 +163,10 @@ in {
163163
''
164164
)
165165
(
166-
pkgs.writeShellScriptBin "psql-reproduce-timeouts" ''
166+
pkgs.writeShellScriptBin "psql-net-many-gets" ''
167167
set -euo pipefail
168168
169-
psql -U postgres -c "call repro_timeouts();"
169+
psql -U postgres -c "call wait_for_many_gets(url:='$1');"
170170
''
171171
)
172172
];

nix/xpg.nix

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ let
33
dep = fetchFromGitHub {
44
owner = "steve-chavez";
55
repo = "xpg";
6-
rev = "v1.3.2";
7-
sha256 = "sha256-ooYqMOQD9y+/87wBd33Mvbpsx+FwEMdZoibGRM4gvBk=";
6+
rev = "v1.3.3";
7+
sha256 = "sha256-N0/jp+tOWFz72Z6ZK2oA0M6e5F1W3SPhgk5G/0PbBso=";
88
};
99
xpg = import dep;
1010
in

shell.nix

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ mkShell {
99
nginxCustom = callPackage ./nix/nginxCustom.nix {};
1010
nixopsScripts = callPackage ./nix/nixopsScripts.nix {};
1111
xpg = callPackage ./nix/xpg.nix {inherit fetchFromGitHub;};
12+
loadtest = callPackage ./nix/loadtest.nix {};
1213
pythonDeps = with python3Packages; [
1314
pytest
1415
psycopg2
@@ -20,6 +21,7 @@ mkShell {
2021
pythonDeps
2122
nginxCustom.nginxScript
2223
curl
24+
loadtest
2325
] ++
2426
nixopsScripts;
2527
shellHook = ''

test/init.sql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
create database pre_existing;
22
create role pre_existing nosuperuser login;
33
create extension pg_net;
4-
\ir ../nix/bench.sql
4+
\ir ./loadtest/loadtest.sql

nix/bench.sql renamed to test/loadtest/loadtest.sql

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ select
55
(select error_msg from net._http_response where error_msg is not null order by id desc limit 1) as last_failure_error
66
from net._http_response;
77

8-
create or replace procedure repro_timeouts(number_of_requests int default 10000, url text default 'http://server') as $$
8+
-- loadtest using many gets, used to be called `repro_timeouts`
9+
create or replace procedure wait_for_many_gets(number_of_requests int default 10000, url text default 'http://localhost:8080') as $$
910
declare
1011
last_id bigint;
1112
first_time timestamptz;

0 commit comments

Comments
 (0)