Package: judoscale
JudoscaleThis is the official Python adapter forJudoscale. You can use Judoscale without it, but this gives you request queue time metrics and job queue time (for supported job processors).It is recommended to install the specific web framework and/or background job library support as "extras" to thejudoscalePyPI package. This ensures that checking if the installed web framework and/or background task processing library is supported happens at dependency resolution time.Supported web frameworksDjangoFlaskFastAPISupported job processorsCelery(with Redis 6.0+ as the broker)RQUsing Judoscale with DjangoInstall Judoscale for Django with:$pipinstall'judoscale[django]'Add Judoscale app tosettings.py:INSTALLED_APPS=["judoscale.django",# ... other apps]This sets up the Judoscale middleware to capture request queue times.Optionally, you can customize Judoscale insettings.py:JUDOSCALE={# Log level defaults to ENV["LOG_LEVEL"] or "INFO"."LOG_LEVEL":"DEBUG",}Once deployed, you will see your request queue time metrics available in the Judoscale UI.Using Judoscale with FlaskInstall Judoscale for Flask with:$pipinstall'judoscale[flask]'The Flask support for Judoscale is packaged into a Flask extension. Import the extension class and use like you normally would in a Flask application:# app.pyfromjudoscale.flaskimportJudoscale# If your app is a top-level globalapp=Flask("MyFlaskApp")app.config.from_object('...')# or however you configure your appjudoscale=Judoscale(app)# If your app uses the application factory patternjudoscale=Judoscale()defcreate_app():app=Flask("MyFlaskApp")app.config.from_object('...')# or however you configure your appjudoscale.init_app(app)returnappThis sets up the Judoscale extension to capture request queue times.Optionally, you can override Judoscale's own configuration via your application'sconfiguration dictionary. The Judoscale Flask extension looks for a top-level"JUDOSCALE"key inapp.config, which should be a dictionary, and which the extension uses to configure itself as soon asjudoscale.init_app()is called.JUDOSCALE={# Log level defaults to ENV["LOG_LEVEL"] or "INFO"."LOG_LEVEL":"DEBUG",}Note theofficial recommendations for configuring Flask.Using Judoscale with FastAPIInstall Judoscale for FastAPI with:$pipinstall'judoscale[asgi]'Since FastAPI usesStarlette, an ASGI framework, the integration is packaged intoASGI middleware. Import the middleware class and register it with your FastAPI app:# app.pyfromjudoscale.asgi.middlewareimportFastAPIRequestQueueTimeMiddleware# If your app is a top-level globalapp=FastAPI()app.add_middleware(FastAPIRequestQueueTimeMiddleware)# If your app uses the application factory patterndefcreate_app():app=FastAPI()app.add_middleware(FastAPIRequestQueueTimeMiddleware)returnappThis sets up the Judoscale extension to capture request queue times.Optionally, you can override Judoscale's configuration by passing in extra configuration to theadd_middlewaremethod:app.add_middleware(FastAPIRequestQueueTimeMiddleware,extra_config={"LOG_LEVEL":"DEBUG"})Other ASGI frameworksJudoscale also provides middleware classes for Starlette and Quart. You can use them with# For Starlette, if you're using Starlette directly, without FastAPIfromjudoscale.asgi.middlewareimportStarletteRequestQueueTimeMiddleware# For Quartfromjudoscale.asgi.middlewareimportQuartRequestQueueTimeMiddlewareIf your app uses a framework for which we have not provided a middleware class, but it implements theASGI spec, you can easily create your own version of the request queue time middleware.fromjudoscale.asgi.middlewareimportRequestQueueTimeMiddlewareclassYourFrameworkRequestQueueTimeMiddleware(RequestQueueTimeMiddleware):# NOTE: The `platform` class variable value should be the package name# of the web framework you're using. It is used to look up package# metadata for reporting back to the Judoscale API.platform="your_framework"Then registerYourFrameworkRequestQueueTimeMiddlewarewith your application like you normally would.Using Judoscale with Celery and RedisInstall Judoscale for Celery with:$pipinstall'judoscale[celery-redis]':warning:NOTE 1:The Judoscale Celery integration currently only works with theRedis broker. The minimum supported Redis server version is 6.0.:warning:NOTE 2:Usingtask prioritiesis currently not supported byjudoscale. You can still use task priorities, butjudoscalewon't see and report metrics on any queues other than the default, unprioritised queue.Judoscale can automatically scale the number of Celery workers based on the queue latency (the age of the oldest pending task in the queue).Setting up the integrationTo use the Celery integration, importjudoscale_celeryand call it with the Celery app instance.judoscale_celeryshould be called after you have set up and configured the Celery instance.fromceleryimportCeleryfromjudoscale.celeryimportjudoscale_celerycelery_app=Celery(broker="redis://localhost:6379/0")# Further setup# celery_app.conf.update(...)# ...judoscale_celery(celery_app)This sets up Judoscale to periodically calculate and report queue latency for each Celery queue.If you need to change the Judoscale integration configuration, you can pass a dictionary of Judoscale configuration options tojudoscale_celeryto override the default Judoscale config variables:judoscale_celery(celery_app,extra_config={"LOG_LEVEL":"DEBUG"})An example configuration dictionary accepted byextra_config:{"LOG_LEVEL":"INFO",# In addition to global configuration options for the Judoscale# integration above, you can also specify the following configuration# options for the Celery integration."CELERY":{# Enable (default) or disable the Celery integration"ENABLED":True,# Report metrics on up to MAX_QUEUES queues.# The list of discovered queues are sorted by the length# of the queue name (shortest first) and metrics are# reported for the first MAX_QUEUES queues.# Defaults to 20."MAX_QUEUES":20,# Specify a list of known queues to report metrics for.# MAX_QUEUES is still honoured.# Defaults to empty list (report metrics for discovered queues)."QUEUES":[],# Enable or disable (default) tracking how many jobs are currently being# processed in each queue.# This allows Judoscale to avoid downscaling workers that are executing jobs.# See documentation: https://judoscale.com/docs/long-running-jobs-ruby#enable-long-running-job-support-in-the-dashboard# NOTE: This option requires workers to have unique names. If you are running# multiple Celery workers on the same machine, make sure to give each# worker a distinct name.# More information: https://docs.celeryq.dev/en/stable/userguide/workers.html#starting-the-worker"TRACK_BUSY_JOBS":False,}}:warning:NOTE:Callingjudoscale_celeryturns on sendingtask-sentevents. This is required for the Celery integration with Judoscale to work.Judoscale with Celery and FlaskDepending on how you've structured your Flask app, you should calljudoscale_celeryafter your application has finished configuring the Celery app instance. If you have followed theFlask guidein the Flask documentation, the easiest place to initialise the Judoscale integration is in the application factory:defcreate_app():app=Flask(__name__)app.config.from_object(...)# or however you configure your appcelery_app=celery_init_app(app)# Initialise the Judoscale integrationjudoscale_celery(celery_app,extra_config=app.config["JUDOSCALE"])returnappJudoscale with Celery and DjangoIf you have followed theDjango guidein the Celery documentation, you should have a module where you initialise the Celery app instance, auto-discover tasks, etc. You should calljudoscale_celeryafter you have configured the Celery app instance:fromceleryimportCeleryfromdjango.confimportsettingsfromjudoscale.celeryimportjudoscale_celeryapp=Celery()app.config_from_object("django.conf:settings",namespace="CELERY")app.autodiscover_tasks()# Initialise the Judoscale integrationjudoscale_celery(app,extra_config=settings.JUDOSCALE)Using Judoscale with RQInstall Judoscale for RQ with:$pipinstall'judoscale[rq]'Judoscale can automatically scale the number of RQ workers based on the queue latency (the age of the oldest pending task in the queue).Setting up the integrationTo use the RQ integration, importjudoscale_rqand call it with an instance ofRedispointing to the same Redis database that RQ uses.fromredisimportRedisfromjudoscale.rqimportjudoscale_rqredis=Redis(...)judoscale_rq(redis)This sets up Judoscale to periodically calculate and report queue latency for each RQ queue.If you need to change the Judoscale integration configuration, you can pass a dictionary of Judoscale configuration options tojudoscale_rqto override the default Judoscale config variables:judoscale_rq(redis,extra_config={"LOG_LEVEL":"DEBUG"})An example configuration dictionary accepted byextra_config:{"LOG_LEVEL":"INFO",# In addition to global configuration options for the Judoscale# integration above, you can also specify the following configuration# options for the RQ integration."RQ":{# Enable (default) or disable the RQ integration"ENABLED":True,# Report metrics on up to MAX_QUEUES queues.# The list of discovered queues are sorted by the length# of the queue name (shortest first) and metrics are# reported for the first MAX_QUEUES queues.# Defaults to 20."MAX_QUEUES":20,# Specify a list of known queues to report metrics for.# MAX_QUEUES is still honoured.# Defaults to empty list (report metrics for discovered queues)."QUEUES":[],# Enable or disable (default) tracking how many jobs are currently being# processed in each queue.# This allows Judoscale to avoid downscaling workers that are executing jobs.# See documentation: https://judoscale.com/docs/long-running-jobs-ruby#enable-long-running-job-support-in-the-dashboard"TRACK_BUSY_JOBS":False,}Judoscale with RQ and FlaskThe recommended way to initialise Judoscale for RQ is in the application factory:judoscale=Judoscale()defcreate_app():app=Flask(__name__)app.config.from_object("...")# or however you configure your applicationapp.redis=Redis()# Initialise the Judoscale integration for Flaskjudoscale.init_app(app)# Initialise the Judoscale integration for RQjudoscale_rq(app.redis)returnappThen, in your worker script, make sure that you create an app, which will initialise the Judoscale integration with RQ. Although not required, it's useful to run the worker within the Flask app context. If you have followed theRQ on Heroku patternfor setting up your RQ workers on Heroku, your worker script should look something like this:fromrq.workerimportHerokuWorkerasWorkerapp=create_app()worker=Worker(...,connection=app.redis)withapp.app_context():worker.work()See therun-worker.py scriptfor reference.Judoscale with RQ and DjangoThe Judoscale integration for RQ is packaged into a separate Django app.You should already havejudoscale.djangoin yourINSTALLED_APPS. Next, add the RQ integration appjudoscale.rq:INSTALLED_APPS=["judoscale.django","judoscale.rq",# ... other apps]By default,judoscale.rqwill connect to the Redis instance as specified by theREDIS_URLenvironment variable. If that is not suitable, you can supply Redis connection information in theJUDOSCALEconfiguration dictionary under the"REDIS"key.Accepted formats are:a dictionary containing a single key"URL"pointing to a Redis server URL, or;a dictionary of configuration options corresponding to the keyword arguments of theRedisconstructor.JUDOSCALE={# Configuring with a Redis server URL"REDIS":{"URL":os.getenv("REDISTOGO_URL"),"SSL_CERT_REQS":None# If you are running on Heroku and using Heroku Data for Redis Premium}# Configuring as kwargs to Redis(...)"REDIS":{"HOST":"localhost","PORT":6379,"DB":0"SSL_CERT_REQS":None# If you are running on Heroku and using Heroku Data for Redis Premium}}:warning:NOTE:If you are running on Heroku and using any of the Premium plans for Heroku Data for Redis, you will have to turn off SSL certificate verification as perhttps://help.heroku.com/HC0F8CUS/redis-connection-issues.If you are usingDjango-RQ, you can also pull configuration fromRQ_QUEUESdirectly:RQ_QUEUES={"high_priority":{"HOST":"...","PORT":6379,"DB":0},}JUDOSCALE={# ... other configuration options"REDIS":RQ_QUEUES["high_priority"]}:warning:NOTE:Django-RQ enables configuring RQ such that different queues and workers usedifferentRedis instances. Judoscale currently only supports connecting to and monitoring queue latency on a single Redis instance.Debugging & troubleshootingIf Judoscale is not recognizing your adapter installation or if you're not seeing expected metrics in Judoscale, you'll want to check the logging output. Here's how you'd do that on Heroku.First, enable debug logging:herokuconfig:setJUDOSCALE_LOG_LEVEL=debugThen, tail your logs while your app initializes:herokulogs--tail|grepJudoscaleYou should see Judoscale collecting and reporting metrics every 10 seconds from every running process. If the issue is not clear from the logs, emailhelp@judoscale.comfor support. Please include any logging you've collected and any other behavior you've observed.DevelopmentThis repo includes asample-appsdirectory containing apps you can run locally. These apps use thejudoscaleadapter, but they overrideAPI_BASE_URLso they're not connected to the real Judoscale API. Instead, they post API requests tohttps://requestinspector.comso you can observe the API behavior.See theREADMEin a sample app for details on how to set it up and run locally.ContributingjudoscaleusesPoetryfor managing dependencies and packaging the project. Head over to theinstallations instructionsand install Poetry, if needed.Clone the repo with$gitclonegit@github.com:judoscale/judoscale-python.git
$cdjudoscale-pythonVerify that you are on a recent version of Poetry:$poetry--version
Poetry(version1.3.1)Install dependencies with Poetry and activate the virtualenv$poetryinstall--all-extras
$poetryshellRun tests with$pytest