Monitoring and alerting
This guide defines the minimum production signals, dashboards, alerts, and response links for LakeHold. It starts with conservative thresholds. Tune them from measured traffic, but retain alerts for availability, data-protection jobs, capacity, and security even on a quiet deployment.
Signal sources
| Source | What it proves | Limit |
|---|---|---|
GET /health |
API is serving and can connect to the control plane | Does not attach or query every catalog |
GET /alive |
API process can serve a request | Does not test dependencies; not proxied by production nginx |
| Docker health/restart/OOM state | Container lifecycle | Does not prove application integrity |
| OpenTelemetry metrics | Request, runtime, query, session, wire, cache, and maintenance behavior | Requires an external OTLP collector/backend |
| OpenTelemetry traces | Per-request tenant/catalog attribution and operation timing | Sampling must retain errors and slow traces |
| Structured application logs | Startup, scheduler, and exception detail | Local Docker logs rotate and are not durable |
/api/maintenance/schedule |
Latest 100 per-catalog scheduled runs | In-memory; cleared by restart |
| Backup/eject manifests | Artifact completion and recovery point | Same-volume artifacts are not off-host DR |
| Host/storage metrics | CPU, memory, filesystem, volume, and I/O saturation | Supplied by the deployment platform |
Set OTEL_EXPORTER_OTLP_ENDPOINT to a collector reachable from the API. The production Compose file
sets OTEL_SERVICE_NAME=lakehold-api; an unset endpoint exports nothing. Compose includes no
collector or alert manager.
LakeHold emits logs, metrics, and traces through OpenTelemetry. EF instrumentation strips
db.statement and db.query.text, and LakeHold spans never record submitted SQL, credentials,
metadata sources, or data paths. Do not re-add those fields in collectors or processors.
Health probes
Externally through the private workbench ingress:
curl --fail --silent --show-error \
"http://127.0.0.1:${LAKEHOLD_PORT:-8080}/health"
Internally:
docker compose -f compose.production.yaml exec api \
curl --fail --silent --show-error http://127.0.0.1:5200/health
docker compose -f compose.production.yaml exec api \
curl --fail --silent --show-error http://127.0.0.1:5200/alive
Both return only Healthy or Unhealthy and disable caching. Keep them unauthenticated for probes,
but block both at public ingress.
- Readiness
/health: includescontrol-plane. Remove the node from traffic when it fails. - Liveness
/alive: includes only the in-processselfcheck. Restart the process when it fails. Do not add external dependencies; that would turn dependency failure into a restart loop.
Use a 10–15 second interval, 5 second timeout, and at least three consecutive failures before paging. The API container itself uses 15 seconds, 5 seconds, a 45-second start period, and five retries.
LakeHold metrics
The canonical OpenTelemetry instrument names are below. A Prometheus exporter may normalize dots to underscores and append unit or counter suffixes; verify the actual backend names after deployment.
| Instrument | Type/unit | Use |
|---|---|---|
lakehold.query.duration |
histogram, seconds | End-to-end statement latency by lakehold.outcome |
lakehold.query.rows |
histogram, rows | Returned result size |
lakehold.query.truncated |
counter | Results hitting MaxRowsPerResult |
lakehold.session.queue.duration |
histogram, seconds | Time waiting for a catalog's serialized session gate |
lakehold.session.start.duration |
histogram, seconds | Cold extension load and catalog attach |
lakehold.sessions.warm |
up/down counter | Current warm compute sessions |
lakehold.session.evictions |
counter | Evictions by lakehold.reason: idle, overflow, explicit |
lakehold.pool.requests |
counter | Session pool hit/miss |
lakehold.pgwire.connections |
up/down counter | Current wire connections |
lakehold.pgwire.connections.closed |
counter | Clean, refused, or faulted closes |
lakehold.pgwire.auth.failures |
counter | Rejected wire authentication attempts |
lakehold.catalog.cache.requests |
counter | Catalog-cache hit/miss |
lakehold.maintenance.duration |
histogram, seconds | Maintenance latency by operation, dry run, and outcome |
lakehold.maintenance.lease.attempts |
counter | Scheduled lease acquired or held_elsewhere |
Metric tags are deliberately bounded. Tenant and catalog are span attributes
(lakehold.tenant, lakehold.catalog), not metric dimensions, to prevent per-tenant time-series
explosion. Find an affected tenant through sampled error/slow traces after a metric identifies the
class of failure.
Required dashboards
Availability and requests
- ingress and API request rate, status class, p50/p95/p99 duration;
/healthand/alivestatus by node;- container status, restart count, OOM kills, and deployed image digest;
- control-plane connectivity failures;
- current incident and last deployment annotation.
Query and session behavior
lakehold.query.durationby outcome;- query error and truncation rate;
- p95
lakehold.session.queue.durationbeside p95 query duration; - p95
lakehold.session.start.duration; - warm sessions against
Lakehouse:MaxWarmSessions; - pool miss rate and evictions by reason;
- host CPU, API/container memory, filesystem I/O, and object-store latency.
Data protection and maintenance
- last successful flush and backup per catalog;
- failed scheduled runs and incomplete backup generations;
- backup recovery point, age, size, manifest completeness, off-host copy, checksum, and retention;
- maintenance duration and lease outcomes by operation;
- state-volume use and forecasted exhaustion;
- last successful catalog and full-node restore drill.
The schedule endpoint alone cannot power this dashboard because it is in-memory and bounded. Export event 2000 failures and maintenance metrics, and inventory manifests from outside the API.
Security and integration
- HTTP 401/403 rate and unusual changes from baseline;
- wire authentication failures and refused connections;
- token creation, revocation, expiry, and break-glass use from the approved audit source;
- CDC subscriptions with consecutive failures, last attempt/error, and cursor lag;
- TLS/certificate and external credential expiry;
- public exposure checks for API, MCP, PostgreSQL wire,
/health, and/alive.
Initial alert policy
Every page must include service, environment, severity, first observed time, current value, threshold, dashboard link, and a link to the exact incident-response section. Deduplicate by failure domain so a control-plane outage does not page separately for every failed API route.
| Alert | Initial condition | Severity | Runbook |
|---|---|---|---|
| Service not ready | /health fails for 3 consecutive probes or all nodes are unready |
SEV-1 if all traffic is unavailable; otherwise SEV-2 | Service/control plane unavailable |
| Process not alive | /alive fails for 3 probes or restart loop begins |
SEV-2, SEV-1 if all nodes | Service unavailable |
| Elevated server errors | 5xx > 5% for 5 minutes with at least 20 requests | SEV-2 | Rapid classification |
| Query errors | Error outcome materially above baseline for 10 minutes | SEV-2/3 by impact | Query degradation |
| Query queue saturation | p95 queue > 5 seconds for 10 minutes, or queue time > 50% of p95 query time | SEV-3; SEV-2 if critical workload breaches SLO | Query degradation |
| Session churn | Pool miss rate > 20% and overflow evictions rise for 15 minutes | SEV-3 | Query/resource degradation |
| OOM kill | Any API OOM kill | SEV-2; SEV-1 if repeated outage | Resource exhaustion |
| State volume warning | > 80% used or forecast < 7 days | SEV-3 | Resource exhaustion |
| State volume critical | > 90% used or forecast < 24 hours | SEV-2 | Resource exhaustion |
| Flush overdue | No successful flush for 2 configured intervals on a writable catalog | SEV-2 | Maintenance/backup failure |
| Backup failed | 2 consecutive backup failures for a catalog | SEV-2 | Maintenance/backup failure |
| Backup overdue | No complete backup for 2 configured intervals | SEV-2 | Maintenance/backup failure |
| Off-host backup overdue | No verified off-host state copy inside the approved RPO | SEV-2 | Disaster recovery |
| Incomplete generation | A new generation remains without a manifest past the normal job duration | SEV-3, SEV-2 if no newer complete backup | Maintenance/backup failure |
| Maintenance stuck | Duration exceeds 2× its 30-day p95 or the lease duration | SEV-3 | Maintenance/backup failure |
| Wire connection saturation | Connections > 80% of MaxConnections for 10 minutes |
SEV-3 | Query/resource degradation |
| Authentication anomaly | Wire auth failures or HTTP 401s exceed baseline by an agreed rate | SEV-2/3; SEV-1 with exposure evidence | Security incident |
| CDC delivery failing | Consecutive failures ≥ 3 or cursor lag exceeds the consumer SLO | SEV-3; SEV-2 for critical integrations | CDC delivery failure |
| CDC lease takeover | lakehold.cdc.lease.takeovers rises outside a planned node restart |
SEV-3; investigate worker health and PostgreSQL latency | CDC delivery failure |
| CDC worker saturation | lakehold.cdc.workers.active stays at MaxConcurrentSubscriptions and lakehold.cdc.snapshot.lag rises |
SEV-3 | CDC delivery failure |
| Restore drill missed/failed | Required monthly or quarterly drill absent or failed | SEV-3 governance alert | Disaster recovery |
For a low-traffic deployment, ratio alerts alone are noisy: one failed request can be 100%. Always combine a ratio with a minimum count, or use an absolute failure count and longer window.
The five-second queue and 20% pool-miss values are starting points, not product limits. Replace them after establishing a 30-day baseline and service objectives. Do not tune away backup, volume, readiness, OOM, or security alerts merely because they fired correctly.
Logs and events
The production containers use Docker's json-file driver with three 10 MB files each. This protects
the host from unbounded logs but is not sufficient retention. Export logs before rotation.
Important scheduler events:
| Event ID | Level | Meaning |
|---|---|---|
| 2000 | Error | Scheduled operation failed for a tenant/catalog |
| 2001 | Information | Effective flush, backup, and compact schedules at startup |
| 2002 | Information | Scheduling is disabled |
Alert on event 2000. Inventory 2001 at deployment so an unexpected cadence is detected. Treat 2002 as an alert in production unless a recorded change explicitly disabled schedules.
Log searches and exported fields must redact:
- bootstrap and bearer tokens;
- PostgreSQL and object-store credentials or connection strings;
- webhook secrets and signed headers;
- eject signing keys;
- user SQL and result data.
Backup monitoring
A successful HTTP response or scheduler log is not enough. For every writable catalog, verify:
- a generation exists within the expected interval;
completeis true, which meansmanifest.jsonexists and parsed;- the manifest timestamp and snapshot match the intended recovery point;
- the generation is copied outside the primary failure domain where required;
- the off-host object/archive checksum or provider integrity check passes;
- retention keeps enough generations without deleting the only proven recovery point;
- a representative generation has been restored within the drill window.
The default production host currently puts backup generations in the state volume. Monitor the off-host state archive separately; a green catalog-backup job with no off-host copy still leaves whole-volume-loss RPO unbounded.
Alert validation
At installation and at least quarterly:
- stop a disposable API and prove readiness/liveness pages reach the on-call route;
- make a disposable control plane unavailable and prove readiness fails without a restart storm;
- induce a test scheduler failure and confirm event 2000 plus the backup alert;
- fill a disposable filesystem past warning and critical thresholds;
- generate rejected authentication attempts through an isolated test endpoint;
- fail a test CDC receiver and verify consecutive-failure/cursor-lag alerting;
- complete a restore drill and verify the dashboard records its RPO/RTO.
Record alert creation time, notification time, acknowledgement, runbook usability, and cleanup. Fix missing labels, links, ownership, and deduplication before calling the monitoring gate complete.