Skip to content

test(search): make ranking tokens collision-free and stop swallowing poll errors (1.13) - #30251

Merged
mohityadav766 merged 1 commit into
1.13from
fix/search-ranking-token-entropy-1.13
Jul 21, 2026
Merged

test(search): make ranking tokens collision-free and stop swallowing poll errors (1.13)#30251
mohityadav766 merged 1 commit into
1.13from
fix/search-ranking-token-entropy-1.13

Conversation

@mohityadav766

@mohityadav766 mohityadav766 commented Jul 20, 2026

Copy link
Copy Markdown
Member

Describe your changes:

Full fix for the SearchEntityRankingIT ranking failures on 1.13. Keeps both parts: the collision-free token change and the awaitOrReason poll-error surfacing.

Note: on main the token-collision half was superseded by #30120 (tokenFreeValue, a disjoint-alphabet filler), so the main PR #30250 was narrowed to just awaitOrReason. #30120 is not on 1.13, so this branch still needs the token fix — hence it carries the full change, standalone rather than a cherry-pick of #30250.

1.13 is where this actually bites. Main is green today only because its post-#29903 Tier1 boost of 0.5 sits above the noise ceiling; 1.13 ships 0.05, which sits inside it, so the tie case inverts at a measured ~12% rate per entity type. This is the fix for the SearchEntityRankingIT ranking failures on the 1.13 CI runs.


SearchEntityRankingIT's "Tier1 > untagged on a text tie" case seeds two documents with an identical displayName so their text scores are equal, then asserts the Tier.Tier1 global term boost breaks the tie. The tie was never actually exact.

RankingSupport.uniqueTerm() derived its token from TestNamespace.uniqueShortId(), which is RUN_ID.substring(0,8) + methodHash + 4 random chars — only 4 of 16 characters vary within a test method. Every token minted for one test therefore shared a 14-character prefix, and EntitySeeder.nameFor() places a sibling token in each entity's name, so the query ngram-matched both documents' names by differing amounts. That leaked a variable name.ngram / displayName.ngram contribution into scores the case requires to be equal.

The signature is visible in the failing output — three documents in a result set where the case seeded two:

zzorhlgutmhtng|hhok      <- 14-char shared prefix, 4 random chars
zzorhlgutmhtng|ojpl
zzorhlgutmhtng|sukm      <- bleed from an earlier case in the same method

explain=true confirms the text-match components are byte-identical on both documents (displayName.keyword 21.5011, displayName 8.0629) — the entire delta comes from the ngram subqueries.

Why main is green today and 1.13 is not. The noise ratio is bounded around 1.17, because the ngram terms sit in the max plus 0.3 times others bucket while displayName.keyword dominates at boost 26.4. Main's post-#29903 Tier1 boost of 0.5 (1.5x) sits above that ceiling; 1.13 ships 0.05 (1.05x), which sits inside it. The defect is present on main too — just out of range. Measured on a live 1.13.1 server, varying only the boost:

