{
"__inputs": [
{
"name": "DS_PROMETHEUS",
"label": "Prometheus",
"type": "datasource",
"pluginId": "prometheus"
}
],
"__requires": [
{ "type": "grafana", "id": "grafana", "version": "10.0.0" },
{ "type": "datasource", "id": "prometheus", "version": "1.0.0" },
{ "type": "panel", "id": "timeseries", "version": "" },
{ "type": "panel", "id": "stat", "version": "" },
{ "type": "panel", "id": "gauge", "version": "" }
],
"title": "FraiseQL Performance",
"uid": "fraiseql-perf-v1",
"version": 1,
"schemaVersion": 38,
"tags": ["fraiseql", "graphql", "performance"],
"refresh": "30s",
"time": { "from": "now-1h", "to": "now" },
"timepicker": {},
"templating": {
"list": [
{
"name": "instance",
"label": "Instance",
"type": "query",
"datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" },
"query": "label_values(fraiseql_graphql_queries_total, instance)",
"refresh": 2,
"multi": false,
"includeAll": true,
"allValue": ".*"
}
]
},
"panels": [
{
"id": 1,
"title": "Requests / sec",
"description": "GraphQL query throughput over 1-minute windows.",
"type": "timeseries",
"gridPos": { "x": 0, "y": 0, "w": 8, "h": 8 },
"datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" },
"targets": [
{
"expr": "rate(fraiseql_graphql_queries_total{instance=~\"$instance\"}[1m])",
"legendFormat": "queries/s"
}
],
"fieldConfig": {
"defaults": { "unit": "reqps", "color": { "mode": "palette-classic" } }
},
"options": { "tooltip": { "mode": "multi" } }
},
{
"id": 2,
"title": "Query Latency (P50 / P95 / P99)",
"description": "Histogram quantiles for GraphQL query execution time.",
"type": "timeseries",
"gridPos": { "x": 8, "y": 0, "w": 8, "h": 8 },
"datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" },
"targets": [
{
"expr": "histogram_quantile(0.50, rate(fraiseql_request_duration_seconds_bucket{instance=~\"$instance\"}[5m]))",
"legendFormat": "P50"
},
{
"expr": "histogram_quantile(0.95, rate(fraiseql_request_duration_seconds_bucket{instance=~\"$instance\"}[5m]))",
"legendFormat": "P95"
},
{
"expr": "histogram_quantile(0.99, rate(fraiseql_request_duration_seconds_bucket{instance=~\"$instance\"}[5m]))",
"legendFormat": "P99"
}
],
"fieldConfig": {
"defaults": { "unit": "s", "color": { "mode": "palette-classic" } }
}
},
{
"id": 3,
"title": "Error Rate",
"description": "Percentage of GraphQL queries that returned an error.",
"type": "stat",
"gridPos": { "x": 16, "y": 0, "w": 8, "h": 8 },
"datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" },
"targets": [
{
"expr": "100 * rate(fraiseql_graphql_queries_total{status=\"error\",instance=~\"$instance\"}[5m]) / rate(fraiseql_graphql_queries_total{instance=~\"$instance\"}[5m])",
"legendFormat": "error %"
}
],
"fieldConfig": {
"defaults": {
"unit": "percent",
"thresholds": {
"mode": "absolute",
"steps": [
{ "color": "green", "value": null },
{ "color": "yellow", "value": 1 },
{ "color": "red", "value": 5 }
]
}
}
}
},
{
"id": 4,
"title": "Cache Hit Ratio",
"description": "Fraction of GraphQL queries served from result cache.",
"type": "stat",
"gridPos": { "x": 0, "y": 8, "w": 8, "h": 8 },
"datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" },
"targets": [
{
"expr": "100 * rate(fraiseql_cache_hits_total{instance=~\"$instance\"}[5m]) / (rate(fraiseql_cache_hits_total{instance=~\"$instance\"}[5m]) + rate(fraiseql_cache_misses_total{instance=~\"$instance\"}[5m]))",
"legendFormat": "hit %"
}
],
"fieldConfig": {
"defaults": {
"unit": "percent",
"min": 0, "max": 100,
"thresholds": {
"mode": "absolute",
"steps": [
{ "color": "red", "value": null },
{ "color": "yellow", "value": 50 },
{ "color": "green", "value": 80 }
]
}
}
}
},
{
"id": 5,
"title": "Cache Hits vs Misses / sec",
"description": "Cache hit and miss throughput over 1-minute windows.",
"type": "timeseries",
"gridPos": { "x": 8, "y": 8, "w": 8, "h": 8 },
"datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" },
"targets": [
{
"expr": "rate(fraiseql_cache_hits_total{instance=~\"$instance\"}[1m])",
"legendFormat": "hits/s"
},
{
"expr": "rate(fraiseql_cache_misses_total{instance=~\"$instance\"}[1m])",
"legendFormat": "misses/s"
}
],
"fieldConfig": {
"defaults": { "unit": "ops", "color": { "mode": "palette-classic" } }
}
},
{
"id": 6,
"title": "APQ Stored Queries",
"description": "Total number of Automatic Persisted Queries stored in the APQ cache.",
"type": "stat",
"gridPos": { "x": 16, "y": 8, "w": 8, "h": 8 },
"datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" },
"targets": [
{
"expr": "fraiseql_apq_stored_total{instance=~\"$instance\"}",
"legendFormat": "stored"
}
],
"fieldConfig": {
"defaults": { "unit": "short" }
}
},
{
"id": 7,
"title": "Connection Pool (total / active / idle)",
"description": "Database connection pool utilisation over time.",
"type": "timeseries",
"gridPos": { "x": 0, "y": 16, "w": 8, "h": 8 },
"datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" },
"targets": [
{
"expr": "fraiseql_db_pool_connections_total{instance=~\"$instance\"}",
"legendFormat": "total"
},
{
"expr": "fraiseql_db_pool_connections_active{instance=~\"$instance\"}",
"legendFormat": "active"
},
{
"expr": "fraiseql_db_pool_connections_idle{instance=~\"$instance\"}",
"legendFormat": "idle"
}
],
"fieldConfig": {
"defaults": { "unit": "short", "color": { "mode": "palette-classic" } }
},
"options": { "fillOpacity": 20 }
},
{
"id": 8,
"title": "Requests Waiting for Connection",
"description": "Number of queries queued waiting for a free database connection.",
"type": "gauge",
"gridPos": { "x": 8, "y": 16, "w": 8, "h": 8 },
"datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" },
"targets": [
{
"expr": "fraiseql_db_pool_requests_waiting{instance=~\"$instance\"}",
"legendFormat": "waiting"
}
],
"fieldConfig": {
"defaults": {
"unit": "short",
"min": 0, "max": 50,
"thresholds": {
"mode": "absolute",
"steps": [
{ "color": "green", "value": null },
{ "color": "yellow", "value": 5 },
{ "color": "red", "value": 15 }
]
}
}
}
},
{
"id": 9,
"title": "Pool Pressure / Scaling Recommendations / sec",
"description": "Rate of pool scaling recommendations emitted by the pressure monitor (recommendation mode; pool is not resized at runtime).",
"type": "timeseries",
"gridPos": { "x": 16, "y": 16, "w": 8, "h": 8 },
"datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" },
"targets": [
{
"expr": "rate(fraiseql_pool_tuning_adjustments_total{instance=~\"$instance\"}[5m])",
"legendFormat": "adjustments/s"
}
],
"fieldConfig": {
"defaults": { "unit": "ops" }
}
},
{
"id": 10,
"title": "Multi-Root Query Rate",
"description": "Queries dispatched via the parallel multi-root execution path.",
"type": "timeseries",
"gridPos": { "x": 0, "y": 24, "w": 8, "h": 8 },
"datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" },
"targets": [
{
"expr": "rate(fraiseql_multi_root_queries_total{instance=~\"$instance\"}[1m])",
"legendFormat": "multi-root/s"
}
],
"fieldConfig": {
"defaults": { "unit": "reqps" }
}
},
{
"id": 11,
"title": "Recommended vs Current Pool Size",
"description": "Pool pressure monitor recommended size vs current total. Adjust max_connections and restart to act on recommendations.",
"type": "timeseries",
"gridPos": { "x": 8, "y": 24, "w": 8, "h": 8 },
"datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" },
"targets": [
{
"expr": "fraiseql_pool_recommended_size{instance=~\"$instance\"}",
"legendFormat": "recommended"
},
{
"expr": "fraiseql_db_pool_connections_total{instance=~\"$instance\"}",
"legendFormat": "current"
}
],
"fieldConfig": {
"defaults": { "unit": "short", "color": { "mode": "palette-classic" } }
}
},
{
"id": 12,
"title": "Database Error Rate",
"description": "Rate of database-layer errors per second.",
"type": "timeseries",
"gridPos": { "x": 16, "y": 24, "w": 8, "h": 8 },
"datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" },
"targets": [
{
"expr": "rate(fraiseql_db_errors_total{instance=~\"$instance\"}[1m])",
"legendFormat": "db errors/s"
}
],
"fieldConfig": {
"defaults": {
"unit": "ops",
"thresholds": {
"mode": "absolute",
"steps": [
{ "color": "green", "value": null },
{ "color": "red", "value": 0.1 }
]
}
}
}
},
{
"title": "Database Query Stats (Top 5)",
"type": "table",
"gridPos": { "x": 0, "y": 32, "w": 24, "h": 8 },
"datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" },
"targets": [
{
"expr": "topk(5, fraiseql_db_query_exec_seconds{instance=~\"$instance\"})",
"legendFormat": "{{query}}",
"format": "table",
"instant": true
},
{
"expr": "topk(5, fraiseql_db_query_calls{instance=~\"$instance\"})",
"legendFormat": "{{query}}",
"format": "table",
"instant": true
},
{
"expr": "topk(5, fraiseql_db_cache_hit_ratio{instance=~\"$instance\"})",
"legendFormat": "{{query}}",
"format": "table",
"instant": true
}
],
"fieldConfig": {
"defaults": {},
"overrides": [
{
"matcher": { "id": "byName", "options": "Value #A" },
"properties": [{ "id": "displayName", "value": "Exec Time (s)" }]
},
{
"matcher": { "id": "byName", "options": "Value #B" },
"properties": [{ "id": "displayName", "value": "Calls" }]
},
{
"matcher": { "id": "byName", "options": "Value #C" },
"properties": [{ "id": "displayName", "value": "Cache Hit Ratio" }]
}
]
},
"transformations": [
{
"id": "merge",
"options": {}
}
]
}
]
}