8000 Evaluate TiDB as a MySQL database in tests by ahus1 · Pull Request #54725 · keycloak/keycloak · GitHub
[go: up one dir, main page]

Skip to content
8000

Evaluate TiDB as a MySQL database in tests - #54725

Draft
ahus1 wants to merge 6 commits into
keycloak:mainfrom
ahus1:is-54724-spike-tidb
Draft

ahus1 wants to merge 6 commits into
keycloak:mainfrom
ahus1:is-54724-spike-tidb

Conversation

@ahus1
@ahus1 ahus1 commented Nov 5, 2026 •
Copy link
Copy Markdown
Member

Closes #54724

Spike to find out if Keycloak works with TiDB when it is treated as a plain MySQL database (vendor mysql, MySQL dialect and driver). The tests run against TiDB v8.5.8 via KC_TEST_DATABASE=tidb, and the CI job runs nightly, manually, or when the commit message contains [tidb ci].

Finding: TiDB accepts FOR UPDATE SKIP LOCKED but silently ignores the SKIP LOCKED part (pingcap/tidb#18207). A row locked by another transaction is returned instead of being skipped. JpaUserSessionPersisterProvider.lockUserSession and lockClientSession use it to check that the row is locked by the current transaction, so on TiDB they reported rows as locked that were not. The versioned update then matched 0 rows, which failed ConcurrentLoginTest with OptimisticLockException and an HTTP 500 "Database operation failed".

Quick fix in this PR: JpaUtils.isSkipLockedSupported detects TiDB via the JDBC product version (8.0.11-TiDB-v8.5.8), as the dialect and product name are MySQL. On TiDB, both lock methods use FOR UPDATE NOWAIT instead of SKIP LOCKED. If the row is locked by another transaction, TiDB fails immediately (error 3572), and the persister treats this as "not locked", so the existing retry path in a separate transaction applies. Plain FOR UPDATE was tried first, but it deadlocked in ConcurrentLoginTest.concurrentLoginSingleUserSingleClient on CI when requests locked rows in a different order. With NOWAIT, StatelessTestSuite passes on TiDB v8.5.8 (150 tests, 0 failures). This relies on how Hibernate handles the failed lock: with a lock timeout and the MySQL dialect, it doesn't mark the transaction as rollback-only, while other dialects such as PostgreSQL do. Therefore the fallback is intended for TiDB only and throws an UnsupportedOperationException on other dialects. UserSessionPersisterLockTest (MySQL dialect only) verifies that a failed lock returns false and leaves the transaction usable, and will fail if a Hibernate upgrade changes this.

Other places using SKIP LOCKED (event store expiry, outbox drain and poll) were reviewed but not changed. On TiDB they should only wait for locks instead of skipping rows, and not produce wrong results.

[tidb ci]

Signed-off-by: Alexander Schwartz <alexander.schwartz@ibm.com>
@ahus1 ahus1 self-assigned this Nov 5, 2026
ahus1 and others added 3 commits November 5, 2026 23:10
Stack traces were forwarded line by line at INFO level and dropped by the
test log filter, leaving only the first line of an error.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Alexander Schwartz <alexander.schwartz@ibm.com>
Realm logout-all sends a backchannel logout to every client with an adminUrl,
which logged a connection refused warning for localhost:8180.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Alexander Schwartz <alexander.schwartz@ibm.com>
TiDB accepts FOR UPDATE SKIP LOCKED but ignores the SKIP LOCKED part, so the
session lock check reported rows as locked that were not, which led to
optimistic lock failures under concurrent logins.
On TiDB, use a plain FOR UPDATE that waits for the lock, the version comparison
afterward detects a changed row.

See pingcap/tidb#18207

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Alexander Schwartz <alexander.schwartz@ibm.com>
@ahus1 ahus1 changed the title Treat TiDB as a MySQL database Evaluate TiDB as a MySQL database in tests Nov 5, 2026
ahus1 and others added 2 commits November 6, 2026 08:32
TiDB ignores SKIP LOCKED, and plain FOR UPDATE can deadlock when concurrent
requests lock session rows in different orders. Use NOWAIT instead and treat a
row locked by another transaction as not locked, so the caller retries in a
separate transaction. Add a test that a failed lock leaves the transaction usable.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Alexander Schwartz <alexander.schwartz@ibm.com>
Explain why NOWAIT works on the MySQL dialect only, as it depends on how
Hibernate marks transactions for rollback after a failed lock. Throw an
exception if the fallback is reached on another dialect, and check the dialect
instead of the product name in the test.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Alexander Schwartz <alexander.schwartz@ibm.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.

Evaluate current compatibility with TiDB

1 participant

0