Leave a comment at the end of this page or email contact@krishagni.com
Query Risk Assessment for Generated SQL
Overview
OpenSpecimen generates SQL dynamically from AQL queries. Majority of the generated queries execute successfully. However, a small percentage of queries can produce expensive database execution plans. Such queries consume excessive database resources, eventually timing out or affecting overall system performance.
To reduce this risk, OpenSpecimen now supports an optional Query Risk Assessment layer. This layer evaluates the generated SQL before execution using the database explain plan. If the query is considered safe, it is allowed to execute. If the query is risky, it is rejected before it reaches the database execution phase.
This feature is intended to protect the database from slow-running or resource-intensive queries and provide administrators with enough diagnostic information to tune forms, indexes, AQLs, or risk thresholds.
JIRA: OPSMN-7313
High-Level Flow
Expanded Flow
Risk Assessor Internals
Its responsibility is to:
Take the SQL generated from AQL.
Run MySQL
EXPLAIN FORMAT=JSON.Parse the explain plan into an internal structure.
Apply configurable risk rules.
Return one of:
OK: query can run.REJECT: query is considered risky and should not run.Observe only: risk is logged, but query is allowed.
What Happens When a Query Is Rejected?
When a query is rejected, OpenSpecimen notifies the system administrators with the following details:
Original AQL
Generated SQL
Raw explain plan
Rejection reason
Structured risk metrics
Risk Assessment Configuration
Risk assessment is controlled through user-supplied/default configuration. A full default configuration looks like:
{
"enabled": true,
"observeOnly": false,
"largeTableRows": 1000000,
"maxRowsExaminedPerScan": 1000000,
"maxRowsProducedPerJoin": 5000000,
"maxTotalRowsExamined": 10000000,
"maxSortRows": 1000000,
"maxTempTableRows": 1000000,
"maxFullScanRowsForSortOrTemp": 100000,
"maxDependentSubqueryRows": 100000,
"maxJoinTables": 30,
"maxQueryCost": -1.0,
"maxEstimatedJoinWork": -1.0,
"maxJoinBufferRows": 100000,
"minFilteredPercentForFullScan": 10.0
}A value of 0 or -1 disables the corresponding rule.
Risk Rules
Rule | Level | Config Key(s) | Default | Rejects When | Rationale |
|---|---|---|---|---|---|
Total rows examined | Query |
|
| The query is estimated to examine more than | Catches queries whose total scan effort is too high across the whole plan. |
MySQL query cost | Query |
|
| MySQL’s estimated query cost is greater than | Uses MySQL’s global cost estimate. Disabled by default because cost needs production calibration. |
Estimated join work | Query / join |
|
| The estimated repeated join work is greater than | Catches join fan-out where repeated scan work is high. |
Max rows produced by join | Query / join |
|
| A join step is estimated to produce more than | Prevents large intermediate join results. |
Too many joined tables | Query |
|
| The query joins more than | Guards against broad AQLs that create complex join plans. |
Rows examined per table scan | Table |
|
| Any single table access is estimated to examine more than | Catches one table access that is too expensive by itself. |
Full scan on large low-selectivity table | Table |
|
| A table is fully scanned, examines more than | Catches large full scans where meaningful data survives filtering. |
Missing usable key | Table |
|
| A table has candidate indexes, but MySQL chooses no index and estimates scanning more than | Flags possible index mismatch, stale stats, or low-selectivity predicates. |
Join buffer / hash join over large rows | Table / join |
|
| A table join uses join buffer or hash join and is estimated to produce more than | Useful for detecting unindexed joins. |
Large full scan feeding sort/temp table | Hybrid |
|
| The query uses filesort or a temporary table, and at least one table is fully scanned for more than | Catches grouped or ordered aggregate queries that full-scan many rows before temp-table or filesort work. |
Filesort over large result | Query |
|
| The query uses filesort and is estimated to process more than | Prevents expensive sort operations. |
Temporary table over large result | Query |
|
| The query uses a temporary table and is estimated to process more than | Guards against memory or disk pressure from large temporary tables. |
Dependent subquery repeated work | Query / subquery |
|
| A dependent subquery is estimated to perform repeated work greater than | Accounts for repeated execution cost, roughly outer rows multiplied by subquery work. |