Skip to content

Commit deccad7

Browse files
committed
test: negative pgrst_db_pool_available in metrics
Proves the failure on #4622. This doesn't require additional test infra, only nginx. Taking advantage of the `stream {}` context which is also compatible with unix socket besides TCP.
1 parent a971320 commit deccad7

5 files changed

Lines changed: 84 additions & 1 deletion

File tree

nix/tools/tests.nix

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
, hpc-codecov
1010
, jq
1111
, lib
12+
, nginx
1213
, postgrest
1314
, python3
1415
, runtimeShell
@@ -93,6 +94,7 @@ let
9394
args = [ "ARG_LEFTOVERS([pytest arguments])" ];
9495
workingDir = "/";
9596
withEnv = postgrest.env;
97+
withPath = [ nginx ];
9698
}
9799
''
98100
${cabal-install}/bin/cabal v2-build ${devCabalOptions} exe:postgrest
@@ -155,6 +157,7 @@ let
155157
redirectTixFiles = false;
156158
withEnv = postgrest.env;
157159
withTmpDir = true;
160+
withPath = [ nginx ];
158161
}
159162
(
160163
# required for `hpc markup` in CI; glibcLocales is not available e.g. on Darwin

test/io/config.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
FIXTURES = yaml.load(
1111
(BASEDIR / "fixtures/fixtures.yaml").read_text(), Loader=yaml.Loader
1212
)
13+
NGINX_BIN = shutil.which("nginx")
1314
POSTGREST_BIN = shutil.which("postgrest")
1415
SECRET = "reallyreallyreallyreallyverysafe"
1516

test/io/nginx/nginx.conf

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
daemon off;
2+
pid ./nginx.pid;
3+
4+
events {}
5+
6+
stream {
7+
server {
8+
listen unix:$PGPROXYHOST/.s.PGSQL.5432;
9+
proxy_timeout 10ms;
10+
proxy_pass unix:$PGHOST/.s.PGSQL.5432;
11+
}
12+
}

test/io/postgrest.py

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,13 @@
88
import subprocess
99
import tempfile
1010
import time
11+
import string
1112
import urllib.parse
1213

1314
import requests
1415
import requests_unixsocket
1516

16-
from config import POSTGREST_BIN, hpctixfile
17+
from config import POSTGREST_BIN, NGINX_BIN, hpctixfile
1718

1819

1920
def sleep_until_postgrest_scache_reload():
@@ -170,6 +171,51 @@ def run(
170171
process.wait()
171172

172173

174+
@contextlib.contextmanager
175+
def run_nginx(env=None):
176+
"Run nginx as a unix socket proxy for PostgreSQL and expose PGPROXYHOST."
177+
env = dict(os.environ if env is None else env)
178+
179+
with tempfile.TemporaryDirectory() as tmpdir:
180+
# build a <tmpdir>/conf/ so `nginx -p` picks the config automatically
181+
tmpdir = pathlib.Path(tmpdir)
182+
conf_dir = tmpdir / "conf"
183+
conf_dir.mkdir(parents=True)
184+
185+
nginx_env = dict(env)
186+
nginx_env["PGPROXYHOST"] = str(tmpdir)
187+
188+
source_conf = pathlib.Path("test/io/nginx/nginx.conf")
189+
out_conf = conf_dir / "nginx.conf"
190+
out_conf.write_text(
191+
string.Template(source_conf.read_text()).substitute(nginx_env)
192+
)
193+
194+
process = subprocess.Popen(
195+
[NGINX_BIN, "-p", str(tmpdir), "-e", "stderr"],
196+
stdout=subprocess.PIPE,
197+
stderr=subprocess.PIPE,
198+
text=True,
199+
env=nginx_env,
200+
)
201+
202+
if process.poll() is not None:
203+
(_, stderr_output) = process.communicate(timeout=1)
204+
raise RuntimeError(
205+
f"{NGINX_BIN} exited with {process.returncode}: {stderr_output}"
206+
)
207+
208+
try:
209+
yield str(tmpdir)
210+
finally:
211+
process.terminate()
212+
try:
213+
process.wait(timeout=1)
214+
except subprocess.TimeoutExpired:
215+
process.kill()
216+
process.wait()
217+
218+
173219
def freeport(used_ports=None):
174220
"Find an unused free port on localhost."
175221
while True:

test/io/test_io.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
is_ipv6,
1515
reset_statement_timeout,
1616
run,
17+
run_nginx,
1718
set_statement_timeout,
1819
sleep_until_postgrest_config_reload,
1920
sleep_until_postgrest_full_reload,
@@ -2178,3 +2179,23 @@ def test_vary_default_header_set(defaultenv):
21782179
response = postgrest.session.get("/projects")
21792180

21802181
assert response.headers["Vary"] == "Accept, Prefer, Range"
2182+
2183+
2184+
def test_negative_pool_metric(defaultenv):
2185+
"When a network failure is caused on the pg connection, the pgrst_db_pool_available is negative"
2186+
2187+
with run_nginx(defaultenv) as pgproxyhost:
2188+
env = {**defaultenv, "PGHOST": pgproxyhost}
2189+
2190+
with run(env=env, wait_for_readiness=False) as postgrest:
2191+
time.sleep(2)
2192+
2193+
response = postgrest.admin.get("/metrics", timeout=1)
2194+
assert response.status_code == 200
2195+
2196+
metrics = float(
2197+
re.search(
2198+
r"pgrst_db_pool_available (-?\d+(?:\.\d+)?)", response.text
2199+
).group(1)
2200+
)
2201+
assert metrics < 0

0 commit comments

Comments
 (0)