Chapter 18 · Diagnostics, Logging, Interceptors, Metrics, and Observability

Query Tags, Command Timeouts, Slow-Query Correlation, and Mapping ORM Events to Database Plans

Correlate a ServiceHub query from TagWith/TagWithCallSite through command duration and timeout evidence to SQLite EXPLAIN QUERY PLAN and optional SQL Server/PostgreSQL plan tools, without confusing client/provider timing with database execution cost.

Advanced150–190 minutestag/timeout/plan-correlation labEF Core 10.0.11 · .NET 10.0.11SQLite provider 10.0.11 mandatory baselinedotnet-ef 10.0.11 · SDK 10.0.400Observability reviewed: August 2026

Learning outcomes

01

Use TagWith and TagWithCallSite as stable correlation aids and understand that tags become SQL comments rather than parameters.

02

Configure command timeout deliberately and explain the provider-specific meaning of timeout, especially Microsoft.Data.Sqlite lock/busy behavior.

03

Capture EF command duration/error evidence without assuming it is the same as database CPU, I/O, lock-wait, or full result-enumeration time.

04

Correlate tagged EF SQL to SQLite EXPLAIN QUERY PLAN and identify equivalent optional plan tools for SQL Server and PostgreSQL.

05

Diagnose a semantically correct but inefficient ServiceHub query using query shape, index evidence, plan output, and trace/log correlation.

06

Build a slow-query triage workflow that protects sensitive SQL/parameters and avoids arbitrary global timeout tuning.

1. A “slow EF query” is a hypothesis, not a diagnosis

When a dispatch endpoint is slow, the delay may come from EF translation, waiting for a pooled connection, a database lock, an inefficient access path, transferring too many rows/columns, materializing/tracking a graph, serialization, or downstream work. Chapter 17 measured those phases in a lab. Production diagnostics now need a stable way to join the application operation, EF command, and database plan without copying parameters into every metric.

Frozen lab baseline

Mandatory baseline: .NET runtime 10.0.11, SDK 10.0.400, Microsoft.EntityFrameworkCore / Design / Sqlite and dotnet-ef 10.0.11. SQLite is the free local database. EF Core 11 preview APIs are out of scope. OpenTelemetry core/hosting 1.18.0 may be used for an optional console telemetry path; OpenTelemetry.Instrumentation.EntityFrameworkCore 1.18.0-beta.1 remains prerelease/beta and is never required for the mandatory labs.

2. Query tags create a stable bridge from LINQ to SQL evidence

csharp · tag a ServiceHub query
var query = db.WorkOrders    .TagWith("servicehub.operation=dispatch.queue")    .TagWithCallSite()    .AsNoTracking()    .Where(w => w.Priority == WorkOrderPriority.High)    .OrderByDescending(w => EF.Property<DateTime>(w, "CreatedUtc"))    .ThenBy(w => w.Id)    .Take(50)    .Select(w => new    {        w.Id,        w.WorkOrderNumber,        w.CustomerName,        CreatedUtc = EF.Property<DateTime>(w, "CreatedUtc")    });Console.WriteLine(query.ToQueryString());var rows = await query.ToListAsync(cancellationToken);

EF emits the tag as a SQL comment. TagWithCallSite can include the source filename and line number. Query tags are not parameterizable; keep them static and low-risk. Do not put request IDs, customer values, usernames, secrets, or work-order numbers into a tag.

sql · representative SQLite SQL comment
-- servicehub.operation=dispatch.queue-- file: .../DispatchQueries.cs:42SELECT "w"."work_order_id", "w"."work_order_number",       "w"."customer_name", "w"."created_utc"FROM "work_orders" AS "w"WHERE "w"."priority" = @__priority_0ORDER BY "w"."created_utc" DESC, "w"."work_order_id"LIMIT @__p_1

3. ToQueryString and command logs are translation evidence, not timing truth

ToQueryString() shows diagnostic SQL text before execution. A Database.Command completion event adds provider-side command duration. Neither proves the database's CPU/I/O plan, and neither includes the entire request pipeline. The OpenTelemetry EF instrumentation likewise documents that command activity duration does not include full enumeration of all query results.

Evidence Useful for Missing
ToQueryString translation shape, predicates, projection, joins, tags execution, locks, server plan, result enumeration
EF command event/interceptor command execution duration/failure on client/provider boundary request work outside command; database internal cause
database plan access path, joins/scans/seeks, estimates/actuals depending engine/tool EF translation/materialization/network outside server
request Activity end-to-end correlated application timing database-internal explanation unless linked

4. Command timeout is provider behavior, not a universal “slow query” limit

EF can set a relational command timeout, but what the underlying provider does with that value matters. For Microsoft.Data.Sqlite, CommandTimeout is specifically used while waiting to obtain a lock; the provider automatically retries busy/locked errors until the timeout. It is not a reliable CPU-time cancellation mechanism for an arbitrary long-running SQLite statement.

csharp · short timeout for a lock-contention lab
await using var db = factory.CreateDbContext();var oldTimeout = db.Database.GetCommandTimeout();try{    db.Database.SetCommandTimeout(2);    await RunLockContentionProbeAsync(db, cancellationToken);}finally{    db.Database.SetCommandTimeout(oldTimeout);}

