Adds the scripts used to size the connection pool and validate fleet polling under load: burst arrival and mixed workload drivers, pool and resource samplers, a UI probe, host seed/drop SQL, and a gevent Dockerfile. Hosts are addressed by compose service name (api:5001) rather than a local path, so the harness runs against any stack rather than one machine. loadtest_token.py mints a scoped collector token named LOADTEST and prints the secret once, then revokes only that token by name so a real collector token is never touched. envkeyfallback is false, so the harness needs a genuine managed token rather than an env key.
43 lines
1.3 KiB
Python
43 lines
1.3 KiB
Python
"""Report SQLAlchemy pool saturation from inside the API container.
|
|
|
|
WHY: MySQL's max_connections is NOT the ceiling that binds first. The effective
|
|
limit is per-process: pool_size + max_overflow, multiplied by worker count.
|
|
With the defaults (5 + 10) and 4 workers that is 60, not 151. A peak of 52
|
|
therefore means 87 percent of the real ceiling, not 34 percent.
|
|
|
|
Prints the computed ceiling so a measured high-water mark can be compared
|
|
against the right number.
|
|
|
|
Usage (inside the api container, PYTHONPATH=/app):
|
|
python pool_status.py
|
|
"""
|
|
import json
|
|
import os
|
|
|
|
from shopdb import create_app
|
|
from shopdb.api import db
|
|
|
|
|
|
def main():
|
|
app = create_app(os.environ.get('FLASK_ENV', 'production'))
|
|
with app.app_context():
|
|
engine = db.engine
|
|
pool = engine.pool
|
|
size = pool.size()
|
|
overflow = getattr(pool, '_max_overflow', 0)
|
|
workers = int(os.environ.get('GUNICORN_WORKERS', '4'))
|
|
print(json.dumps({
|
|
'pool_class': type(pool).__name__,
|
|
'pool_size': size,
|
|
'max_overflow': overflow,
|
|
'per_worker_ceiling': size + overflow,
|
|
'workers': workers,
|
|
'effective_ceiling': (size + overflow) * workers,
|
|
'checkedout': pool.checkedout(),
|
|
'status': pool.status(),
|
|
}, indent=2))
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|