Skip to content

fix(ontology): retry an edit-lock acquire that loses a MySQL deadlock; de-flake TaskResourceIT and TestCaseResourceIT - #35507

Merged
sonika-shah merged 3 commits into
mainfrom
fix/ontology-lock-deadlock-and-task-it
Oct 15, 2026
Merged

sonika-shah merged 3 commits into
mainfrom
fix/ontology-lock-deadlock-and-task-it

Conversation

@sonika-shah

@sonika-shah sonika-shah commented Oct 15, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Two merge-queue IT flakes, fixed at the root cause:

  • Ontology edit-lock deadlock (product bug, MySQL). OntologyEditLockService.acquire does SELECT … FOR UPDATE and then INSERT when no lease exists, in one transaction. On MySQL a locking read of an absent key takes a gap lock, so two first-time acquires deadlock on their inserts, even for different resources, and one user gets a 500. The fix wraps the transaction in the existing DeadlockRetry (same pattern as ConversationRepository). The rolled-back acquire replays and either succeeds, or finds the winner's row and gets the normal 409.
  • TaskResourceIT.testScopedTaskEndpointsHonorTimeRange (test bug). It asserted paging.total == data.size() over every task visible to the shared USER1, with a window ending in the future. A concurrent test creating a task for USER1 between the count and list queries made them disagree. The listers are now scoped to the test's own domain, which every task endpoint already filters on.
  • TestCaseResourceIT's add-every-test-case tests run alone (test isolation). test_bulkAddAllTestCasesToLogicalTestSuite and test_bulkAddAllTestCasesWithExcludeIds add every test case in the shared DB to a suite, rewriting each one's search doc while the parallel lane runs. That write lands inside other tests' async search updates to their own test cases. TableCertificationPropagationIT's cert cascade is one, and it is skipped on the version conflict (ejected Fixes #35317: check hybrid search with a query, not the stored pipeline #35321). These two methods now hold @ResourceLock(Resources.GLOBAL, READ_WRITE), the same as ContainerResourceIT; the rest of the class stays concurrent. The product-side race (async update-by-query with conflicts=proceed, never retried) is still tracked in the issue.
  • TestCaseResourceIT's list tests are scoped to their own table. testListFluentAPI, testAutoPaginationFluentAPI, test_listTestCasesWithPagination and test_listTestCasesFromSearch listed every test case in the shared DB. Their size() >= N checks passed on other tests' rows, and they could only fail when another test's row broke the list call. They now filter by their own entityLink (as test_listTestCases_200_OK does) and assert their own ids. The pagination test walks every page and checks each id appears once. testFluentList is unchanged: the fluent TestCases.list() has no filter option.

Merge-queue ejections these caused: #33908 (OntologyStructureIT deadlock), #32038 (TaskResourceIT) and #35321 (TableCertificationPropagationIT). Context and the other flakes: #35508 and #35505.

Tests

Run Before fix After fix
New OntologyEditLockResourceIT.concurrentFirstAcquiresOnDifferentResourcesAllSucceed (8 concurrent first acquires on different terms), MySQL + ES RED: 7× Deadlock on OntologyEditLockDAO.insert, 500s GREEN: all 7 tests in the class pass; DeadlockRetry logs show the deadlocks absorbed
Same class, PostgreSQL + OpenSearch — GREEN (7/7)
TaskResourceIT#testScopedTaskEndpointsHonorTimeRange, MySQL + ES and PostgreSQL + OpenSearch — GREEN
TestCaseResourceIT#test_bulkAddAll* + #test_bulkAddTestCasesToLogicalTestSuiteByIds with TableCertificationPropagationIT, PostgreSQL + OpenSearch parallel — GREEN (all pass with the lock)
The 4 re-scoped list tests + testFluentList + the 3 bulk-add tests, PostgreSQL + OpenSearch parallel — GREEN (8/8)
Details
  • Renewing an existing lease isn't affected: FOR UPDATE finds the row and takes a record lock only. Only first acquires collide, and on a mostly-empty table almost any two overlapping ones share a gap.
  • Postgres doesn't take gap locks for FOR UPDATE on a missing row, so this deadlock is MySQL-only. A same-resource race on Postgres still surfaces as a duplicate-key error rather than a 409; that's pre-existing and out of scope here.
  • DeadlockRetry retries only deadlock and lock-wait errors (MySQL 1213/1205, SQLSTATE 40001/40P01), so the policy's 409 for an active foreign lease is never retried.
  • This is the only FOR UPDATE in RdfInfraDAOs and the ontology/ package, so no sibling has the same shape.

OntologyEditLockService.acquire reads the lease row with SELECT ... FOR
UPDATE and inserts it when absent, in one transaction. On MySQL a locking
read of an absent key takes a gap lock, so two first-time acquires deadlock
on their inserts even for different resources, and one returns 500. Wrap the
transaction in DeadlockRetry: the rolled-back acquire replays and succeeds,
or sees the winner's row and gets the usual 409.

Also scope TaskResourceIT's time-range assertions to the test's own domain:
paging.total vs page size over everything visible to the shared USER1 moved
whenever a concurrent test created a task for USER1 between the two queries.
@github-actions

Copy link
Copy Markdown
Contributor

❌ PR checklist incomplete

This PR cannot be merged until the following are addressed on its linked issue:

  • No GitHub issue is linked. Link an issue in the Development section of the PR (or add Fixes #12345 to the description). For a same-org cross-repo issue, add Fixes open-metadata/<repo>#123 to the description.

The fields live on the linked issue in the Shipping project (open the issue → right sidebar → Projects). After you set them, re-run this check (or push a commit) — issue/project changes do not re-trigger it automatically.

Maintainers can bypass this check by adding the skip-pr-checks label.

@github-actions github-actions Bot added backend safe to test Add this label to run secure Github workflows on PRs labels Oct 15, 2026
test_bulkAddAllTestCasesToLogicalTestSuite and
test_bulkAddAllTestCasesWithExcludeIds add every test case in the shared
database to a suite, rewriting each one's search doc while the parallel lane
runs. That write lands inside other tests' async search updates to their own
test cases, e.g. TableCertificationPropagationIT's cert cascade, which is then
skipped on the version conflict (ejected #35321). Hold the global lock for
just these two methods, as ContainerResourceIT already does; the rest of the
class stays concurrent.
@sonika-shah sonika-shah changed the title fix(ontology): retry an edit-lock acquire that loses a MySQL deadlock; de-flake TaskResourceIT time-range test fix(ontology): retry an edit-lock acquire that loses a MySQL deadlock; de-flake TaskResourceIT and TestCaseResourceIT Oct 15, 2026
testListFluentAPI, testAutoPaginationFluentAPI, test_listTestCasesWithPagination
and test_listTestCasesFromSearch listed every test case in the shared database.
Their size() >= N checks passed on other tests' rows, so they never checked
their own, and they could only fail when another test's row broke the list
(e.g. its table hard-deleted mid-call). Filter by the test's own entityLink,
as test_listTestCases_200_OK already does, and assert the created ids come
back; the pagination test now walks every page and checks each id once.
@gitar-bot

gitar-bot Bot commented Oct 15, 2026

Copy link
Copy Markdown
Code Review ✅ Approved

🟡 Medium risk · Retrying deadlocked ontology edit-lock acquisitions could delay requests during contention; remaining changes only scope and isolate integration tests.

Wraps the ontology edit-lock acquire transaction in DeadlockRetry so a first-time acquire that loses a MySQL gap-lock deadlock replays instead of returning a 500, and scopes the TaskResourceIT time-range test listers to the test's own domain to remove a flake. No issues found.

Verification

Rules No rules evaluated

Functional validation Not enabled

Set up

Auto-approval Not enabled

Set up

For more verification details, comment gitar display:verbose.

Options

Display: compact → Showing compact verification results.

Comment with these commands to change the behavior for this request:

Compact
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Powered by Gitar — free for open source

@sonika-shah
sonika-shah added this pull request to the merge queue Oct 15, 2026
Merged via the queue into main with commit 10f4c78 Oct 15, 2026
101 of 103 checks passed
@sonika-shah
sonika-shah deleted the fix/ontology-lock-deadlock-and-task-it branch October 15, 2026 18:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend safe to test Add this label to run secure Github workflows on PRs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants