Skip to content

feat(mysql-dual-conn, tidb-stmt-cache): exercise COM_STMT_RESET + prepared-stmt orphan-EXECUTE#137

Merged
gouravkrosx merged 2 commits into
mainfrom
feat/mysql-dual-conn-stmt-reset
Jun 1, 2026
Merged

feat(mysql-dual-conn, tidb-stmt-cache): exercise COM_STMT_RESET + prepared-stmt orphan-EXECUTE#137
gouravkrosx merged 2 commits into
mainfrom
feat/mysql-dual-conn-stmt-reset

Conversation

@khareyash05
Copy link
Copy Markdown
Member

@khareyash05 khareyash05 commented May 28, 2026

Description

This PR bundles two related sample-app additions whose CI counterparts both land on keploy/keploy@fix/mysql-synthetic-prepare-ok-cachePrepStmts.

mysql-dual-conn//api/oms/stmt-reset/{n} (existing in this PR)

Adds GET /api/oms/stmt-reset/{n} that re-executes a server-side prepared statement n times on the same JDBC connection. Also updates the OMS JDBC URL with useServerPrepStmts=true&cachePrepStmts=true&useCursorFetch=true so MySQL Connector/J 8.x emits COM_STMT_RESET between re-executions.

Exercises keploy's COM_STMT_RESET synthetic-OK fallback (keploy/keploy#4217).

tidb-stmt-cache/ — new sample (this commit)

New Spring Boot 3 + JdbcTemplate sample targeting pingcap/tidb:v8.5.6 on :4000. Drives MySQL Connector/J prepared-statement traffic with useServerPrepStmts=true&cachePrepStmts=true. HikariCP pool LIFO causes Connector/J to skip COM_STMT_PREPARE and emit COM_STMT_EXECUTE only on cache-hit calls — exercising the orphan-EXECUTE matching path that keploy's param-alone fallback (match.go:967-976, from commit b2e68adb) handles.

Endpoints:

  • GET /api/health — plain SELECT 1, used by wait_for_app.
  • GET /api/kv/{v} — prepared SELECT ? AS v; same SQL across calls so the cache hits.
  • GET /api/kv/insert-select/{v} — prepared INSERT + prepared SELECT on the same connection, two concurrent prepares.

TiDB runs with --store=unistore so boot is ~5s and there's no PD/TiKV flakiness. Schema is bootstrapped by a CommandLineRunner inside the app — no init.sql needed (TiDB unistore doesn't run /docker-entrypoint-initdb.d/).

Type of change

  • New feature (non-breaking change which adds functionality)

How Has This Been Tested?

mysql-dual-conn was previously tested as described in the original PR body.

tidb-stmt-cache is exercised end-to-end via the keploy-side companion PR's CI matrix entry — local reproduction against TiDB wasn't run because the keploy proxy needs eBPF / Linux, which isn't available on the dev host. Each of the three compat-matrix cells (record_latest_replay_build, record_build_replay_latest, record_build_replay_build) will record + replay traffic that includes cachePrepStmts-cached EXECUTEs. The keploy-side script also strips one COM_STMT_PREPARE mock from the recorded mocks.yaml before replay as a deterministic guard so the param-alone fallback fires on every run regardless of natural HikariCP timing.

# Local sanity check (TiDB only, no keploy proxy)
cd tidb-stmt-cache
docker compose up -d
mvn -B -DskipTests package
java -jar target/tidb-stmt-cache-0.0.1-SNAPSHOT.jar &
curl http://localhost:8080/api/health
curl http://localhost:8080/api/kv/1
curl http://localhost:8080/api/kv/2
curl http://localhost:8080/api/kv/insert-select/42

Additional Context

  • The mysql-dual-conn/ endpoint deliberately keeps the existing dual-handshake test path unaffected — Camunda pool's JDBC URL is unchanged.
  • tidb-stmt-cache/ is a new top-level directory next to mysql-dual-conn/; the two samples share no code.

Related: keploy/keploy#4217 (COM_STMT_RESET fix), keploy/keploy b2e68adb (orphan-EXECUTE fix).

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • My changes generate no new warnings
  • I have signed the commit message (`--signoff`)

