Skip to content

fix(ci-mysqlx): cache runtime deps + unblock soak listener bind#5852

Merged
renecannao merged 20 commits into
v3.0from
v3.0_fix_ci-mysqlx-cache-and-soak
Jul 5, 2026
Merged

fix(ci-mysqlx): cache runtime deps + unblock soak listener bind#5852
renecannao merged 20 commits into
v3.0from
v3.0_fix_ci-mysqlx-cache-and-soak

Conversation

@renecannao

@renecannao renecannao commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

Summary

Two follow-up CI fixes surfaced after #5823 landed:

  1. .github/workflows/CI-mysqlx.yml — add the shared libs that test/tap/tap/libtap.so DT_NEEDEDs at runtime to every build-cache path. Without deps/re2/re2/obj/so/libre2.so.10 and deps/postgresql/.../libpq.so.5 in the cache, the downstream e2e-tests (and soak-tests) jobs restore the test binaries but the dynamic loader fails with `error while loading shared libraries: libre2.so.10: cannot open shared object file`. The unit-test binaries don't link libtap.so, which is why the gap stayed hidden.

  2. test/tap/groups/mysqlx-soak/setup-infras.bash — `SELECT COUNT(*) FROM runtime_mysql_users` and `runtime_mysql_servers` between `LOAD MYSQL ... TO RUNTIME` and `LOAD MYSQLX ... TO RUNTIME`. The mysqlx plugin's `install_users_from_admin` / `install_endpoints_from_admin` query those tables directly via SQLite, but admin populates them only via its lazy SELECT-triggered refresh — not by `LOAD MYSQL {USERS,SERVERS} TO RUNTIME`. Without the nudge the plugin sees empty rows, the route can't resolve, and the listener never binds, surfacing as `ERROR: mysqlx listener did not bind on port 6603 within 10s`.

