Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add TPC-DS stress tests #17

Merged
merged 24 commits into from
Jan 10, 2020
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
957d83f
Transfer tests to python3
ololobus Oct 25, 2019
bbc89f5
Try TPC-DS test
ololobus Oct 25, 2019
459a060
Use committed TPC-DS queries for now
ololobus Oct 25, 2019
37bedbe
Prepare TPC-DS data
ololobus Oct 25, 2019
166ead3
New tests refactoring
ololobus Oct 26, 2019
91d7754
Add comment
ololobus Oct 26, 2019
5bc7769
Set stress_in_progress as global
ololobus Oct 26, 2019
8c1a8d7
Some formatting and new queries
ololobus Oct 26, 2019
7118ea2
Remove committed TCP-DS queries
ololobus Oct 26, 2019
7bb3c33
README.md updated to reflect recent changes
ololobus Oct 28, 2019
087b0c8
Support only Python 3+ for running tests
ololobus Oct 28, 2019
4d4734a
Use copy_from from psycopg2 for TPC-DS data load
ololobus Oct 28, 2019
80a5e1a
Segregate stress test based on TPC-DS from common ones
maksm90 Oct 29, 2019
302350c
Fix tests runner script
maksm90 Oct 29, 2019
18f5a38
Try to fix usage of USE_TPCDS variable in test script
ololobus Oct 30, 2019
02049e1
Pass USE_TPCDS env variable to Docker container
ololobus Oct 30, 2019
a4a2ec3
Create pg_query_state extension in the case of TPC-DS test
ololobus Oct 30, 2019
ba34af1
Do not run TPC-DS on 9.6
ololobus Oct 30, 2019
b2f8e82
Split common tests and tpc-ds stress test on different modules
Dec 21, 2019
772062a
Segregate setup and running of stress test
maksm90 Dec 21, 2019
2ac1f8a
Make periodic pg_query_state calls to backend running TPC-DS bench
maksm90 Dec 21, 2019
61cf837
Refactor the main cycle of tpc-ds stress test
maksm90 Dec 28, 2019
c1776bc
Make light refactoring after review from ololobus
maksm90 Jan 9, 2020
8545705
Light refactoring regarding constants in run_tpcds function
maksm90 Jan 10, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@ pg_query_state--*.sql
cscope.out
tags
Dockerfile
tmp_stress

6 changes: 3 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ notifications:
on_failure: always

env:
- PG_VERSION=12 LEVEL=hardcore
- PG_VERSION=12 LEVEL=hardcore USE_TPCDS=1
- PG_VERSION=12
- PG_VERSION=11 LEVEL=hardcore
- PG_VERSION=11 LEVEL=hardcore USE_TPCDS=1
- PG_VERSION=11
- PG_VERSION=10 LEVEL=hardcore
- PG_VERSION=10 LEVEL=hardcore USE_TPCDS=1
- PG_VERSION=10
- PG_VERSION=9.6 LEVEL=hardcore
- PG_VERSION=9.6
Expand Down
6 changes: 3 additions & 3 deletions Dockerfile.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ FROM postgres:${PG_VERSION}-alpine

# Install dependencies
RUN apk add --no-cache \
openssl curl \
openssl curl git \
perl perl-ipc-run \
make musl-dev gcc bison flex coreutils \
zlib-dev libedit-dev \
clang clang-analyzer linux-headers \
python2 python2-dev py2-virtualenv;
python3 python3-dev py3-virtualenv;


# Install fresh valgrind
Expand Down Expand Up @@ -35,4 +35,4 @@ ADD . /pg/testdir
WORKDIR /pg/testdir

USER postgres
ENTRYPOINT LEVEL=${LEVEL} /run.sh
ENTRYPOINT LEVEL=${LEVEL} USE_TPCDS=${USE_TPCDS} /run.sh
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ DATA_built = $(EXTENSION)--$(EXTVERSION).sql
PGFILEDESC = "pg_query_state - facility to track progress of plan execution"

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

ifdef USE_PGXS
PG_CONFIG ?= pg_config
Expand Down
39 changes: 27 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,26 +26,41 @@ Add module name to the `shared_preload_libraries` parameter in `postgresql.conf`
shared_preload_libraries = 'pg_query_state'
```
It is essential to restart the PostgreSQL instance. After that, execute the following query in psql:
```
```sql
CREATE EXTENSION pg_query_state;
```
Done!

## Tests
Tests using parallel sessions using python 2.7 script:
```
python tests/pg_qs_test_runner.py [OPTION]...
```
Test using parallel sessions with Python 3+ compatible script:
```shell
python tests/pg_qs_test_runner.py [OPTION]...
```
*prerequisite packages*:
* `psycopg2` version 2.6 or later
* `PyYAML` version 3.11 or later

* `progressbar2` for stress test progress reporting

*options*:
* *- -host* --- postgres server host, default value is *localhost*
* *- -port* --- postgres server port, default value is *5432*
* *- -database* --- database name, default value is *postgres*
* *- -user* --- user name, default value is *postgres*
* *- -password* --- user's password, default value is empty
* *- -tpc-ds* --- runs only stress tests on TPC-DS benchmark

Or run all tests in `Docker` using:

```shell
export LEVEL=hardcore
export USE_TPCDS=1
export PG_VERSION=12

docker-compose build
docker-compose run tests
```

There are different test levels: `hardcore`, `nightmare` (runs tests under `valgrind`) and `stress` (runs tests under `TPC-DS` load).

## Function pg\_query\_state
```plpgsql
Expand Down Expand Up @@ -92,11 +107,11 @@ This parameters is set on called side before running any queries whose states ar

## Examples
Set maximum number of parallel workers on `gather` node equals `2`:
```
```sql
postgres=# set max_parallel_workers_per_gather = 2;
```
Assume one backend with pid = 49265 performs a simple query:
```
```sql
postgres=# select pg_backend_pid();
pg_backend_pid
----------------
Expand All @@ -105,7 +120,7 @@ postgres=# select pg_backend_pid();
postgres=# select count(*) from foo join bar on foo.c1=bar.c1;
```
Other backend can extract intermediate state of execution that query:
```
```sql
postgres=# \x
postgres=# select * from pg_query_state(49265);
-[ RECORD 1 ]+-------------------------------------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -150,11 +165,11 @@ In example above working backend spawns two parallel workers with pids `49324` a
`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).

Assume first backend executes some function:
```
```sql
postgres=# select n_join_foo_bar();
```
Other backend can get the follow output:
```
```sql
postgres=# select * from pg_query_state(49265);
-[ RECORD 1 ]+------------------------------------------------------------------------------------------------------------------
pid | 49265
Expand All @@ -180,7 +195,7 @@ leader_pid | (null)
First row corresponds to function call, second - to query which is in the body of that function.

We can get result plans in different format (e.g. `json`):
```
```sql
postgres=# select * from pg_query_state(pid := 49265, format := 'json');
-[ RECORD 1 ]+------------------------------------------------------------
pid | 49265
Expand Down
8 changes: 8 additions & 0 deletions mk_dockerfile.sh
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#!/usr/bin/env sh

if [ -z ${PG_VERSION+x} ]; then
echo PG_VERSION is not set!
exit 1
Expand All @@ -7,10 +9,16 @@ if [ -z ${LEVEL+x} ]; then
LEVEL=scan-build
fi

if [ -z ${USE_TPCDS+x} ]; then
USE_TPCDS=0
fi

echo PG_VERSION=${PG_VERSION}
echo LEVEL=${LEVEL}
echo USE_TPCDS=${USE_TPCDS}

sed \
-e 's/${PG_VERSION}/'${PG_VERSION}/g \
-e 's/${LEVEL}/'${LEVEL}/g \
-e 's/${USE_TPCDS}/'${USE_TPCDS}/g \
Dockerfile.tmpl > Dockerfile
14 changes: 9 additions & 5 deletions run_tests.sh
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/env bash

#
# Copyright (c) 2018, Postgres Professional
# Copyright (c) 2019, Postgres Professional
#
# supported levels:
# * standard
Expand Down Expand Up @@ -55,7 +55,7 @@ fi

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

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

# run python tests
set +x -e
virtualenv /tmp/env && source /tmp/env/bin/activate &&
pip install PyYAML && pip install psycopg2
python3 -m venv /tmp/env && source /tmp/env/bin/activate &&
pip install -r tests/requirements.txt
set -e #exit virtualenv with error code
python tests/pg_qs_test_runner.py --port $PGPORT
if [[ "$USE_TPCDS" == "1" ]]; then
python tests/pg_qs_test_runner.py --port $PGPORT --tpc-ds-setup
python tests/pg_qs_test_runner.py --port $PGPORT --tpc-ds-run
fi
deactivate
set -x

Expand All @@ -170,4 +174,4 @@ gcov *.c *.h
set +ux

# send coverage stats to Codecov
bash <(curl -s https://codecov.io/bash)
bash <(curl -s https://codecov.io/bash)
103 changes: 103 additions & 0 deletions tests/common.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
'''
common.py
Copyright (c) 2016-2019, Postgres Professional
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess you can put 2020 everywhere already :)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

'''

import psycopg2
import psycopg2.extensions
import select
import time

BACKEND_IS_IDLE_INFO = 'INFO: state of backend is idle\n'
BACKEND_IS_ACTIVE_INFO = 'INFO: state of backend is active\n'

def wait(conn):
"""wait for some event on connection to postgres"""
while 1:
state = conn.poll()
if state == psycopg2.extensions.POLL_OK:
break
elif state == psycopg2.extensions.POLL_WRITE:
select.select([], [conn.fileno()], [])
elif state == psycopg2.extensions.POLL_READ:
select.select([conn.fileno()], [], [])
else:
raise psycopg2.OperationalError("poll() returned %s" % state)

def n_async_connect(config, n=1):
"""establish n asynchronious connections to the postgres with specified config"""

aconfig = config.copy()
aconfig['async'] = True

result = []
for _ in range(n):
conn = psycopg2.connect(**aconfig)
wait(conn)
result.append(conn)
return result

def n_close(conns):
"""close connections to postgres"""

for conn in conns:
conn.close()

def pg_query_state(config, pid, verbose=False, costs=False, timing=False, \
buffers=False, triggers=False, format='text'):
"""
Get query state from backend with specified pid and optional parameters.
Save any warning, info, notice and log data in global variable 'notices'
"""

conn = psycopg2.connect(**config)
curs = conn.cursor()

curs.callproc('pg_query_state', (pid, verbose, costs, timing, buffers, triggers, format))
result = curs.fetchall()
notices = conn.notices[:]
conn.close()

return result, notices

def onetime_query_state(config, async_conn, query, args={}, num_workers=0):
"""
Get intermediate state of 'query' on connection 'async_conn' after number of 'steps'
of node executions from start of query
"""

acurs = async_conn.cursor()

set_guc(async_conn, 'enable_mergejoin', 'off')
set_guc(async_conn, 'max_parallel_workers_per_gather', num_workers)
acurs.execute(query)

# extract current state of query progress
MAX_PG_QS_RETRIES = 10
DELAY_BETWEEN_RETRIES = 0.1
pg_qs_args = {
'config': config,
'pid': async_conn.get_backend_pid()
}
for k, v in args.items():
pg_qs_args[k] = v
n_retries = 0
while True:
result, notices = pg_query_state(**pg_qs_args)
n_retries += 1
if len(result) > 0:
break
if n_retries >= MAX_PG_QS_RETRIES:
# pg_query_state callings don't return any result, more likely run
# query has completed
break
time.sleep(DELAY_BETWEEN_RETRIES)
wait(async_conn)

set_guc(async_conn, 'enable_mergejoin', 'on')
return result, notices

def set_guc(async_conn, param, value):
acurs = async_conn.cursor()
acurs.execute('set %s to %s' % (param, value))
wait(async_conn)
Loading