Adds GET /api/oms/stmt-reset/{n} which re-executes a server-side prepared
statement n times on the SAME JDBC connection. Updates the OMS JDBC URL
with useServerPrepStmts=true, cachePrepStmts=true, and useCursorFetch=true
so MySQL Connector/J 8.x emits COM_STMT_RESET between re-executions.

This exercises the COM_STMT_RESET synthetic-OK fallback added in
keploy/keploy#4217 during keploy record/replay against this existing
samples-java CI app, avoiding the need to wire a brand new sample
(spring-mysql-redis) into keploy's java_linux pipeline. The existing
dual-handshake test path is unchanged — the Camunda pool keeps the
original JDBC URL, and the new endpoint is purely additive.

Signed-off-by: Yash Khare <khareyash05@gmail.com>
gouravkrosx pushed a commit to keploy/keploy that referenced this pull request May 29, 2026
…4225)

Adds one curl to /api/oms/stmt-reset/5 inside the mysql-dual-conn
record loop so the java_linux pipeline now exercises the
COM_STMT_RESET synthetic-OK fallback added in #4217. The endpoint
itself is added in keploy/samples-java#137.

Without this, the only Java + MySQL CI app exercises COM_QUERY and
HandshakeResponse41 paths but never re-executes a server-prepared
statement on the same JDBC connection, so the COM_STMT_RESET
branch in matchCommand has no e2e coverage. With this curl, all
three java_linux compat-matrix cells (record_latest_replay_build,
record_build_replay_latest, record_build_replay_build) will fail
if the synthetic-OK fallback ever regresses.

Signed-off-by: Yash Khare <khareyash05@gmail.com>
…CUTE

Adds a minimal Spring Boot 3 + JdbcTemplate sample that drives MySQL
Connector/J prepared-statement traffic against a single-node TiDB :4000.
The combination useServerPrepStmts=true + cachePrepStmts=true plus
HikariCP pool LIFO causes Connector/J to skip COM_STMT_PREPARE and emit
COM_STMT_EXECUTE only on cache-hit calls -- exercising the orphan-EXECUTE
matching path that keploy/keploy@b2e68adb's param-alone fallback handles.

A companion CI script and matrix entry will land on keploy/keploy in the
same fix branch (fix/mysql-synthetic-prepare-ok-cachePrepStmts) so the
three compat-matrix cells (record_latest_replay_build, etc.) start
exercising this sample once both PRs merge.

The pingcap/tidb:v8.5.6 image runs with --store=unistore to keep boot
time ~5s and avoid PD/TiKV flakiness; schema (the kv table) is bootstrapped
by a CommandLineRunner inside the app so no init.sql is needed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Yash Khare <khareyash05@gmail.com>
@khareyash05 khareyash05 changed the title feat(mysql-dual-conn): add endpoint to trigger COM_STMT_RESET feat(mysql-dual-conn, tidb-stmt-cache): exercise COM_STMT_RESET + prepared-stmt orphan-EXECUTE Jun 1, 2026
khareyash05 added a commit to keploy/keploy that referenced this pull request Jun 1, 2026
Wires the new keploy/samples-java/tidb-stmt-cache app into the java_linux
workflow via the standard three-cell compat matrix (record_latest_replay_build,
record_build_replay_latest, record_build_replay_build).

The script drives prepared-statement traffic against single-node TiDB
(pingcap/tidb:v8.5.6, unistore) with Connector/J cachePrepStmts on, then
mutates the recorded YAML mocks.yaml to drop one COM_STMT_PREPARE mock
before replay. That yq surgery deterministically forces an unresolvable
(connID, stmtID) lookup in recordedPrepByConn during replay, so the
param-alone EXECUTE fallback added in b2e68ad (match.go ~967-976) is
exercised on every CI run regardless of whether the natural HikariCP
LIFO timing happened to produce the orphan condition on its own.

If the fallback ever regresses to "no matching mock", the
test-set-*-report.yaml status flips to FAILED and the job fails loudly.