Both diagnosed from the CI-mysqlx run for 3e80983 (PR #5823's pre-merge SHA).

Test plan

  • CI-mysqlx build job succeeds (no behaviour change at build time)
  • CI-mysqlx unit-tests job continues to pass
  • CI-mysqlx e2e-tests job passes (was failing on `libre2.so.10` before)
  • CI-mysqlx soak-tests job's "Bring up infrastructure" step succeeds (was failing on listener bind before)

Summary by CodeRabbit

  • Tests

    • Improved soak and integration test reliability with additional runtime-table probes and SQL checks to verify rows before/after plugin load, reducing flakiness and improving CI observability.
  • Chores

    • CI now stages native runtime libraries and exports a test runtime library path so unit, e2e, and soak tests reliably load native dependencies; build cache keys updated to match.
  • Bug Fixes

    • Improved listener startup diagnostics and resource cleanup so socket bind/listen failures produce clearer error output and close resources properly.

CI-mysqlx.yml: extend the build cache to include the deps shared
libraries that test/tap/tap/libtap.so DT_NEEDEDs at runtime — without
them, downstream jobs restore the linked binaries but the loader
fails with "libre2.so.10: cannot open shared object file". libtap.so
pulls libre2.so.10 (from deps/re2/re2/obj/so/) and libpq.so.5 (from
deps/postgresql/.../libpq/) via -Wl,-rpath at build time, so the
exact .so siblings must be cached alongside the binaries that link
to them. Surfaced by test_mysqlx_e2e_handshake-t in the e2e-tests
job — the unit-tests binaries don't link libtap.so so they were
fine.

mysqlx-soak/setup-infras.bash: SELECT runtime_mysql_users and
runtime_mysql_servers before LOAD MYSQLX {USERS,BACKEND ENDPOINTS,
ROUTES} TO RUNTIME. The mysqlx plugin's install_*_from_admin
helpers query those tables directly via SQLite, but admin only
populates them on its lazy SELECT-trigger refresh — not by LOAD
MYSQL {USERS,SERVERS} TO RUNTIME. Without the COUNT(*) nudge here
the plugin sees empty source rows, the route never resolves to a
usable hostgroup, and the listener never binds on the soak port —
surfacing as "ERROR: mysqlx listener did not bind on port 6603
within 10s" in CI.
@coderabbitai

coderabbitai Bot commented Jun 13, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

CI now stages libre2.so.10 and libpq.so.5 into test/tap/tap/_runtime_libs/ and updates downstream jobs to restore the new cache key and prepend that dir to LD_LIBRARY_PATH. Soak setup adds runtime-table COUNT probes. mysqlx listener errors now emit diagnostics and clean up the fd on failures.

Changes

MySQLX Test Infrastructure Enhancement

Layer / File(s) Summary
Stage runtime shared libs and export LD_LIBRARY_PATH
.github/workflows/CI-mysqlx.yml
Build cache key bumped to build-runtime-libs-v2-${SHA}; build copies libre2.so.10 and libpq.so.5 into test/tap/tap/_runtime_libs/; unit-tests/e2e-tests/soak-tests restore the updated cache; unit-tests and e2e-tests prepend that dir to LD_LIBRARY_PATH.
Probe runtime tables before/after LOAD MYSQLX
test/tap/groups/mysqlx-soak/setup-infras.bash
Adds --table to the mysql invocation and inserts pre-LOAD MYSQLX SELECT COUNT(*) probes for runtime_mysql_users/runtime_mysql_servers, and post-LOAD MYSQLX COUNT(*) probes for runtime_mysqlx_users, runtime_mysqlx_routes, and runtime_mysqlx_backend_endpoints.
Improve add_listener() diagnostics and fd cleanup
plugins/mysqlx/src/mysqlx_thread.cpp
Adds #include <cstdio> and replaces generic failure returns in Mysqlx_Thread::add_listener() with fprintf(stderr, ...) diagnostics including bind address, port, route name, and strerror(errno); closes listener fd on bind failure and logs listen failures before returning -1.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

Possibly related PRs

  • sysown/proxysql#5645: Also modifies plugins/mysqlx/src/mysqlx_thread.cpp around listener lifecycle and routing.
  • sysown/proxysql#5642: Another change touching Mysqlx_Thread implementation and related behaviors.
  • sysown/proxysql#5797: Also updates .github/workflows/CI-mysqlx.yml cache/build behavior for mysqlx CI jobs.

Poem

🐇 I hid two libs in a snug little nest,
Told tests where to look so they’d do their best.
I counted rows before and after the load,
And when sockets failed I shouted the code.
Hop-skip—CI passes; the rabbit applauds.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main changes: caching runtime dependencies and fixing a listener bind issue in CI-mysqlx workflow.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch v3.0_fix_ci-mysqlx-cache-and-soak

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the setup-infras.bash script for the mysqlx-soak test group. It adds SELECT COUNT(*) queries on runtime_mysql_users and runtime_mysql_servers to force a lazy-refresh of these tables before loading MySQLX configurations, preventing empty source rows. There are no review comments to address, and I have no additional feedback to provide.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/CI-mysqlx.yml:
- Around line 57-58: The RE2 runtime location is misaligned: ensure the cached
path deps/re2/re2/obj/so/ is the canonical runtime dir and update build/link
flags to point at its "so" subdir; specifically, set RE2_LDIR references (or the
places that form -L and -Wl,-rpath) to use $(RE2_LDIR)/so where libre2.so*
actually lives, and/or add explicit -L$(RE2_LDIR)/so and
-Wl,-rpath,$(RE2_LDIR)/so in targets that link -lre2 (notably the linkage in
test/tap/tap/Makefile and any targets like
test/tap/tests_with_deps/deprecate_eof_support/Makefile that perform dynamic
linking), so libtap.so and executables can resolve libre2.so from the cached
obj/so directory.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 837d57ca-cac0-4b46-832f-1ee31fb2d36b

📥 Commits

Reviewing files that changed from the base of the PR and between 6a33b85 and 8b20a19.

📒 Files selected for processing (2)
  • .github/workflows/CI-mysqlx.yml
  • test/tap/groups/mysqlx-soak/setup-infras.bash
📜 Review details
🧰 Additional context used
📓 Path-based instructions (1)
{test/infra/**/*.bash,test/tap/**/Makefile,.github/workflows/**/*.yml}

📄 CodeRabbit inference engine (CLAUDE.md)

ALWAYS use run-tests-isolated.bash for running TAP tests. It handles infrastructure setup, ProxySQL start, test execution, and cleanup. Never manually create Docker networks, start containers, or run init scripts.

Files:

  • .github/workflows/CI-mysqlx.yml
🔇 Additional comments (2)
.github/workflows/CI-mysqlx.yml (1)

57-58: LGTM!

Also applies to: 150-151, 214-215, 428-429

test/tap/groups/mysqlx-soak/setup-infras.bash (1)

85-95: LGTM!

Comment thread .github/workflows/CI-mysqlx.yml Outdated
The previous attempt added deps/re2/re2/obj/so/ and deps/postgresql/
.../libpq/libpq.so* to the build cache, but tar can't restore those
on the downstream jobs:

  /usr/bin/tar: deps/re2/re2/obj: Cannot mkdir: No such file or directory

`deps/re2/re2` is a symlink to `re2-2022-12-01/`, which is the
extracted tarball and not part of the git tree. Sparse-checkout
materialises the symlink but not its target, so tar can't traverse
into deps/re2/re2/obj/ on restore.

Sidestep the symlink chain: at the end of the build step copy the
two SONAMEs libtap.so DT_NEEDEDs — libre2.so.10 and libpq.so.5 —
into test/tap/tap/_runtime_libs/. That path is under test/, which
the cache already preserves, and lives entirely inside the git
tree (no symlinks to chase). The unit-tests and e2e-tests run
steps then prepend that directory to LD_LIBRARY_PATH so the
loader resolves libtap.so's deps without needing the deps/ tree
materialised.

soak-tests is unaffected — those binaries run inside Docker
containers with their own library path.
…effect

Pre-existing caches keyed `build-${SHA}` were written by the run that
auto-fired BEFORE my staging step landed — i.e. they contain
src/proxysql + test/ + plugin.so but NOT
test/tap/tap/_runtime_libs/. With `if: cache-hit != 'true'` guarding
the build step, a hit on the legacy key skips the build (and
therefore the mkdir/cp staging) entirely. The e2e-tests step then
exports LD_LIBRARY_PATH=…/test/tap/tap/_runtime_libs which doesn't
exist on the runner, and test_mysqlx_e2e_handshake-t fails again
with `libre2.so.10: cannot open shared object file` — exactly the
symptom this PR was supposed to fix.

Manual cache-bust: prefix the key with `runtime-libs-v1-` so the
next run is a clean miss, the build step runs end-to-end including
the staging cp, and the new cache lays out _runtime_libs/
alongside the binaries. Bump the suffix again if the staging
layout changes incompatibly.
The Bring up infrastructure step pipes the SQL block to `mysql` via
docker exec -i with stdin redirected from a heredoc. With stdin not
attached to a tty `mysql` switches to a silent default that suppresses
header + row output for non-error queries, so the existing SELECT
COUNT(*) probes left no trace in the CI log — making it impossible to
tell whether the lazy-refresh nudge ran, whether LOAD MYSQLX TO RUNTIME
returned errors, or whether the install_*_from_admin path saw zero
source rows.

Two surgical changes:

* `mysql --table` forces boxed output regardless of stdin shape, so
  SELECT results land in the runner log.
* Add a final UNION SELECT that counts rows in
  runtime_mysqlx_users / runtime_mysqlx_routes / runtime_mysqlx_
  backend_endpoints after the LOAD statements run. Zero in any of
  those columns localises the bind failure to install_*_from_admin
  having seen empty source rows.
1. env.sh racing $GITHUB_ENV — MYSQLX_E2E_PORT was overwritten
   The Verify X Protocol step writes the dynamically-discovered
   sandbox X port (e.g. 18408 for MySQL 8.4.8) into $GITHUB_ENV
   so the test step sees the right port. The test step then runs
   `source test/tap/groups/mysqlx-e2e/env.sh` which unconditionally
   `export MYSQLX_E2E_PORT=33060`. After the source the port has
   been clobbered back to the env.sh default and tests connect to
   nothing on 33060. Capture the discovered port BEFORE sourcing,
   restore it after.

   Surfaced in run 27476177152's e2e-tests step:
     not ok 1 - TCP connect to X port 33060 succeeds

2. mysqlx_thread.cpp::add_listener swallows bind/listen errors
   The reconcile path calls add_listener which returns -1 on any
   failure but logs nothing. The soak-tests "Bring up infrastructure"
   step waits 10s for the listener and fails with
     ERROR: mysqlx listener did not bind on port 6603 within 10s
   with zero diagnostic in the proxysql container log. Add
   fprintf(stderr, ...) on socket()/bind()/listen() failures so the
   actual syscall errno makes it into the log on the next run.

3. Cache-key bump to v2 — pre-existing v1 caches are stale
   The plugin .so source changed (new add_listener logging) and
   the previous v1 cache for this SHA was written without the env.sh
   workaround. A v1 cache hit would skip both the new staging path
   and the corrected mysqlx_thread.cpp. Bump to v2; comment block
   updated to record bump reasons.

Diagnostic milestone from 27476177152: the soak post-LOAD probes
showed runtime_mysqlx_users=1, runtime_mysqlx_routes=1,
runtime_mysqlx_backend_endpoints=1 — confirming the SELECT-nudge
fix DOES work and the listener-bind failure is independent of
the lazy-refresh chain. The add_listener errno will identify what
actually fails (port in use, permission denied, or something else).
Run 27479228546 confirmed e2e-tests now passes (env.sh port-override
fix landed). soak-tests still fails on listener bind, but the
add_listener errno log we added in v2 produced ZERO output — meaning
add_listener was never called. The bind isn't failing, it isn't
being attempted.

To localise the bail-out, log on three frames:

1. `load_routes_to_runtime` (mysqlx_admin_schema.cpp) — log the
   COUNT(*) row count from mysqlx_routes WHERE active=1, and
   whether the weak `mysqlx_reconcile_listeners` symbol resolved
   to the strong def or to null.

2. `mysqlx_reconcile_listeners_impl` entry — log threads.size()
   and route_to_thread.size(). The function returns early when
   threads is empty.

3. Per-route loop in step 1 of reconcile_listeners_impl — log the
   raw bind spec from the store, the parsed host and port for each
   desired route.

Cache key bumped to v3 because plugin .so changed.
Re-run of soak-tests for run 27482049216 finally captured the
diagnostic trace:

  mysqlx: load_routes_to_runtime: mysqlx_routes active row_count=1,
      reconcile_listeners=resolved
  mysqlx: reconcile_listeners entered: threads=4 route_to_thread=0
  mysqlx: reconcile_listeners: store has 1 active routes
  mysqlx: reconcile_listeners: desired route name='r1' host='0.0.0.0'
      port=6603 (raw bind='0.0.0.0:6603')

So:
  - LOAD MYSQLX ROUTES TO RUNTIME runs ✓
  - weak symbol resolves ✓
  - reconcile sees 4 threads and 0 existing listeners ✓
  - store has the route, parsed correctly to 0.0.0.0:6603 ✓

…and then nothing. No add_listener line, success or failure. Step 3
of reconcile must be bailing without calling add_listener at all,
OR add_listener succeeds (its success path was previously silent).

Disambiguate with three new fprintfs:

* `step3: pool=N desired_count=M route_to_thread_count=K` at the
  top of the add loop — confirms we reach step 3 with a non-zero
  desired set.
* `step3: route '...' -> tidx=N threads[tidx]=<non-null|NULL>`
  per iteration — surfaces the bail-out if threads[tidx] is null
  or if route_to_thread already had the route.
* `step3: add_listener rc=N` after the call so a successful bind
  also leaves a trace.
* `add_listener entered: …` AND `add_listener(…): bind+listen OK,
  fd=N` at the top and end of the add_listener function so we know
  whether it ran at all and whether bind/listen completed.

Cache key bumped to v4.
Run 27484657649's v4 diagnostics in the plugin captured the full
reconcile chain end-to-end:

  mysqlx: load_routes_to_runtime: ... row_count=1, reconcile_listeners=resolved
  mysqlx: reconcile_listeners entered: threads=4 route_to_thread=0
  mysqlx: reconcile_listeners: store has 1 active routes
  mysqlx: reconcile_listeners: desired route name='r1' host='0.0.0.0' port=6603
  mysqlx: reconcile_listeners step3: pool=4 desired_count=1 route_to_thread_count=0
  mysqlx: reconcile_listeners step3: route 'r1' -> tidx=0 threads[tidx]=non-null
  mysqlx: add_listener entered: bind=0.0.0.0 port=6603 route=r1
  mysqlx: add_listener(0.0.0.0:6603, route=r1): bind+listen OK, fd=90
  mysqlx: reconcile_listeners step3: add_listener rc=0

So the listener IS up. The wait-loop probe is wrong. It runs
`docker exec ... sh -c "exec 3<>/dev/tcp/127.0.0.1/PORT"` — but
`/dev/tcp/<host>/<port>` is a bash builtin, and the proxysql container's
/bin/sh is dash (Ubuntu default), which silently fails the
redirection regardless of whether the listener is up. The loop
times out every time and the step reports
"mysqlx listener did not bind" even when the bind succeeded
seconds earlier.

Swap `sh -c` for `bash -c`. No other change.
…warning

The plugin had 22 fprintf(stderr,...) calls — 11 from this PR's
listener-reconcile diagnostic dig, 11 pre-existing for backend NOTICE
handling, TLS handshake failures, and missing-services. All are now
routed through ProxySQL's standard logger via proxy_info /
proxy_error / proxy_warning macros from include/proxysql_debug.h.

Why this is even possible: the plugin .so links proxy_error_func as
an undefined symbol; the main `src/proxysql` binary is built with
-Wl,--export-dynamic (lib/Makefile line 133), so the symbol resolves
at dlopen time from the host binary. Top-level `make debug` already
passes -DDEBUG into the plugin (Makefile line 288); that's the
codepath CI uses, so the DEBUG-format prefix
(file:line:func(): [INFO]) lights up in CI logs.

Severity mapping:

* proxy_error — auth failures (empty/malformed/unknown-type NOTICE
  during backend auth, malformed NOTICE during data plane and
  SESS_RESET), socket/bind/listen failures in add_listener, threads
  empty bailing in reconcile_listeners, TLS handshake failures,
  missing services in admin schema registration, add_listener rc!=0
  and null-thread cases in the per-route reconcile loop.

* proxy_warning — backend-emitted WARNING and
  GROUP_REPLICATION_STATE_CHANGED NOTICE frames during auth (drained
  but operationally interesting).

* proxy_info — reconcile_listeners entry and active-route count,
  per-route desired-bind log, add_listener entry and bind+listen OK,
  load_routes_to_runtime row_count and weak-symbol resolution. These
  fire once per LOAD MYSQLX ROUTES TO RUNTIME and once per chassis
  start — low volume.

Also stripped the temporary verbose step3 traces in
mysqlx_reconcile_listeners_impl that helped narrow the soak
listener-bind diagnostic; the remaining INFO logs cover the same
information without the per-iteration noise.

Validated locally end-to-end:
  - ./test/infra/control/destroy-infras.bash + ensure-infras.bash
    with TAP_GROUP=mysqlx-soak brings up dbdeployer-mysql84 (3 nodes)
    and proxysql with the new .so.
  - docker logs proxysql.dev-soak-rene shows entries like:
      2026-06-14 02:41:48 .../mysqlx_thread.cpp:377:add_listener():
      [INFO] mysqlx: add_listener(0.0.0.0:6603, route=r1): bind+listen
      OK, fd=96
    confirming proxy_info routes through the proxysql logger.
  - setup-infras.bash listener probe reports
      >>> mysqlx listener is up on port 6603

Cache key bumped to v5 since the plugin .so changed.
Five blockers surfaced once the bash/sh listener-wait probe stopped
lying:

1. test_mysqlx_route_drop_inflight-t.cpp — handshake protocol bugs
   identical to the pre-PR-#5823 state of test_mysqlx_e2e_handshake-t:
   reads an unsolicited Capabilities frame that never arrives, sends
   `CapabilitiesSet(authentication.mechanisms=MYSQL41)` which is read-
   only (X Plugin returns ER_X_CAPABILITIES_PREPARE_FAILED 5001),
   hex-decodes the raw 20-byte challenge, and uses the wrong
   AuthenticateContinue payload shape. Apply the same fix pattern from
   PR #5823: drop the unsolicited read, drop the CapabilitiesSet step,
   treat AuthenticateContinue.auth_data as raw bytes, send
   `\0schema\0user` on AuthStart and `*hex_scramble` on AuthContinue
   (ProxySQL plugin format).

2. test/infra/docker-base/Dockerfile — pip3 install pulled
   mysql-connector-python==9.7.0 by default, which dropped the X
   DevAPI `mysqlx` module (Oracle split it into a separate package
   from v9.x). The behavioral and stress harnesses then fail at
   `import mysqlx`. Add mysqlx-connector-python alongside.

3. test/tap/tests/test_mysqlx_soak_{behavioral,stress}-t.sh — two
   wrapper-script bugs that masked their own failure:

     - PROXYSQL_PATH walked up from $PWD which is /var/lib/proxysql
       inside the test-runner container; the harness lookup would
       always fall through to "." and python3 would silently report
       "No such file or directory". Walk up from $(dirname "$0")
       instead — the script's own location is mounted from the host
       worktree so its parent chain reaches src/proxysql_global.cpp.

     - The wrappers ran `python3 ${HARNESS} ... 2>&1 | sed '...'`
       without `set -o pipefail`, so a failing python3 left the
       pipeline exiting 0 (the sed exit) and the wrapper printed
       `ok 1` regardless. Add pipefail. Both tests previously
       reported false-PASS on every run.

4. test/scripts/mysqlx/behavioral_validation.py — the SIGTERM
   scenario calls `find_proxysql_pid()` which does `pidof proxysql`
   locally. In the docker-isolated harness ProxySQL runs in a
   separate container; the test-runner container has no
   `proxysql` process to find, so pidof exits 1 and the whole
   harness crashes before observing any client behaviour. Add a
   `--external-kill` flag: with it set, the harness skips the
   local pidof + os.kill path and just lets steady-traffic threads
   accumulate observations of the disconnect delivered by the
   wrapper's `docker kill -s TERM` in the background. Wire the
   behavioral .sh wrapper to pass --external-kill.

5. test/tap/groups/mysqlx-soak/setup-infras.bash — the `alice`
   fixture user was only created in ProxySQL's mysql_users +
   mysqlx_users. With backend_auth_mode='mapped' the mysqlx plugin
   then tries to authenticate alice on the BACKEND MySQL 8.4 X
   listener (port 33060), and the backend rejects with
   "Access denied for user 'alice'". Every forwarded session
   fails — surfaced as 100% error rate in the stress harness and
   "pre-drop SELECT 1 succeeded on 0/N sessions" in
   route_drop_inflight. Provision alice on each dbdeployer-mysql84
   sandbox (master + 2 replicas) with mysql_native_password and
   GRANT ALL.

Local end-to-end validation:
  - destroy-infras.bash + ensure-infras.bash + run-tests-isolated.bash
    against the rebuilt proxysql-ci-base image.
  - alice now authenticates on the backend
    (`docker exec proxysql.dev-soak-rene mysql -h <backend> -u alice
     -palicepass -e 'SELECT 1'` returns 1).
  - Stage 1 of test_mysqlx_route_drop_inflight-t now opens 5/5
    sessions (was 0/5).
  - Stages 3-6 (admin drop + reload, listener teardown, TCP
    refusal, restore) all pass.

REMAINING: Stage 2 (`pre-drop SELECT 1 succeeded on 0/5 sessions`)
still fails. Same root cause as PR #5823's deferred follow-up note:
mysqlx_session.cpp::forward_to_backend never drives server_ds() to
XDS_READY for SQL frames, so the SELECT enqueued from the client side
never reaches the backend or its response never returns. Affects both
the C++ TAP test and Python mysqlx-connector-python clients
identically. Out of scope for this PR (#5852) which is bounded to
CI infrastructure + test-harness fixes; tracked alongside the
deferred routing test from PR #5823.
…ed pwd)

Three independent bugs, each of which on its own broke every off-the-shelf
X-Protocol client against ProxySQL's mysqlx plugin. All three surfaced
once the soak harness started running mysql-connector-python instead of
the in-tree TAP test's bespoke wire emitter; the TAP test happened to
sidestep them by sending the legacy ProxySQL shapes.

1. handler_auth_challenge_response only accepted MYSQL41 AuthContinue in
   the legacy bare `*hex_scramble` shape — the form our TAP test emits.
   Real X-Protocol clients send `<authzid>\0<authcid>\0<response>` where
   the response is either 20 raw bytes (the upstream MySQL X plugin's
   canonical shape) or `*<40 hex>`, optionally NUL-padded. Pre-fix, every
   stock client got 1045 with a valid scramble. Now parses all three
   shapes; also harvests authcid from the AuthContinue prefix when the
   AuthStart leg left username_ empty (stock clients put no credentials
   at all in AuthStart for MYSQL41).

2. MysqlxConnection::start_connect rejected anything that wasn't an IPv4
   dotted-quad — the prior code's "hostnames are not supported here"
   stance. mysqlx_backend_endpoints.hostname accepts arbitrary strings
   and operators routinely populate it with DNS names (docker-compose
   service names, k8s services, etc.), so the session emitted 2003 for
   every first query. Adds a getaddrinfo fallback after the inet_pton
   fast path so already-resolved dotted-quads still skip DNS while names
   resolve cleanly.

3. handler_connecting_server unconditionally used
   identity_->backend_password when setting backend creds, but the
   mapped-mode contract is the symmetric counterpart of the existing
   backend_username pick: service_account rows carry both fields
   explicitly, mapped rows leave both empty and reuse the frontend
   credentials. Without the fallback, mapped sessions sent an empty
   password to the backend X plugin and backend auth always failed 1045
   even when frontend auth succeeded.

env.sh: the dbdeployer X-protocol port convention is classic+10000 not
classic+20000, so the prior MYSQLX_BACKEND_X_PORT=23306 pointed every
backend connection at a closed port. Verified via the sandbox's
my.sandbox.cnf (mysqlx-port=13306).
Without an explicit ref: on actions/checkout under a workflow_run
trigger, GitHub defaults to the workflow file's branch — v3.0 — even
though workflow_run.head_sha points at the PR commit that fired
CI-trigger. Net effect: every workflow_run-driven CI-mysqlx run since
the workflow landed has built and tested v3.0 mainline while the
run-name, cache key, and check display name all advertised the PR's
head_sha. PR authors saw stable red across iterations because their
fixes were never compiled. Adds the explicit ref to all four checkout
steps (build, unit-tests, e2e-tests, soak-tests) so the four jobs run
against the same SHA the rest of the workflow already names.

Bumps the cache prefix to v6 because the v5 entries are wrong-source:
keyed by PR SHA but containing v3.0 binaries. Without the bump the
corrected workflow would cache-hit those stale entries and continue
testing v3.0 indefinitely.
renecannao added a commit that referenced this pull request Jun 15, 2026
CI-mysqlx on v3.0 was the only test workflow that defined its jobs
inline instead of delegating to a reusable on this branch. Under
workflow_run, GitHub does not auto-decorate the PR with check_runs from
the calling workflow's own jobs — only the explicit
LouisBrunner/checks-action POST inside the dedicated-reusable pattern
does that. Net effect: every CI-mysqlx run since the workflow landed
was invisible on the PR status rollup, even on failure, so PR authors
saw "all green" while soak-tests and e2e-tests were red.

This file is the mirror of CI-mysqlx.yml@v3.0 in the same shape every
other CI test workflow follows (ci-legacy-g4.yml is the canonical
template): workflow_call interface, SHA derived from the trigger JSON,
permissions: write-all on each job, in_progress check posted at job
entry, conclusion check posted at job exit. The four job bodies (build,
unit-tests, e2e-tests, soak-tests) are migrated verbatim with their
existing comments preserved; the only structural changes are:

  - cache key prefix bumped v6 -> v7 so the new reusable's runs don't
    inherit v6 entries that were written by the old inline workflow
  - ref/cache key/checkout all keyed off `env.SHA` (derived once at the
    workflow level) instead of the long inline expression
  - the soak-tests cache restore's fail-on-cache-miss gate moves from
    `github.event_name == 'workflow_run'` to `inputs.trigger != ''`
    since under workflow_call the event itself is no longer workflow_run

This file is half of the pair; the matching thin caller lands on v3.0
in PR #5852.
…er CI-*)

