Taddes af1c5fb68a
Some checks failed
Glean probe-scraper / glean-probe-scraper (push) Has been cancelled
Main Workflow - Lint, Build, Test / python-env (push) Has been cancelled
Main Workflow - Lint, Build, Test / rust-env (push) Has been cancelled
Main Workflow - Lint, Build, Test / python-checks (push) Has been cancelled
Main Workflow - Lint, Build, Test / rust-checks (push) Has been cancelled
Main Workflow - Lint, Build, Test / clippy (mysql) (push) Has been cancelled
Main Workflow - Lint, Build, Test / clippy (postgres) (push) Has been cancelled
Main Workflow - Lint, Build, Test / clippy (spanner) (push) Has been cancelled
Main Workflow - Lint, Build, Test / build-and-unit-test-postgres (push) Has been cancelled
Main Workflow - Lint, Build, Test / build-postgres-image (push) Has been cancelled
Main Workflow - Lint, Build, Test / postgres-e2e-tests (push) Has been cancelled
Main Workflow - Lint, Build, Test / build-and-unit-test-mysql (push) Has been cancelled
Main Workflow - Lint, Build, Test / build-mysql-image (push) Has been cancelled
Main Workflow - Lint, Build, Test / mysql-e2e-tests (push) Has been cancelled
Main Workflow - Lint, Build, Test / build-and-unit-test-spanner (push) Has been cancelled
Main Workflow - Lint, Build, Test / build-spanner-image (push) Has been cancelled
Main Workflow - Lint, Build, Test / spanner-e2e-tests (push) Has been cancelled
Build, Tag and Push Container Images to GAR / check (push) Has been cancelled
Build, Tag and Push Container Images to GAR / build-and-push-syncstorage-rs (push) Has been cancelled
Build, Tag and Push Container Images to GAR / build-and-push-syncserver-postgres (push) Has been cancelled
Build, Tag and Push Container Images to GAR / build-and-push-syncstorage-rs-spanner-python-utils (push) Has been cancelled
Build, Tag and Push Container Images to GAR / build-and-push-syncserver-postgres-python-utils (push) Has been cancelled
Build, Tag and Push Container Images to GAR / build-and-push-syncserver-mysql (push) Has been cancelled
Publish Sync docs to pages / build-mdbook (push) Has been cancelled
Publish Sync docs to pages / build-openapi (push) Has been cancelled
Publish Sync docs to pages / combine-and-prepare (push) Has been cancelled
Publish Sync docs to pages / deploy (push) Has been cancelled
feat: add python utils and integrate into workflow (#2176)
feat: add python utils and integrate into workflow
2026-04-01 16:02:34 -04:00

130 lines
3.6 KiB
Python

#! /usr/bin/env python
"""Script to populate the tokenserver database with test user records."""
import random
import time
from sqlalchemy import create_engine
from sqlalchemy.sql import text as sqltext
_CREATE_USER_RECORD = sqltext(
"""\
insert into
users
(service, email, nodeid, generation, client_state,
created_at, replaced_at)
values
(:service, :email, :nodeid, 0, "", :timestamp, NULL)
"""
)
_GET_SERVICE_ID = sqltext(
"""\
select
id
from
services
where
service = :service
"""
)
_GET_NODE_ID = sqltext(
"""\
select
id
from
nodes
where
service=:service and node=:node
"""
)
_SERVICE_NAME = "sync-1.5"
# This class creates a bunch of users associated with the sync-1.5 service.
#
# The resulting users will have an address in the form of <uid>@<host> where
# uid is an int from 0 to :param user_range:.
#
# This class is useful to populate the database during the load tests. It
# allows us to test a specific behaviour: making sure that we are not reading
# the values from memory when retrieving the node information.
#
# :param sqluri: the sqluri string used to connect to the database
# :param nodes: the list of available nodes for this service
# :param user_range: the number of users to create
# :param host: the hostname to use when generating users
class PopulateDatabase:
"""Create test users associated with the sync-1.5 service for load testing."""
def __init__(self, sqluri, nodes, user_range, host="loadtest.local"):
engine = create_engine(sqluri)
self.database = engine.execution_options(isolation_level="AUTOCOMMIT").connect()
self.service_id = self._get_service_id()
self.node_ids = [self._get_node_id(node) for node in nodes]
self.user_range = user_range
self.host = host
def _get_node_id(self, node_name):
"""Get numeric id for a node."""
res = self.database.execute(
_GET_NODE_ID, service=self.service_id, node=node_name
)
row = res.fetchone()
res.close()
if row is None:
raise ValueError("unknown node: " + node_name)
return row[0]
def _get_service_id(self):
res = self.database.execute(_GET_SERVICE_ID, service=_SERVICE_NAME)
row = res.fetchone()
res.close()
return row.id
def run(self):
"""Populate the database by assigning each user in the range to a random node."""
params = {
"service": self.service_id,
"timestamp": int(time.time() * 1000),
}
# for each user in the range, assign them to a node
for idx in range(0, self.user_range):
email = "%s@%s" % (idx, self.host)
nodeid = random.choice(self.node_ids)
self.database.execute(
_CREATE_USER_RECORD, email=email, nodeid=nodeid, **params
)
def main():
"""Run the populate_db script, reading args from the command line.
Read the arguments from the command line and pass them to the
PopulateDb class.
Example use:
python3 populate-db.py sqlite:////tmp/tokenserver\
node1,node2,node3,node4,node5,node6 100
"""
import sys
if len(sys.argv) < 4:
raise ValueError(
"You need to specify (in this order) sqluri, "
"nodes (comma separated), and user_range"
)
# transform the values from the cli to python objects
sys.argv[2] = sys.argv[2].split(",") # comma separated => list
sys.argv[3] = int(sys.argv[3])
PopulateDatabase(*sys.argv[1:]).run()
print("created {nb_users} users".format(nb_users=sys.argv[3]))
if __name__ == "__main__":
main()