The companion sample app lives on keploy/samples-java#137 and must merge
before this job's CI will pass on this PR.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Yash Khare <khareyash05@gmail.com>
@gouravkrosx gouravkrosx merged commit 9738912 into main Jun 1, 2026
2 checks passed
khareyash05 added a commit to keploy/keploy that referenced this pull request Jun 1, 2026
…aned mocks (#4226)

* fix(mysql/replay): synthesize PREPARE_OK + accept EXECUTE on params alone for orphaned mocks

JDBC's client-side prepared-statement cache (useServerPrepStmts=true&
cachePrepStmts=true, common with TiDB) reuses server-allocated statement
IDs across requests without re-issuing COM_STMT_PREPARE. When such a
statement ID predates the recording window, the recorder captures the
COM_STMT_EXECUTE but never its PREPARE setup. On replay, the driver's
cache is cold so it does send COM_STMT_PREPARE — and finds no mock,
dropping the connection with "no matching mock found".

This change adds two complementary fallbacks:

1. matchCommand: when COM_STMT_PREPARE finds no mock, synthesize a
   COM_STMT_PREPARE_OK with a fresh statement_id from
   decodeCtx.NextStmtID and one placeholder ColumnDefinition41 per "?"
   in the request SQL (type=252/MYSQL_TYPE_BLOB, the standard "unknown"
   placeholder). The synthetic stmtID + query are stashed in
   decodeCtx.PreparedStatements / StmtIDToQuery so the subsequent
   EXECUTE can resolve actualQuery.

2. matchStmtExecutePacketQueryAware: when the mock has no recorded
   PREPARE for its stmtID (expectedQuery==""), accept the EXECUTE on
   allParamsMatched alone. Per-test mocks are consumed on match so
   ordered EXECUTEs with identical param signatures still pick up
   distinct mocks.

Follows the same policy as 9e16ae4 (synthetic OK for COM_STMT_RESET)
and the synthetic OK for unmocked control/DDL in matchCommand —
tolerate protocol traffic the record window didn't capture but doesn't
require a recorded response for correctness.

* chore: remove comments

Signed-off-by: Yash Khare <khareyash05@gmail.com>

* ci(java/tidb-stmt-cache): exercise prepared-stmt orphan-EXECUTE matching

Wires the new keploy/samples-java/tidb-stmt-cache app into the java_linux
workflow via the standard three-cell compat matrix (record_latest_replay_build,
record_build_replay_latest, record_build_replay_build).

The script drives prepared-statement traffic against single-node TiDB
(pingcap/tidb:v8.5.6, unistore) with Connector/J cachePrepStmts on, then
mutates the recorded YAML mocks.yaml to drop one COM_STMT_PREPARE mock
before replay. That yq surgery deterministically forces an unresolvable
(connID, stmtID) lookup in recordedPrepByConn during replay, so the
param-alone EXECUTE fallback added in b2e68ad (match.go ~967-976) is
exercised on every CI run regardless of whether the natural HikariCP
LIFO timing happened to produce the orphan condition on its own.

If the fallback ever regresses to "no matching mock", the
test-set-*-report.yaml status flips to FAILED and the job fails loudly.

The companion sample app lives on keploy/samples-java#137 and must merge
before this job's CI will pass on this PR.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Yash Khare <khareyash05@gmail.com>

* ci(java): temporarily pin tidb-stmt-cache checkout to compose-flags fix branch

The tidb-stmt-cache sample on samples-java/main currently ships a
docker-compose.yml that passes invalid double-dash flags to the TiDB
entrypoint, so the container exits at startup and the matrix job
never reaches :4000.

Pin samples-java checkout to fix/tidb-stmt-cache-compose-flags
(keploy/samples-java#139) so this branch can produce real CI signal
for the matcher fix. Revert this pin to the default branch once #139
merges.

Signed-off-by: Yash Khare <khareyash05@gmail.com>

* remove pipelines

---------

Signed-off-by: Yash Khare <khareyash05@gmail.com>
Co-authored-by: Gourav kumar <44055698+gouravkrosx@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants