executor, session: extend point-get execution shortcut to non-prepared statements
- Dominant language
- Go
- Stars
- 40.5k
- Forks
- 6.2k
- PR merge metrics
- PR metrics pending
Description
## Enhancement
Extend the point-get execution shortcut (currently used only by prepared statements) to non-prepared `PointGet` / `BatchPointGet` statements.
### Background
In `session.executeStmtImpl`, statements with `stmt.PsStmt != nil` (prepared statements resolved to a point plan) take a simplified execution path that skips the full `runStmt` envelope — rich trace events, flight recorder triggers, and telemetry counters. Non-prepared point-get queries, even when `TryFastPlan` or the non-prepared plan cache has already produced a `*PointGetPlan` / `*BatchPointGetPlan`, still pay the full `runStmt` overhead on every execution.
For high-QPS point-lookup workloads issued as plain text queries (very common with ORMs and simple applications that do not use prepared statements), this per-statement envelope is measurable overhead that the prepared path already avoids.
### Proposal
When the statement's plan is already a `*PointGetPlan` or `*BatchPointGetPlan`, route it through the same execution shortcut used by prepared point-gets. The change is gated behind a new session/global variable `tidb_enable_point_get_exec_shortcut` (default `OFF`) so the behavior can soak before being considered for default-on.
Supporting changes:
- `ExecStmt.PointGet` handles `a.PsStmt == nil`: the prepared-statement executor cache is simply skipped for non-prepared statements, which build a fresh executor each time.
- The shortcut sits after optimization, so it is orthogonal to (and composes with) the non-prepared plan cache.
### Benchmark results
A/B benchmark (`BenchmarkPointGetExecShortcut` in `pkg/planner/core/tests/pointget`) comparing the same non-prepared query with the shortcut off vs. on, mock store, Apple M-series (darwin/arm64), `-benchtime=2s -count=3`:
| Case | Shortcut OFF (ns/op) | Shortcut ON (ns/op) | Improvement |
| --- | --- | --- | --- |
| PointGet | 49363 / 50167 / 51693 | 45072 / 44417 / 44950 | ~10–11% |
| BatchPointGet | 59382 / 64640 / 59104 | 52925 / 52027 / 52488 | ~11–12% |
The ranges do not overlap across runs. On a real cluster the absolute KV latency is larger, but the saved per-statement CPU overhead is the same, so this directly improves point-lookup throughput per core.
### Trade-offs
The shortcut path emits a simplified `stmt.start` trace event and skips some observability work done by the full `runStmt` path (rich trace events, flight recorder triggers, telemetry counters) — the same trade-off prepared point-gets already make today. Defaulting the variable to `OFF` keeps existing behavior unchanged until the observability gap is reviewed.
### Verification
- `go test -run '^$' -bench BenchmarkPointGetExecShortcut -benchtime=2s -count=3 -tags=intest,deadlock` in `pkg/planner/core/tests/pointget` (results above).
- `./tools/check/failpoint-go-test.sh pkg/sessiontxn -run TestNonPreparedPointGetExecShortcut -count=1` — passes; covers correctness of the shortcut path for non-prepared point-get and batch point-get, including txn-context behavior.
Contributor guide
Research direction
Start with session.executeStmtImpl and ExecStmt.PointGet, then inspect the existing prepared point-get shortcut and the session/global variable handling. Run BenchmarkPointGetExecShortcut in pkg/planner/core/tests/pointget and TestNonPreparedPointGetExecShortcut through the listed commands; done means both non-prepared point-get forms use the gated shortcut correctly with transaction context preserved.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- backend, databases
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 48/100