-
Notifications
You must be signed in to change notification settings - Fork 3k
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 PostgresUser to examples #2836
Changes from 13 commits
cf958a6
08ef825
548eb60
d919018
86aa6ef
01674a1
b6737b7
8e9663f
cd04c15
1fb3488
516ba58
5d3b1ef
83f70e4
70ddd14
b43a0c7
4931df1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
# Overview | ||
|
||
Read the instruction below for your specific database | ||
|
||
## PostgreSQL | ||
|
||
### How to run the test | ||
|
||
- Prerequisites: | ||
|
||
- `psycopg3` - https://www.psycopg.org/psycopg3/docs/basic/install.html | ||
|
||
- Set your environment variables for: | ||
|
||
- PGHOST | ||
- PGPORT | ||
- PGDATABASE | ||
- PGUSER | ||
- PGPASSWORD | ||
|
||
- Run locust as usual, see https://docs.locust.io/en/stable/quickstart.html |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
from locust import TaskSet, between, task | ||
from locust.contrib.postgres import PostgresUser | ||
|
||
import os | ||
import random | ||
|
||
|
||
class UserTasks(TaskSet): | ||
@task | ||
def run_select_query(self): | ||
self.client.execute_query( | ||
self.user.conn_string, | ||
"SELECT * FROM loadtesting.invoice WHERE amount > 500", | ||
) | ||
|
||
@task(3) | ||
def run_update_query(self): | ||
random_amount = random.randint(1, 12) | ||
self.client.execute_query( | ||
self.user.conn_string, | ||
f"UPDATE loadtesting.invoice SET amount={random_amount} WHERE amount < 10", | ||
) | ||
|
||
|
||
class PostgresLocust(PostgresUser): | ||
tasks = [UserTasks] | ||
min_wait = 0 | ||
max_wait = 3 | ||
wait_time = between(min_wait, max_wait) | ||
|
||
# Use environment variables or default values | ||
PGHOST = os.getenv("PGHOST", "localhost") | ||
PGPORT = os.getenv("PGPORT", "5432") | ||
PGDATABASE = os.getenv("PGDATABASE", "loadtesting_db") | ||
PGUSER = os.getenv("PGUSER", "postgres") | ||
PGPASSWORD = os.getenv("PGPASSWORD", "postgres") | ||
|
||
conn_string = f"postgresql://{PGUSER}:{PGPASSWORD}@{PGHOST}:{PGPORT}/{PGDATABASE}" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
from locust import TaskSet, User, events | ||
|
||
import time | ||
|
||
import psycopg | ||
|
||
|
||
def create_conn(conn_string): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this method is too small to make sense! The code is easier to understand if you just call .connect directly. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good call, it looks a lot cleaner calling the |
||
return psycopg.connect(conn_string) | ||
|
||
|
||
def execute_query(conn_string, query): | ||
db_conn = create_conn(conn_string) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this create a new connection on each query? That sounds very wasteful. Probably you wanna do that only when the client object is created. |
||
return db_conn.cursor().execute(query) | ||
|
||
|
||
class PostgresClient: | ||
def __getattr__(self, name): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I dont think you need the whole Just define the execute_query method directly on the PostgresClient class. |
||
def request_handler(*args, **kwargs): | ||
start_time = time.time() | ||
try: | ||
execute_query(*args, **kwargs) | ||
response_time = int((time.time() - start_time) * 1000) | ||
events.request.fire( | ||
request_type="postgres_success", | ||
name=name, | ||
response_time=response_time, | ||
response_length=0, | ||
) | ||
except Exception as e: | ||
response_time = int((time.time() - start_time) * 1000) | ||
events.request.fire( | ||
request_type="postgres_failure", | ||
name=name, | ||
response_time=response_time, | ||
response_length=0, | ||
exception=e, | ||
) | ||
print(f"error: {e}") | ||
|
||
return request_handler | ||
|
||
|
||
class PostgresUser(User): | ||
abstract = True | ||
|
||
def __init__(self, *args, **kwargs): | ||
super().__init__(*args, **kwargs) | ||
self.client = PostgresClient() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove the wait_time stuff and just leave it as the default, to simplify the example.
You can skip the taskset and put the tasks directly under the user too.