CI-mysqlx was the only test workflow on v3.0 that defined its jobs
inline instead of delegating to a dedicated reusable on the GH-Actions
branch. Under workflow_run, GitHub only attaches check_runs to the
originating PR when the running workflow explicitly POSTs them via
LouisBrunner/checks-action — which every other CI-* does via its callee
on GH-Actions, but the inline CI-mysqlx never did. Net effect: every
CI-mysqlx run since the workflow landed was invisible on the PR status
rollup. This PR's own iteration history is the proof: CI-mysqlx kept
failing on b8a710c and d8c9448 with red soak/e2e jobs while the PR
view showed 60 green checks and no mysqlx checks at all.

Replaces the 554-line inline workflow with the 20-line caller that
every other test workflow uses (see CI-legacy-g4.yml on v3.0 for the
canonical shape, doc/GH-Actions/README.md § "The two-branch
architecture" for the rationale). The matching ci-mysqlx.yml callee
landed on GH-Actions in commit a0e783e with the four job bodies
migrated verbatim and the LouisBrunner check_run posts added at job
entry and exit.

Net effect on this PR: CI-mysqlx's build/unit-tests/e2e-tests/soak-tests
will now show up as four checks on the PR commit, with conclusions, so
authors can see at a glance whether the workflow is actually green.
The unit-test Makefile's PROXYSQL40-gated block adds 14 genai_*_unit-t
binaries to UNIT_TESTS. Each one statically links 30+ plugins/genai/
src/*.cpp files at debug -O0 -ggdb, landing at ~160 MB compiled (~2 GB
for the full set). Combined with the rest of the chassis build, this
blows past the ~14 GB free disk on the GitHub-hosted ubuntu-22.04
runner that ci-builds.yml@GH-Actions uses for the new -tap-mysqlx
matrix variant.

Add an opt-in env var SKIP_GENAI_UNIT_TESTS that cuts the genai_*_unit-t
block from the unit-test build without touching the mysqlx_/plugin_/
test_mysqlx_ ones (which CI-mysqlx does run). The knob flows from
ci-builds.yml's matrix-conditional sed-into-docker-compose -> the
_build service's env passthrough list -> entrypoint.bash's EXTRA make
args -> the unit Makefile's ifneq guard.

Leaving the var unset on every other CI variant (everywhere except
-tap-mysqlx in CI-builds, plus local PROXYSQL40 dev builds where the
genai tests are wanted) keeps the full unit-test suite buildable when
CI-unittests is re-enabled.

This is the third refactor of how CI-mysqlx gets its artefacts (after
the workflow_run check_run plumbing and the dedicated-reusable
pattern). All three commits together get CI-mysqlx onto the same
docker-compose build path every other CI workflow uses.
…ll path

Replaces -lprotobuf with $(PROTOBUF_LIB) -- the same approach
plugins/mysqlx/Makefile already uses -- in all 11 link rules of
test/tap/tests/unit/Makefile.

Root cause of the regression caught by CI-builds run 27585217607:

  * The vendored protobuf 3.21.12 is built static-only
    (protobuf_BUILD_SHARED_LIBS=OFF in deps/Makefile).
  * The deb-compliant entrypoint installs libprotobuf-dev (system
    v3.12) when PROXYSQL40=1. With both present in the link search
    path, `-Bdynamic -lprotobuf` resolves to the system .so first
    (.so > .a in ld's default search order) and never reaches the
    vendored .a.
  * The proto-generated .pb.o references symbols only in v3.21.12
    (ZeroFieldsBase, AddDescriptorsRunner, ArenaStringPtr::Destroy,
    ...), so the link fails with thousands of undefined references
    against the system v3.12.

Previously this worked only on hosts where libprotobuf-dev was NOT
installed system-wide (the fallback path inside ld: no .so found ->
use .a). That covers most developer machines but not the CI-builds
container, which apt-installs libprotobuf-dev unconditionally for
PROXYSQL40 builds via the existing pkg-config sanity branch in
entrypoint.bash.

Local dev not running ci-builds keeps working the same way; CI now
builds the unit tests successfully under -tap-mysqlx.
…as unit)

Run 27587366723 of CI-builds got past the unit-test linker errors fixed
in f65323b but stopped on the same protobuf 3.12 vs 3.21 mismatch at
the next layer up:

  make[2]: *** [Makefile:401: test_mysqlx_e2e_routing-t] Error 1
  ZeroFieldsBase, AddDescriptorsRunner, ArenaStringPtr::Set, ...
  /opt/proxysql/deps/protobuf/protobuf-3.21.12/install/include/.../arenastring.h:409: undefined reference

Same root cause: test_mysqlx_e2e_handshake-t,
test_mysqlx_e2e_routing-t, and test_mysqlx_route_drop_inflight-t in
test/tap/tests/Makefile linked with -lprotobuf, which ld resolves
against the system libprotobuf.so v3.12 that the deb-compliant
entrypoint apt-installs for PROXYSQL40 builds. Switch all three rules
to $(PROTOBUF_LIB) (full path to the vendored 3.21.12 static archive),
mirroring what plugins/mysqlx/Makefile and test/tap/tests/unit/Makefile
already do.
Run 27588618191 wrote a _src cache without libpq.so.5 + libre2.so.10,
which broke the e2e-tests job in CI-mysqlx 27589710419. Those .so
paths were added to ci-builds.yml@GH-Actions in commit 6b53c94, but
re-dispatching CI-builds for the SAME SHA hits the existing cache via
the lookup-only check and skips the build entirely. An empty commit
bumps the SHA so CI-builds writes a fresh _src cache including the
two .so files.
The previous _src cache for 2abbd6b had single-file paths for
libpq.so.5 + libre2.so.10 which broke CI-mysqlx restore (missing
symlink target, missing parent directories). Fixed in commit
8f52a91 on GH-Actions by switching to parent directory paths.
Re-dispatching for the same SHA would hit the broken cache via
cache-check; bump the SHA to write fresh.
The ci-builds.yml _src cache path was reverted to the original
2-path list in commit 54aeba8 on GH-Actions (it had been extended
to include plugins/*.so which changed the cache version hash and
broke ~40 downstream workflows). Bump SHA to force fresh CI-builds
that saves under the correct version.
@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
2 Security Hotspots
C Security Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

@renecannao renecannao merged commit e069bfc into v3.0 Jul 5, 2026
65 of 68 checks passed
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.

1 participant