Skip to content

Commit 79fc013

Browse files
authored
Merge pull request #17 from ololobus/tpc-ds
Add TPC-DS stress tests
2 parents 830cc91 + 8545705 commit 79fc013

13 files changed

+447
-284
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,5 @@ pg_query_state--*.sql
99
cscope.out
1010
tags
1111
Dockerfile
12+
tmp_stress
13+

.travis.yml

+3-3
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@ notifications:
1818
on_failure: always
1919

2020
env:
21-
- PG_VERSION=12 LEVEL=hardcore
21+
- PG_VERSION=12 LEVEL=hardcore USE_TPCDS=1
2222
- PG_VERSION=12
23-
- PG_VERSION=11 LEVEL=hardcore
23+
- PG_VERSION=11 LEVEL=hardcore USE_TPCDS=1
2424
- PG_VERSION=11
25-
- PG_VERSION=10 LEVEL=hardcore
25+
- PG_VERSION=10 LEVEL=hardcore USE_TPCDS=1
2626
- PG_VERSION=10
2727
- PG_VERSION=9.6 LEVEL=hardcore
2828
- PG_VERSION=9.6

Dockerfile.tmpl

+3-3
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@ FROM postgres:${PG_VERSION}-alpine
22

33
# Install dependencies
44
RUN apk add --no-cache \
5-
openssl curl \
5+
openssl curl git \
66
perl perl-ipc-run \
77
make musl-dev gcc bison flex coreutils \
88
zlib-dev libedit-dev \
99
clang clang-analyzer linux-headers \
10-
python2 python2-dev py2-virtualenv;
10+
python3 python3-dev py3-virtualenv;
1111

1212

1313
# Install fresh valgrind
@@ -35,4 +35,4 @@ ADD . /pg/testdir
3535
WORKDIR /pg/testdir
3636

3737
USER postgres
38-
ENTRYPOINT LEVEL=${LEVEL} /run.sh
38+
ENTRYPOINT LEVEL=${LEVEL} USE_TPCDS=${USE_TPCDS} /run.sh

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ DATA_built = $(EXTENSION)--$(EXTVERSION).sql
99
PGFILEDESC = "pg_query_state - facility to track progress of plan execution"
1010

1111
EXTRA_CLEAN = ./isolation_output $(EXTENSION)--$(EXTVERSION).sql \
12-
Dockerfile ./tests/*.pyc
12+
Dockerfile ./tests/*.pyc ./tmp_stress
1313

1414
ifdef USE_PGXS
1515
PG_CONFIG ?= pg_config

README.md

+27-12
Original file line numberDiff line numberDiff line change
@@ -27,26 +27,41 @@ Add module name to the `shared_preload_libraries` parameter in `postgresql.conf`
2727
shared_preload_libraries = 'pg_query_state'
2828
```
2929
It is essential to restart the PostgreSQL instance. After that, execute the following query in psql:
30-
```
30+
```sql
3131
CREATE EXTENSION pg_query_state;
3232
```
3333
Done!
3434

3535
## Tests
36-
Tests using parallel sessions using python 2.7 script:
37-
```
38-
python tests/pg_qs_test_runner.py [OPTION]...
39-
```
36+
Test using parallel sessions with Python 3+ compatible script:
37+
```shell
38+
python tests/pg_qs_test_runner.py [OPTION]...
39+
```
4040
*prerequisite packages*:
4141
* `psycopg2` version 2.6 or later
4242
* `PyYAML` version 3.11 or later
43-
43+
* `progressbar2` for stress test progress reporting
44+
4445
*options*:
4546
* *- -host* --- postgres server host, default value is *localhost*
4647
* *- -port* --- postgres server port, default value is *5432*
4748
* *- -database* --- database name, default value is *postgres*
4849
* *- -user* --- user name, default value is *postgres*
4950
* *- -password* --- user's password, default value is empty
51+
* *- -tpc-ds* --- runs only stress tests on TPC-DS benchmark
52+
53+
Or run all tests in `Docker` using:
54+
55+
```shell
56+
export LEVEL=hardcore
57+
export USE_TPCDS=1
58+
export PG_VERSION=12
59+
60+
docker-compose build
61+
docker-compose run tests
62+
```
63+
64+
There are different test levels: `hardcore`, `nightmare` (runs tests under `valgrind`) and `stress` (runs tests under `TPC-DS` load).
5065

5166
## Function pg\_query\_state
5267
```plpgsql
@@ -93,11 +108,11 @@ This parameters is set on called side before running any queries whose states ar
93108

94109
## Examples
95110
Set maximum number of parallel workers on `gather` node equals `2`:
96-
```
111+
```sql
97112
postgres=# set max_parallel_workers_per_gather = 2;
98113
```
99114
Assume one backend with pid = 49265 performs a simple query:
100-
```
115+
```sql
101116
postgres=# select pg_backend_pid();
102117
pg_backend_pid
103118
----------------
@@ -106,7 +121,7 @@ postgres=# select pg_backend_pid();
106121
postgres=# select count(*) from foo join bar on foo.c1=bar.c1;
107122
```
108123
Other backend can extract intermediate state of execution that query:
109-
```
124+
```sql
110125
postgres=# \x
111126
postgres=# select * from pg_query_state(49265);
112127
-[ RECORD 1 ]+-------------------------------------------------------------------------------------------------------------------------
@@ -151,11 +166,11 @@ In example above working backend spawns two parallel workers with pids `49324` a
151166
`Seq Scan` node has statistics on passed loops (average number of rows delivered to `Nested Loop` and number of passed loops are shown) and statistics on current loop. Other nodes has statistics only for current loop as this loop is first (`loop number` = 1).
152167

153168
Assume first backend executes some function:
154-
```
169+
```sql
155170
postgres=# select n_join_foo_bar();
156171
```
157172
Other backend can get the follow output:
158-
```
173+
```sql
159174
postgres=# select * from pg_query_state(49265);
160175
-[ RECORD 1 ]+------------------------------------------------------------------------------------------------------------------
161176
pid | 49265
@@ -181,7 +196,7 @@ leader_pid | (null)
181196
First row corresponds to function call, second - to query which is in the body of that function.
182197

183198
We can get result plans in different format (e.g. `json`):
184-
```
199+
```sql
185200
postgres=# select * from pg_query_state(pid := 49265, format := 'json');
186201
-[ RECORD 1 ]+------------------------------------------------------------
187202
pid | 49265

mk_dockerfile.sh

+8
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#!/usr/bin/env sh
2+
13
if [ -z ${PG_VERSION+x} ]; then
24
echo PG_VERSION is not set!
35
exit 1
@@ -7,10 +9,16 @@ if [ -z ${LEVEL+x} ]; then
79
LEVEL=scan-build
810
fi
911

12+
if [ -z ${USE_TPCDS+x} ]; then
13+
USE_TPCDS=0
14+
fi
15+
1016
echo PG_VERSION=${PG_VERSION}
1117
echo LEVEL=${LEVEL}
18+
echo USE_TPCDS=${USE_TPCDS}
1219

1320
sed \
1421
-e 's/${PG_VERSION}/'${PG_VERSION}/g \
1522
-e 's/${LEVEL}/'${LEVEL}/g \
23+
-e 's/${USE_TPCDS}/'${USE_TPCDS}/g \
1624
Dockerfile.tmpl > Dockerfile

run_tests.sh

+9-5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#!/usr/bin/env bash
22

33
#
4-
# Copyright (c) 2018, Postgres Professional
4+
# Copyright (c) 2019, Postgres Professional
55
#
66
# supported levels:
77
# * standard
@@ -55,7 +55,7 @@ fi
5555

5656
# build and install PostgreSQL
5757
if [ "$LEVEL" = "hardcore" ] || \
58-
[ "$LEVEL" = "nightmare" ]; then
58+
[ "$LEVEL" = "nightmare" ]; then
5959
# enable Valgrind support
6060
sed -i.bak "s/\/* #define USE_VALGRIND *\//#define USE_VALGRIND/g" src/include/pg_config_manual.h
6161

@@ -143,10 +143,14 @@ if [ -f regression.diffs ]; then cat regression.diffs; fi
143143

144144
# run python tests
145145
set +x -e
146-
virtualenv /tmp/env && source /tmp/env/bin/activate &&
147-
pip install PyYAML && pip install psycopg2
146+
python3 -m venv /tmp/env && source /tmp/env/bin/activate &&
147+
pip install -r tests/requirements.txt
148148
set -e #exit virtualenv with error code
149149
python tests/pg_qs_test_runner.py --port $PGPORT
150+
if [[ "$USE_TPCDS" == "1" ]]; then
151+
python tests/pg_qs_test_runner.py --port $PGPORT --tpc-ds-setup
152+
python tests/pg_qs_test_runner.py --port $PGPORT --tpc-ds-run
153+
fi
150154
deactivate
151155
set -x
152156

@@ -170,4 +174,4 @@ gcov *.c *.h
170174
set +ux
171175

172176
# send coverage stats to Codecov
173-
bash <(curl -s https://codecov.io/bash)
177+
bash <(curl -s https://codecov.io/bash)

tests/common.py

+103
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
'''
2+
common.py
3+
Copyright (c) 2016-2020, Postgres Professional
4+
'''
5+
6+
import psycopg2
7+
import psycopg2.extensions
8+
import select
9+
import time
10+
11+
BACKEND_IS_IDLE_INFO = 'INFO: state of backend is idle\n'
12+
BACKEND_IS_ACTIVE_INFO = 'INFO: state of backend is active\n'
13+
14+
def wait(conn):
15+
"""wait for some event on connection to postgres"""
16+
while 1:
17+
state = conn.poll()
18+
if state == psycopg2.extensions.POLL_OK:
19+
break
20+
elif state == psycopg2.extensions.POLL_WRITE:
21+
select.select([], [conn.fileno()], [])
22+
elif state == psycopg2.extensions.POLL_READ:
23+
select.select([conn.fileno()], [], [])
24+
else:
25+
raise psycopg2.OperationalError("poll() returned %s" % state)
26+
27+
def n_async_connect(config, n=1):
28+
"""establish n asynchronious connections to the postgres with specified config"""
29+
30+
aconfig = config.copy()
31+
aconfig['async'] = True
32+
33+
result = []
34+
for _ in range(n):
35+
conn = psycopg2.connect(**aconfig)
36+
wait(conn)
37+
result.append(conn)
38+
return result
39+
40+
def n_close(conns):
41+
"""close connections to postgres"""
42+
43+
for conn in conns:
44+
conn.close()
45+
46+
def pg_query_state(config, pid, verbose=False, costs=False, timing=False, \
47+
buffers=False, triggers=False, format='text'):
48+
"""
49+
Get query state from backend with specified pid and optional parameters.
50+
Save any warning, info, notice and log data in global variable 'notices'
51+
"""
52+
53+
conn = psycopg2.connect(**config)
54+
curs = conn.cursor()
55+
56+
curs.callproc('pg_query_state', (pid, verbose, costs, timing, buffers, triggers, format))
57+
result = curs.fetchall()
58+
notices = conn.notices[:]
59+
conn.close()
60+
61+
return result, notices
62+
63+
def onetime_query_state(config, async_conn, query, args={}, num_workers=0):
64+
"""
65+
Get intermediate state of 'query' on connection 'async_conn' after number of 'steps'
66+
of node executions from start of query
67+
"""
68+
69+
acurs = async_conn.cursor()
70+
71+
set_guc(async_conn, 'enable_mergejoin', 'off')
72+
set_guc(async_conn, 'max_parallel_workers_per_gather', num_workers)
73+
acurs.execute(query)
74+
75+
# extract current state of query progress
76+
MAX_PG_QS_RETRIES = 10
77+
DELAY_BETWEEN_RETRIES = 0.1
78+
pg_qs_args = {
79+
'config': config,
80+
'pid': async_conn.get_backend_pid()
81+
}
82+
for k, v in args.items():
83+
pg_qs_args[k] = v
84+
n_retries = 0
85+
while True:
86+
result, notices = pg_query_state(**pg_qs_args)
87+
n_retries += 1
88+
if len(result) > 0:
89+
break
90+
if n_retries >= MAX_PG_QS_RETRIES:
91+
# pg_query_state callings don't return any result, more likely run
92+
# query has completed
93+
break
94+
time.sleep(DELAY_BETWEEN_RETRIES)
95+
wait(async_conn)
96+
97+
set_guc(async_conn, 'enable_mergejoin', 'on')
98+
return result, notices
99+
100+
def set_guc(async_conn, param, value):
101+
acurs = async_conn.cursor()
102+
acurs.execute('set %s to %s' % (param, value))
103+
wait(async_conn)

0 commit comments

Comments
 (0)