Accelerator
How the Accelerator discovers hot queries, creates Readyset caches for them, and keeps routing rules reconciled with cache and health state.
What the Accelerator does
The Accelerator (config block [plugins.accelerator]) is QueryPilot's caching component. It watches query traffic through the Router, ranks the hottest query shapes, creates caches for them on a Readyset deployment, and installs the routing rules that steer matching queries to those caches. It then keeps caches and rules reconciled as traffic and health change.
MySQL support is in preview. The Accelerator can manage MySQL-protocol Readyset endpoints and ProxySQL deployments, but PostgreSQL is the fully supported GA v1 path.
It ships in two forms:
- In-process, running inside the Router (binary
querypilot), enabled by a[plugins.accelerator]block inquerypilot.toml, with a REST surface at/api/v1/plugins/acceleratoron the Router's admin API. This is the primary deployment form. - Standalone, as the
querypilot-acceleratorbinary with its own TOML-syntax.cnfconfig file, managing either a Router deployment or a ProxySQL deployment from the outside.
The cycle
Each cycle walks the same pipeline regardless of deployment form:
Health check. Ask each Readyset instance SHOW READYSET STATUS: snapshot completed means online; snapshot in progress or unreachable means traffic should fall back to the source database. When the Readyset pool is unhealthy, the Accelerator disables the routing rules it owns so queries flow to the primary, and re-enables them on recovery.
Drop denied. Remove caches and rules for queries that appear on the denylist.
Reconcile rules from caches. Enumerate existing caches (SHOW CACHES) and install routing rules for any cache that lacks one, so caches created by hand also get traffic.
Drop rogue rules. Remove owned rules whose backing cache no longer exists. If the cache catalog cannot be fetched (Readyset down), no rules are dropped; an outage never causes rule churn.
Discovery. Rank query shapes from execution statistics, check candidate support with EXPLAIN CREATE CACHE FROM <sql>, create caches with CREATE CACHE <name> FROM <sql>, and install a routing rule per new cache.
New rules can be installed disabled and enabled only after a warmup window (warmup_time_s), giving Readyset time to warm the cache by mirroring before traffic promotes to it.
Rule ownership
The Accelerator only ever manages rules it owns. Ownership is recorded in the rule's comment metadata (a readyset-meta: JSON blob carrying the rule type, creation time, and cache name), and for the in-process form ownership is additionally limited to the pools named in [plugins.accelerator.grants]. Rules created by operators are never touched.
Running in-process
You need a Router pool whose backends are Readyset instances, plus the plugin block:
[pools.readyset]
backends = [{ host = "readyset.internal", port = 5433 }]
[plugins.accelerator]
enabled = true
interval_s = 10 # discovery cycle cadence, seconds
database = "postgresql"
readyset_servers = [{ host = "readyset.internal", port = 5433 }]
readyset_user = "app"
readyset_password = "<your-password>"
min_execution = 1 # executions before a query shape is a candidate
warmup_time_s = 60 # new rules stay disabled for this window
api_key = "<your-api-key>"
[plugins.accelerator.grants]
pools = ["readyset"]
max_owned_rules = 100The plugin runs the cycle every interval_s seconds on its own thread inside the Router process. It reads the Router's built-in per-query-shape statistics directly (the same data served at GET /api/stats/digests), so there is no external stats collection to set up. Discovery in this form ranks candidates by execution count; the standalone form exposes the ranking mode and top-N as config keys. min_execution sets the execution-count floor and wait_s requires a shape to have been observable for that many seconds before it is a candidate.
Operationally, the plugin is fail-open by design: configuration errors fail Router startup, but no runtime error, whether a Readyset outage, a failed cycle, or a panic inside the cycle, ever affects query traffic. Failed cycles retry on the next tick; repeated panics stop the loop and surface through the liveness metric rather than crashing the Router.
Two knobs shape rollout:
dry_run = truelogs every caching decision without creating caches or rules. Run this first to see what the plugin would do to your workload.warmup_time_sdelays traffic promotion for new rules, as above.
Verify it is working through the admin port:
curl http://localhost:9091/api/v1/plugins/accelerator/status # loop health
curl http://localhost:9091/api/v1/plugins/accelerator/candidates # what discovery sees
curl http://localhost:9091/api/v1/plugins/accelerator/rules # rules it installed
curl "http://localhost:9091/api/routing/test?sql=<your query>" # target_pool: "readyset"A step-by-step walkthrough is in the Accelerator caching tutorial.
REST API
Mounted under /api/v1/plugins/accelerator on the Router's admin API. Mutating endpoints require the configured api_key, sent as the X-QueryPilot-Api-Key header; when no key is set and the admin API binds a non-loopback address, the mutating surface refuses to serve.
| Method | Path | Purpose | Auth |
|---|---|---|---|
| GET | /status | Loop health: last cycle time, duration, result, consecutive errors, paused flag | no |
| GET | /decisions | Ring buffer of recent cycle outcomes | no |
| GET | /candidates | Current discovery candidates with threshold evaluation (query text redacted unless authenticated) | no |
| GET | /rules | Rules the plugin owns, with state and enabled flag | no |
| GET | /denylist | Denylist rule count | no |
| GET | /audit | Trail of operator mutations through this API | yes |
| POST | /actions/force-cache | Queue a specific fingerprint for immediate caching | yes |
| POST | /actions/pause | Kill switch: stop all mutations, keep observing | yes |
| POST | /actions/resume | Resume mutations | yes |
| DELETE | /rules/:hash | Drop an owned rule | yes |
POST /actions/pause is the operational kill switch: the plugin keeps running and reporting but creates and drops nothing until resumed. force-cache requires the fingerprint to exist in stats, be SELECT-shaped, and not be denylisted.
Standalone binary
querypilot-accelerator --config <path> runs one full cycle and exits; an external scheduler (cron, or ProxySQL's scheduler table) invokes it repeatedly. A lock file (lock_file, default /tmp/readyset_query_pilot.lock) guarantees a single running instance. --health-check runs only the health-check operation under its own lock, and --dry-run plans without mutating. The .cnf file contains credentials; keep it chmod 600.
The binary manages one of two backends, selected by backend_type.
Managing a Router deployment (backend_type = "sqp")
The Accelerator drives the Router through its admin API: it reads statistics from GET /api/stats/digests, installs and removes dynamic pattern rules through POST /api/pattern-rules and DELETE /api/pattern-rules/:hash, disables rules during warmup with PUT /api/pattern-rules/:hash/disable, and drives pool health through PUT /api/pools/:name/status. It only owns pattern rules targeting the pool named by sqp_readyset_pool.
backend_type = 'sqp'
database_type = 'postgresql' # wire protocol of the readyset pool; required for this backend
sqp_api_url = 'http://127.0.0.1:9091'
sqp_readyset_pool = 'readyset'
sqp_readyset_servers = [
{ host = '127.0.0.1', port = 5433 },
]
warmup_time_s = 60
number_of_queries = 10
query_discovery_mode = 'sum_time'
[[user]]
readyset_user = 'rs_user'
readyset_password = 'rs_pass'
readyset_database = 'rs_db'Pointing it at a non-loopback Router requires https in sqp_api_url plus an sqp_api_token, sent as a bearer token; startup validation enforces both. ProxySQL-only operations are neutralized in this mode: operation_proxysql_cache is forced off with a warning, and cache_mode = "cloud" is rejected at startup.
Managing ProxySQL (backend_type = "proxysql")
For deployments fronted by ProxySQL instead of the Router, the Accelerator connects to ProxySQL's admin interface, reads query statistics from stats_mysql_query_digest (or the PostgreSQL equivalent), and manages digest-match routing rules in mysql_query_rules (destination readyset_hostgroup, source traffic in source_hostgroup, ownership in the rule comment), per-host server status for the Readyset hostgroup driven by the health check, and optionally ProxySQL's own TTL-based query cache for hot queries Readyset cannot cache (operation_proxysql_cache, proxysql_cache_ttl_ms, proxysql_cache_size_mb).
Per-user targets
At least one [[user]] section is required; each defines a Readyset login (readyset_user, readyset_password, optional readyset_database) and gets its own independent discovery pass per invocation. Most tuning keys (number_of_queries, query_discovery_mode, warmup_time_s, and others) can be overridden per user.
Query discovery modes
The standalone form ranks candidates by query_discovery_mode; each mode sorts descending on one metric and takes the top number_of_queries per user. Supported modes:
| Mode | Ranks by |
|---|---|
count_star (default) | total executions |
sum_time | total execution time |
sum_rows_sent | total rows returned |
mean_time | average execution time |
query_throughput | executions per unit of execution time |
Any other value fails at config validation.
The in-process plugin ([plugins.accelerator]) has no query_discovery_mode key: it always ranks by total executions (count_star), with min_execution and wait_s as its threshold filters.
Threshold filters run before ranking: query_discovery_min_execution, query_discovery_min_rows_sent, and query_discovery_wait_s (minimum time a query must have been observed). Statements whose results stream through without decoding can report zero rows sent, so prefer count- and time-based thresholds over min_rows_sent when in doubt.
The denylist
The denylist file (denylist_path, default /etc/readyset_query_pilot_denylist) lists queries the Accelerator must never cache. Each non-empty line is a rule of space-separated key=value tokens; all tokens on a line must match (omitted tokens match anything), and a query is denied if any line matches:
# deny one specific query shape everywhere
digest=0x9A3C0F12B44D21AA
# deny everything in the internal schema, Readyset caches only
db=internal type=readysetTokens: db=<schema>, digest=0x<hash> (fingerprint hashes as reported in statistics and the REST API), and type= (readyset or proxysql, legacy aliases deep/shallow, or all). The denylist gates discovery and force-cache, and the drop-denied cycle stage retroactively removes caches and rules for newly denylisted queries. The file is read at each cycle start; a missing file logs a warning and denies nothing.
Observability
The in-process plugin exports sqp_qp_* Prometheus series through the Router's metrics endpoint: sqp_qp_cycles_total (by result: success/degraded/error), sqp_qp_cycle_duration_seconds, sqp_qp_caches_created_total, sqp_qp_rules_added_total / sqp_qp_rules_disabled_total / sqp_qp_rules_dropped_total, sqp_qp_support_checks_total (by outcome), sqp_qp_errors_total (by stage), and sqp_qp_last_success_timestamp_seconds. Alert on the last of these going stale to catch a stuck loop. GET /status and GET /decisions give the same picture ad hoc. See the metrics reference.
Router
How the Router pools connections, decides where each query runs, and rewrites SQL on the way, with the configuration that controls each behavior.
Guard
How Guard assigns trust to sessions, analyzes queries, and applies policies that block, amend, or reroute risky SQL before it reaches a backend.