Tier1 boost inversions
0.05 (1.13 ships) 3/25
0.5 (main ships, post-#29903) 0/25

Drawing the token from its own full-entropy source removes the overlap at source rather than relying on a boost margin to outweigh it.

Also: stop swallowing poll errors. awaitTrue reported every timeout as a flat "not indexed in time". ignoreExceptions() is correct while polling a document that is still being indexed, but discarding the exception made a condition that threw on every poll indistinguishable from genuine indexing lag. awaitOrReason() keeps the tolerance and reports the last error alongside the timeout, so real failures identify themselves instead of hiding behind a generic message.

How I tested

  • RankingSupportTest covers both collision properties (hex-free, and no long shared prefix between sibling tokens). Negative control: patched uniqueTerm back to the old scheme and the test fails with share 14 characters — matching the CI signature exactly.
  • End-to-end against a live 1.13.1 server at 1.13's shipped 0.05 boost: 3/25 inversions before the fix, 0/25 after. The fix stands on its own and does not depend on backporting Harden search ranking relevance #29903.

Type of change:

  • Bug fix

Checklist:

  • I have read the CONTRIBUTING document.
  • I have commented on my code, particularly in hard-to-understand areas.

Greptile Summary

This PR removes ranking-token overlap and improves search polling diagnostics. The main changes are:

  • Generate independent, hex-free ranking terms from UUIDs.
  • Update entity seeders to use the new token helper.
  • Preserve the last polling exception in timeout messages.
  • Add tests for token alphabet and shared prefixes.

Confidence Score: 4/5

The token changes look sound, but the new test file can fail repository checks and contains a nondeterministic assertion.

  • Namespace cleanup remains tied to tracked entities rather than search tokens.
  • The UUID mapping removes the shared-prefix and hexadecimal overlap that affected ranking scores.
  • The new source file is missing its required license header.
  • Poll diagnostics retain the wrapper exception but omit its nested cause.

openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/search/RankingSupportTest.java and openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/search/RankingSupport.java

Important Files Changed

Filename Overview
openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/search/RankingSupport.java Adds independent ranking tokens and retains polling exceptions, but the timeout text omits nested cause details.
openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/search/EntitySeeder.java Updates generated field values to use independent tokens while preserving namespace-prefixed entity names.
openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/search/EntityRankingSeeders.java Updates all entity-specific seeders to the revised helper signatures.
openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/search/RankingSupportTest.java Adds token regression tests but lacks the required source header and includes a probabilistic assertion.
openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/search/SearchEntityRankingIT.java Uses independent terms and includes polling timeout reasons in accumulated ranking failures.

Reviews (1): Last reviewed commit: "test(search): make ranking tokens collis..." | Re-trigger Greptile

Greptile also left 3 inline comments on this PR.

Context used:

  • Context used - CLAUDE.md (source)

…poll errors

SearchEntityRankingIT's "Tier1 > untagged on a text tie" case seeds two
documents with an identical displayName so their text scores are equal, then
asserts the Tier.Tier1 global term boost breaks the tie. The tie was not
actually exact.

RankingSupport.uniqueTerm() derived its token from TestNamespace.uniqueShortId(),
which is RUN_ID.substring(0,8) + methodHash + 4 random chars — only 4 of 16
characters vary within a test method. Every token minted for one test therefore
shared a 14-character prefix, and EntitySeeder.nameFor() places a sibling token
in each entity's name, so the query ngram-matched both documents' names by
differing amounts. That leaked a variable name.ngram / displayName.ngram
contribution into scores the case requires to be equal.

Measured against 1.13 (Tier1 boost 0.05, multiplier 1.05x): 3/25 inversions.
The noise ratio is bounded by roughly 1.17 because the ngram terms sit in the
"max plus 0.3 times others" bucket while displayName.keyword dominates, so
main's post-#29903 boost of 0.5 (1.5x) sits above the noise and never inverts
— the defect is present on main too, just out of range. Drawing the token from
its own full-entropy source removes the overlap at source rather than relying
on a boost margin to outweigh it.

Also stop reporting every awaitTrue timeout as "not indexed in time".
ignoreExceptions() is correct while polling a document that is still being
indexed, but discarding the exception made a condition that threw on every
poll indistinguishable from genuine indexing lag. awaitOrReason() keeps the
tolerance and reports the last error alongside the timeout.

RankingSupportTest covers both collision properties; it fails with a 14-char
shared prefix against the previous implementation.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@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 Jul 20, 2026
@gitar-bot

gitar-bot Bot commented Jul 20, 2026

Copy link
Copy Markdown
Code Review ✅ Approved

Eliminates token collision in the search ranking test suite by increasing entropy and improves error visibility in awaitTrue by capturing polling exceptions. No issues found.

Options

Display: compact → Showing less information.

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

Compact
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

@@ -0,0 +1,65 @@
package org.openmetadata.it.tests.search;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Required Source Header Missing

This new Java file has no Apache 2.0 license header. The repository requires that header on every new source file, so the license check can reject the build before these tests run.

Context Used: CLAUDE.md (source)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

for (int i = 0; i < terms.size(); i++) {
for (int j = i + 1; j < terms.size(); j++) {
int shared = sharedPrefixLength(terms.get(i), terms.get(j));
assertTrue(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Random Tokens Can Fail Test

This assertion can fail for valid UUID output when two sampled terms happen to share seven random characters after the fixed zz prefix. The implementation would still be collision-free, but this test would fail nondeterministically; use deterministic token inputs or test a guaranteed structural property instead.

private static String describeTimeout(Exception lastError) {
String reason = "not satisfied within " + INDEX_WAIT;
if (lastError != null) {
reason = reason + "; last error: " + lastError;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Root Poll Error Remains Hidden

lastError is normally the IllegalStateException created by search(), whose message only identifies the index and query. Appending its toString() omits the nested HTTP or deserialization cause, so a persistent 4xx or malformed response still times out without showing the detail needed to distinguish it from indexing lag.

@github-actions

Copy link
Copy Markdown
Contributor

The Python checkstyle failed.

Please run make py_format and py_format_check in the root of your repository and commit the changes to this PR.
You can also use pre-commit to automate the Python code formatting.

You can install the pre-commit hooks with make install_test precommit_install.

@github-actions

Copy link
Copy Markdown
Contributor

🔴 Playwright Results — 2 pipeline/setup failure(s)

✅ 3957 passed · ❌ 0 failed · 🟡 22 flaky · ⏭️ 30 skipped

Pipeline and setup failures

  • Shard 2 reported zero tests.
  • Shard 2 test execution finished with status failure without a reported test failure.
Shard Passed Failed Flaky Skipped
✅ Shard 1 291 0 0 4
✅ Shard 2 0 0 0 0
🟡 Shard 3 734 0 3 7
🟡 Shard 4 778 0 5 2
🟡 Shard 5 748 0 9 9
✅ Shard 6 693 0 0 0
🟡 Shard 7 713 0 5 8
🟡 22 flaky test(s) (passed on retry)
  • Features/BulkEditEntity.spec.ts › Database Schema (shard 3, 2 retries)
  • Features/DomainFilterQueryFilter.spec.ts › Multi-nested domain hierarchy: filters should scope correctly at every level (shard 3, 1 retry)
  • Features/ExploreQuickFilters.spec.ts › tier with assigned asset appears in dropdown, tier without asset does not (shard 3, 1 retry)
  • Features/NestedColumnsExpandCollapse.spec.ts › should not duplicate rows when expanding and collapsing nested columns with same names in Version History (shard 4, 1 retry)
  • Features/Permissions/GlossaryPermissions.spec.ts › Team-based permissions work correctly (shard 4, 1 retry)
  • Features/UserProfileOnlineStatus.spec.ts › Should show "Active recently" for users active within last hour (shard 4, 1 retry)
  • Flow/ObservabilityAlerts.spec.ts › Ingestion Pipeline alert (shard 4, 1 retry)
  • Pages/CustomProperties.spec.ts › Time (shard 4, 1 retry)
  • Pages/CustomProperties.spec.ts › Time Interval (shard 5, 1 retry)
  • Pages/CustomProperties.spec.ts › Time Interval (shard 5, 1 retry)
  • Pages/CustomProperties.spec.ts › Set & Update all CP types on apiCollection (shard 5, 1 retry)
  • Pages/DataContracts.spec.ts › Create Data Contract and validate for Table (shard 5, 1 retry)
  • Pages/DataContracts.spec.ts › Create Data Contract and validate for Api Collection (shard 5, 1 retry)
  • Pages/Domains.spec.ts › Rename domain with deeply nested subdomains (3+ levels) verifies FQN propagation (shard 5, 1 retry)
  • Pages/Domains.spec.ts › Rename domain with assets (tables, topics, dashboards) preserves associations (shard 5, 1 retry)
  • Pages/Domains.spec.ts › Comprehensive domain rename with ALL relationships preserved (shard 5, 1 retry)
  • Pages/Entity.spec.ts › Inactive Announcement create & delete (shard 5, 1 retry)
  • Pages/Lineage/DataAssetLineage.spec.ts › verify create lineage for entity - Mlmodel (shard 7, 1 retry)
  • Pages/Lineage/LineageFilters.spec.ts › Verify lineage schema filter selection (shard 7, 1 retry)
  • Pages/Lineage/LineageRightPanel.spec.ts › Verify custom properties tab IS visible for supported type: searchIndex (shard 7, 1 retry)
  • Pages/TestSuite.spec.ts › Logical TestSuite (shard 7, 1 retry)
  • Pages/Users.spec.ts › Permissions for table details page for Data Consumer (shard 7, 1 retry)

📦 Download artifacts

How to debug locally
# Download playwright-test-results-<shard> artifact and unzip
npx playwright show-trace path/to/trace.zip    # view trace

@mohityadav766
mohityadav766 merged commit 40a806b into 1.13 Jul 21, 2026
50 of 83 checks passed
@mohityadav766
mohityadav766 deleted the fix/search-ranking-token-entropy-1.13 branch July 21, 2026 05:56
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.

2 participants