SQL Server and other providers define command timeout according to their ADO.NET driver semantics. Never copy “2 seconds” into production because it worked in this lab. A production timeout must consider request deadline, retry policy, expected workload, transaction scope, lock behavior, and provider-specific cancellation.

5. Correlate the tag to SQLite EXPLAIN QUERY PLAN

The mandatory free database is SQLite. Use the SQL shape from ToQueryString, substitute safe representative literals in a disposable database, and run EXPLAIN QUERY PLAN. Do not execute a production SQL text containing secrets copied from logs.

sql · SQLite plan probe
EXPLAIN QUERY PLANSELECT work_order_id, work_order_number, customer_name, created_utcFROM work_ordersWHERE priority = 2ORDER BY created_utc DESC, work_order_idLIMIT 50;
text · representative plan evidence
Before useful index:SCAN work_ordersUSE TEMP B-TREE FOR ORDER BYAfter workload-justified index (example only):SEARCH work_orders USING INDEX ...Your actual output depends on schema, SQLite version,statistics, predicates and index definitions.

Do not invent a plan. Capture the learner's actual output and preserve it next to the tagged query and version record.

6. Optional provider plan tools remain provider-specific

Provider Typical plan evidence Important caveat
SQLite EXPLAIN QUERY PLAN compact planner summary; not an EF timing measurement
SQL Server/Azure SQL actual execution plan, Query Store, SET STATISTICS IO/TIME where appropriate permissions and production overhead; plans differ by parameter/cardinality/index/statistics
PostgreSQL EXPLAIN (ANALYZE, BUFFERS) in a safe environment ANALYZE executes the query; writes need transaction/rollback safety
MySQL/MariaDB/Oracle engine/provider plan tooling syntax and metrics differ; consult exact engine/version docs

The point is not to memorize four plan syntaxes. It is to preserve an evidence boundary: EF tells you what it asked the provider to execute; the database tells you how it chose to execute it.

7. Failure case: “fix” every slow command by raising the timeout

A dashboard shows rising timeout errors. An engineer changes the global timeout from 30 seconds to five minutes. The errors disappear, but requests now hold locks/connections for longer and users wait minutes. The root cause—a new unindexed predicate—remains.

Repair: identify the operation tag, inspect command error/duration distribution, correlate to plan/lock evidence, reproduce with realistic data, then change query/index/schema or workload design. Adjust timeout only when the business operation legitimately needs a different deadline.

8. Slow-query exemplars need a stable query ID, not raw parameters

For a dashboard, keep a bounded identifier such as servicehub.operation=dispatch.queue. Store a small number of sampled trace IDs as exemplars in a trace/log system with access control. Do not use full SQL or parameter values as metric dimensions. If SQL text is retained for diagnostics, control access and consider normalization/redaction.

text · safe structured slow-command event
event=ef.command.slowoperation=dispatch.queueprovider=sqliteoutcome=successduration_ms=...trace_id=...command_id=...parameters_captured=falsesql_payload_captured=false

9. Mandatory lab: build the evidence chain

  1. Tag the dispatch-queue query with a stable operation name and optionally TagWithCallSite.
  2. Capture ToQueryString and a safe command completion/failure event.
  3. Run EXPLAIN QUERY PLAN against the disposable SQLite database.
  4. If the plan shows a scan/sort that the workload makes expensive, design a candidate index in the lab and recapture the plan.
  5. Record command duration distribution before/after; do not publish a percentage unless measured.
  6. Create a separate lock-contention probe to show what SQLite's command timeout actually governs.
  7. Verify tags/logs contain no customer/work-order payloads.

10. Production judgment

Query tags are correlation metadata, not security boundaries; timeouts are provider- and operation-specific deadlines, not tuning knobs; EF command timing is one segment of the timeline, not the database plan. Keep the operation identifier stable enough to join traces, logs and plan investigations. The final lesson finishes by converting these signals into a production dashboard and runbook with bounded cardinality and actionable alerts.

Check your understanding

  1. What does TagWith do to generated relational SQL?
  2. Why should a query tag not contain a TraceId or work-order number?
  3. What does Microsoft.Data.Sqlite CommandTimeout primarily govern?
  4. What does ToQueryString fail to tell you?
  5. Why is EXPLAIN ANALYZE more dangerous than a non-executing plan command?
  6. What should be the first response to rising timeouts?
Review the answers

1. It adds the supplied static tag as a SQL comment, making application-to-SQL correlation easier.

2. Tags become SQL literals/comments and high-cardinality or sensitive values create telemetry/privacy problems; use traces/log fields for unique IDs.

3. How long the provider retries/waits when the database is busy or locked, not a universal CPU-time limit for every query.

4. Runtime timing, locks, database execution plan, server I/O/CPU, network cost and result enumeration/materialization.

5. On engines such as PostgreSQL it actually executes the statement; writes need a safe environment/transaction strategy.

6. Correlate the affected operation to command, lock/plan/provider evidence and diagnose the cause before globally increasing the timeout.

Authoritative references

Observability and diagnostics are version-, provider-, and topology-sensitive. Re-check these primary sources before standardizing a production telemetry contract.

Keep knowledge open

Help the academy stay free and grow.

If these tutorials save you time, a small donation supports new lessons, technical review, diagrams, examples, and long-term maintenance.

ETHEthereum / ERC-20 only
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0

Send only Ethereum or ERC-20 compatible assets to this address.

\n