Skip to content

Fixes 30008: Improve server startup and write-path latency#30009

Open
harshach wants to merge 9 commits into
mainfrom
harshach/perf-analysis-startup-latency
Open

Fixes 30008: Improve server startup and write-path latency#30009
harshach wants to merge 9 commits into
mainfrom
harshach/perf-analysis-startup-latency

Conversation

@harshach

@harshach harshach commented Jul 14, 2026

Copy link
Copy Markdown
Collaborator

Describe your changes:

Fixes #30008

I improved server startup, request/write latency, and lineage query performance because warm boots repeated bundled-data and search reconciliation, writes repeated JSON serialization, and the pipeline-summary service type was interpolated into SQL.

Type of change:

  • Improvement

High-level design:

  • Startup observability: ordered step timings, slow-resource attribution, standard request-latency buckets, and opt-in percentile histograms
  • Warm-start path: SHA-256 gates for bundled seed data and search templates, an indexed hard-row presence check for every required bundled seed entity, failure-safe retries, bounded parallel index initialization, and database-backed type-registry hydration
  • Classpath/auth path: one shared ClassGraph scan, one cached classpath resource index, and bounded RBAC SpEL memoization
  • Write path: direct Jakarta JSON-P conversion, replayable TokenBuffer retry snapshots, storage-JSON reuse for cache write-through, and lifecycle snapshot gating
  • Database path: bound pipeline-summary filters plus MySQL generated-column and PostgreSQL expression indexes for pipeline lineage
  • Rollout: startup gates default on with force/disable controls; fingerprint persistence uses the existing settings table; lineage DDL is supplied for both databases
  • Alternatives: duplicate MySQL pipeline-name DDL was rejected because migration 1.2.0 already creates it; Flowable/Quartz deferral remains evidence-gated behind the new timing data

Tests:

Use cases covered

  • First, unchanged, changed, failed, and forced seed/template fingerprint decisions
  • Matching seed fingerprints with hard-missing required rows rerun seed loaders; matching search checksums verify live template markers; probe failures fail open and intentionally deletable seeds remain optional
  • Parallel index creation and template acknowledgement/failure handling
  • PATCH conversion equivalence, replayable retry state, cache JSON reuse, and lifecycle snapshot suppression
  • Pipeline-summary filtering/pagination and quote-containing service-type input
  • Pipeline-reference lineage relation precedence and source-lineage deletion
  • Forced-deadlock update replay without double version bumps or consolidation corruption

Unit tests

  • I added unit tests for the new/changed logic.
  • Files added/updated: CommonUtilTest, StartupTimerTest, SeedDataGateTest, ClasspathScanIndexTest, CollectionRegistryTest, RBACConditionEvaluatorCacheTest, IndexTemplateManagerTest, EntityRepositoryStorageJsonReuseTest, EntityLifecycleEventDispatcherTest, RequestLatencyContextTest, MicrometerBundleTest, SettingsRowMapperTest, and JsonUtilsTest
  • Final review-focused result: SeedDataGateTest passed 8/8 and IndexTemplateManagerTest passed 10/10
  • Coverage %: not measured

Backend integration tests

  • I added integration tests in openmetadata-integration-tests/ for new/changed backend behavior.
  • Files added/updated: PipelineResourceIT.java, LineageResourceIT.java, SeedDataPresenceIT.java, and IndexTemplateIT.java
  • MySQL 8.3 + Elasticsearch 9.3: 3 pipeline/lineage tests passed
  • PostgreSQL 15 + OpenSearch 3.4: 3 pipeline/lineage tests passed
  • MySQL 8.3 + Elasticsearch 9.3: 2 forced-deadlock replay tests passed
  • The seed-presence and search-template integration sources compile; local container execution was blocked before test discovery because exhausted Docker VM storage prevented Testcontainers from creating a container

Ingestion integration tests

  • Not applicable (no ingestion changes).

Playwright (UI) tests

  • Not applicable (no UI changes).

Manual testing performed

  1. Ran clean specification generation/install and compiled service plus integration-test sources
  2. Ran MySQL and PostgreSQL migration smoke checks
  3. Ran mvn spotless:apply, mvn spotless:check, schema validation, and git diff --check

UI screen recording / screenshots:

Not applicable.

Checklist:

  • I have read the CONTRIBUTING document.
  • My PR title is Fixes <issue-number>: <short explanation>
  • My PR is linked to a GitHub issue via Fixes #<issue-number> above.
  • I have commented on my code, particularly in hard-to-understand areas.
  • For JSON Schema changes: I updated the migration scripts or explained why it is not needed.
  • For UI changes: not applicable; this PR has no UI changes.
  • I have added tests (unit / integration / Playwright as applicable) and listed them above.
  • I have added tests around the new logic.

Greptile Summary

This PR improves startup and write-path performance across the backend. The main changes are:

  • Startup checksum gates for bundled seed data and search templates.
  • Required seed-row presence checks before warm-start seed skips.
  • Live search-template fingerprint checks before warm-start template skips.
  • Shared classpath scanning and cached resource discovery.
  • JSON/write-path reuse and lifecycle snapshot gating.
  • Pipeline summary SQL binding and lineage index migrations.

Confidence Score: 5/5

This looks safe to merge.

  • No blocking issues found in the changed code.

Important Files Changed

Filename Overview
openmetadata-service/src/main/java/org/openmetadata/service/seeding/SeedDataGate.java Adds startup checksum handling, seed fingerprinting, and required-row drift detection.
openmetadata-service/src/main/java/org/openmetadata/service/seeding/RequiredSeedRows.java Adds the seed-row identity model used by startup presence checks.
openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/CollectionDAO.java Adds required seed-row counting SQL and updates pipeline lineage and summary queries.
openmetadata-service/src/main/java/org/openmetadata/service/search/SearchRepository.java Adds gated search-template updates with live fingerprint verification.
openmetadata-service/src/main/java/org/openmetadata/service/search/GenericClient.java Defines shared index-template fingerprint metadata and helpers.
openmetadata-service/src/main/java/org/openmetadata/service/search/elasticsearch/ElasticSearchGenericManager.java Writes and reads Elasticsearch template fingerprint markers.
openmetadata-service/src/main/java/org/openmetadata/service/search/opensearch/OpenSearchGenericManager.java Writes and reads OpenSearch template fingerprint markers.

Comments Outside Diff (1)

  1. openmetadata-service/src/main/java/org/openmetadata/service/seeding/SeedDataGate.java, line 3187-3191 (link)

    P1 Seed rows still skip This drift check only verifies types, non-deletable policies, and non-deletable roles. The startup fingerprint covers all bundled json/data/* seed files, and many seed loaders now return before their per-row insert checks when shouldSeed() is false. If STARTUP_CHECKSUMS survives a restore while seeded rows such as bots, docstore rows, task form schemas, test definitions, tags, workflow definitions, or notification templates are missing, this check can still mark the database clean and skip the loaders. Those defaults remain absent until an operator forces seeding.

Reviews (8): Last reviewed commit: "fix: validate warm-start state against l..." | Re-trigger Greptile

Context used (3)

  • Context used - CLAUDE.md (source)
  • Context used - openmetadata-ui-core-components/CLAUDE.md (source)
  • Context used - AGENTS.md (source)

@github-actions github-actions Bot added backend safe to test Add this label to run secure Github workflows on PRs labels Jul 14, 2026
Comment thread common/src/main/java/org/openmetadata/common/utils/CommonUtil.java
@harshach
harshach marked this pull request as ready for review July 14, 2026 04:22
Copilot AI review requested due to automatic review settings July 14, 2026 04:22
@github-actions

Copy link
Copy Markdown
Contributor

❌ PR checklist incomplete

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

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.

Copilot AI left a comment

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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@github-actions

Copy link
Copy Markdown
Contributor

✅ TypeScript Types Auto-Updated

The generated TypeScript types have been automatically updated based on JSON schema changes in this PR.

Copilot AI review requested due to automatic review settings July 14, 2026 04:26
@github-actions
github-actions Bot requested a review from a team as a code owner July 14, 2026 04:26
@harshach
harshach removed the request for review from Copilot July 14, 2026 04:26
Copilot AI review requested due to automatic review settings July 14, 2026 04:40

Copilot AI left a comment

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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@github-actions

github-actions Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Jest test Coverage

UI tests summary

Lines Statements Branches Functions
Coverage: 65%
65.1% (75639/116178) 48.88% (45067/92187) 49.7% (13639/27442)

Copilot AI review requested due to automatic review settings July 18, 2026 00:30

Copilot AI left a comment

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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@gitar-bot

gitar-bot Bot commented Jul 18, 2026

Copy link
Copy Markdown
Code Review ✅ Approved 4 resolved / 4 findings

Improves server startup and write-path performance through checksum-based seed gating, classpath caching, and optimized JSON serialization. All identified findings regarding seed-data and search-template reconciliation logic have been resolved.

✅ 4 resolved
Bug: CommonUtil.getResources now returns an immutable list

📄 common/src/main/java/org/openmetadata/common/utils/CommonUtil.java:63-77
getResources() now returns stream()...toList() over an immutable index (buildClasspathResourceIndex uses List.copyOf), whereas it previously returned a mutable ArrayList. Any caller that mutates the returned list (add/remove/sort) will now throw UnsupportedOperationException at runtime. The new CommonUtilTest even asserts this immutability. Please confirm no downstream caller of getResources()/getJsonDataResources() mutates the result; if any does, wrap it in a new ArrayList or fix the caller.

Performance: Search-template gate never stamps fingerprint if any mapping is missing

📄 openmetadata-service/src/main/java/org/openmetadata/service/search/SearchRepository.java:582 📄 openmetadata-service/src/main/java/org/openmetadata/service/search/SearchRepository.java:595-599 📄 openmetadata-service/src/main/java/org/openmetadata/service/search/SearchRepository.java:607-621
In createOrUpdateIndexTemplates, failed is initialized to entityIndexMap.size() - templates.size(). buildIndexTemplateDefinitions() silently drops any entity whose mapping content is null or fails to read, so templates.size() can be permanently smaller than entityIndexMap.size(). That makes failed > 0 on every boot, which triggers recordSearchTemplateFailure() (resetting the stored fingerprint to null). As a result the search-template gate never records a clean fingerprint and templates are rebuilt on every startup, defeating the warm-start optimization this PR adds. Consider only counting genuine per-template failures (increment inside the catch block) rather than pre-charging failed with the count of skipped/no-mapping entities.

Quality: ClasspathScanIndex keeps ScanResult open for JVM lifetime

📄 openmetadata-service/src/main/java/org/openmetadata/service/util/ClasspathScanIndex.java:32-39 📄 openmetadata-service/src/main/java/org/openmetadata/service/util/ClasspathScanIndex.java:45-57
The new ClasspathScanIndex singleton opens a ClassGraph ScanResult in its constructor and never closes it, whereas the previous CollectionRegistry/Entity code wrapped the scan in try-with-resources. The loaded classes are cached in the two maps, so the ScanResult itself no longer needs to stay open after warmup; retaining it holds ClassGraph's class-graph metadata (and any file handles) for the life of the process. Consider closing the ScanResult after populating the caches, or eagerly materializing the needed lookups and closing the scan.

Edge Case: Seed-data gate skips reseeding on DB drift when files are unchanged

📄 openmetadata-service/src/main/java/org/openmetadata/service/seeding/SeedDataGate.java:96-101 📄 openmetadata-service/src/main/java/org/openmetadata/service/seeding/SeedDataGate.java:173-187 📄 openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/EntityRepository.java:1408-1413
SeedDataGate.shouldSeed() decides whether to seed based solely on a fingerprint of the classpath seed resources plus server version, compared to the fingerprint persisted in the settings table. If seed rows (roles, policies, bootstrap types, etc.) are removed or corrupted out-of-band while the seed files remain unchanged, a restart will skip reseeding and leave the system missing required seed data. This is mitigated by STARTUP_FORCE_SEED_DATA / seedDataGateEnabled=false, but the failure mode is silent. Consider documenting this operational caveat prominently, or gating on a lightweight presence check in addition to the file fingerprint.

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

Comment on lines +602 to +608
if (!force
&& buildResult.failures() == 0
&& !SeedDataGate.getInstance()
.shouldUpdateSearchTemplates(fingerprint, createdIndexCount)) {
LOG.info("Index templates are unchanged; skipping {} template updates", templates.size());
SeedDataGate.getInstance().recordSearchTemplateFingerprint(fingerprint);
return;

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 Templates remain unchecked This branch returns before any Elasticsearch/OpenSearch template read or PUT when the local mapping fingerprint matches and createdIndexCount == 0. On a warm boot where all indexes exist but the search cluster templates were deleted or restored stale, the local checksum still matches the database row, so startup records the same fingerprint again and leaves the cluster templates missing or stale. Later indexing can then run with missing or incorrect mappings until a forced path or mapping change happens.

Copilot AI review requested due to automatic review settings July 19, 2026 00:56

Copilot AI left a comment

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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Comment on lines +313 to +324
private String getEmailTemplateProvider() {
Settings emailSettings =
systemRepository.getConfigWithKey(SettingsType.EMAIL_CONFIGURATION.toString());
if (emailSettings == null || emailSettings.getConfigValue() == null) {
return null;
}
SmtpSettings smtpSettings =
JsonUtils.convertValue(emailSettings.getConfigValue(), SmtpSettings.class);
return smtpSettings.getTemplates() == null
? OPENMETADATA_EMAIL_TEMPLATE_PROVIDER
: smtpSettings.getTemplates().value();
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Edge Case: Warm-start seed gate reseeds every boot when email config is unset

When no EMAIL_CONFIGURATION settings row exists, SeedDataGate.getEmailTemplateProvider() returns null (SeedDataGate.java:316-317), and RequiredSeedRows.selectEmailDocuments(null) then requires BOTH openmetadata and collate email documents to be present (RequiredSeedRows.java:58-60). However the actual seeder DocumentRepository.getEntitiesFromSeedData() loads only ONE provider's templates — defaulting to openmetadata (DocumentRepository.java:64-75) via SettingsCache which returns the OPENMETADATA default rather than null. As a result the collate email documents are never seeded, hasRequiredSeedRows always returns false, and the drift check reseeds on every warm boot — defeating the startup optimization this PR introduces. Make getEmailTemplateProvider default to OPENMETADATA when the config row is absent (mirroring the seeder / DefaultOperationalConfigProvider), instead of returning null.

Default to the OPENMETADATA provider when no email config exists, matching the seeder which only loads one provider's documents.:

private String getEmailTemplateProvider() {
  Settings emailSettings =
      systemRepository.getConfigWithKey(SettingsType.EMAIL_CONFIGURATION.toString());
  if (emailSettings == null || emailSettings.getConfigValue() == null) {
    return OPENMETADATA_EMAIL_TEMPLATE_PROVIDER;
  }
  SmtpSettings smtpSettings =
      JsonUtils.convertValue(emailSettings.getConfigValue(), SmtpSettings.class);
  return smtpSettings.getTemplates() == null
      ? OPENMETADATA_EMAIL_TEMPLATE_PROVIDER
      : smtpSettings.getTemplates().value();
}
  • Apply fix

Check the box to apply the fix or reply for a change | Was this helpful? React with 👍 / 👎

@@ -88,7 +91,7 @@ private static void seedBundle(
EntityReference frameworkRef = saved.getEntityReference();
for (JsonNode controlNode : controlsNode) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Bug: Flat control FQN can skip seeding controls sharing a name across frameworks

FrameworkSeedLoader now stores each AIFrameworkControl with a flat FQN of just the control name (FrameworkSeedLoader.java:94,100) instead of framework.control, and dedups via findByNameOrNull on that flat name. If two frameworks define controls with the same name, the second control resolves to the first framework's already-seeded control and is silently skipped (continue at line 96-97), so it is never created. This matches the flat identity the drift check expects, but silently drops legitimately distinct controls when names collide across frameworks. Consider qualifying the control FQN with the framework (and updating the drift-check identity accordingly) if cross-framework control-name collisions are possible.

Was this helpful? React with 👍 / 👎

@gitar-bot

gitar-bot Bot commented Jul 19, 2026

Copy link
Copy Markdown
Code Review ⚠️ Changes requested 4 resolved / 6 findings

Optimizes server startup and write-path latency through fingerprint-based seed gating, JSON reuse, and lineage index improvements. Addresses seed-data drift and classpath indexing, but requires attention to incomplete seed coverage for specific entities and potential naming collisions in FrameworkSeedLoader.

⚠️ Edge Case: Warm-start seed gate reseeds every boot when email config is unset

📄 openmetadata-service/src/main/java/org/openmetadata/service/seeding/SeedDataGate.java:313-324 📄 openmetadata-service/src/main/java/org/openmetadata/service/seeding/RequiredSeedRows.java:55-67

When no EMAIL_CONFIGURATION settings row exists, SeedDataGate.getEmailTemplateProvider() returns null (SeedDataGate.java:316-317), and RequiredSeedRows.selectEmailDocuments(null) then requires BOTH openmetadata and collate email documents to be present (RequiredSeedRows.java:58-60). However the actual seeder DocumentRepository.getEntitiesFromSeedData() loads only ONE provider's templates — defaulting to openmetadata (DocumentRepository.java:64-75) via SettingsCache which returns the OPENMETADATA default rather than null. As a result the collate email documents are never seeded, hasRequiredSeedRows always returns false, and the drift check reseeds on every warm boot — defeating the startup optimization this PR introduces. Make getEmailTemplateProvider default to OPENMETADATA when the config row is absent (mirroring the seeder / DefaultOperationalConfigProvider), instead of returning null.

Default to the OPENMETADATA provider when no email config exists, matching the seeder which only loads one provider's documents.
private String getEmailTemplateProvider() {
  Settings emailSettings =
      systemRepository.getConfigWithKey(SettingsType.EMAIL_CONFIGURATION.toString());
  if (emailSettings == null || emailSettings.getConfigValue() == null) {
    return OPENMETADATA_EMAIL_TEMPLATE_PROVIDER;
  }
  SmtpSettings smtpSettings =
      JsonUtils.convertValue(emailSettings.getConfigValue(), SmtpSettings.class);
  return smtpSettings.getTemplates() == null
      ? OPENMETADATA_EMAIL_TEMPLATE_PROVIDER
      : smtpSettings.getTemplates().value();
}
💡 Bug: Flat control FQN can skip seeding controls sharing a name across frameworks

📄 openmetadata-service/src/main/java/org/openmetadata/service/resources/ai/FrameworkSeedLoader.java:92-104 📄 openmetadata-service/src/main/java/org/openmetadata/service/seeding/SeedDataGate.java:537-549

FrameworkSeedLoader now stores each AIFrameworkControl with a flat FQN of just the control name (FrameworkSeedLoader.java:94,100) instead of framework.control, and dedups via findByNameOrNull on that flat name. If two frameworks define controls with the same name, the second control resolves to the first framework's already-seeded control and is silently skipped (continue at line 96-97), so it is never created. This matches the flat identity the drift check expects, but silently drops legitimately distinct controls when names collide across frameworks. Consider qualifying the control FQN with the framework (and updating the drift-check identity accordingly) if cross-framework control-name collisions are possible.

✅ 4 resolved
Bug: CommonUtil.getResources now returns an immutable list

📄 common/src/main/java/org/openmetadata/common/utils/CommonUtil.java:63-77
getResources() now returns stream()...toList() over an immutable index (buildClasspathResourceIndex uses List.copyOf), whereas it previously returned a mutable ArrayList. Any caller that mutates the returned list (add/remove/sort) will now throw UnsupportedOperationException at runtime. The new CommonUtilTest even asserts this immutability. Please confirm no downstream caller of getResources()/getJsonDataResources() mutates the result; if any does, wrap it in a new ArrayList or fix the caller.

Performance: Search-template gate never stamps fingerprint if any mapping is missing

📄 openmetadata-service/src/main/java/org/openmetadata/service/search/SearchRepository.java:582 📄 openmetadata-service/src/main/java/org/openmetadata/service/search/SearchRepository.java:595-599 📄 openmetadata-service/src/main/java/org/openmetadata/service/search/SearchRepository.java:607-621
In createOrUpdateIndexTemplates, failed is initialized to entityIndexMap.size() - templates.size(). buildIndexTemplateDefinitions() silently drops any entity whose mapping content is null or fails to read, so templates.size() can be permanently smaller than entityIndexMap.size(). That makes failed > 0 on every boot, which triggers recordSearchTemplateFailure() (resetting the stored fingerprint to null). As a result the search-template gate never records a clean fingerprint and templates are rebuilt on every startup, defeating the warm-start optimization this PR adds. Consider only counting genuine per-template failures (increment inside the catch block) rather than pre-charging failed with the count of skipped/no-mapping entities.

Quality: ClasspathScanIndex keeps ScanResult open for JVM lifetime

📄 openmetadata-service/src/main/java/org/openmetadata/service/util/ClasspathScanIndex.java:32-39 📄 openmetadata-service/src/main/java/org/openmetadata/service/util/ClasspathScanIndex.java:45-57
The new ClasspathScanIndex singleton opens a ClassGraph ScanResult in its constructor and never closes it, whereas the previous CollectionRegistry/Entity code wrapped the scan in try-with-resources. The loaded classes are cached in the two maps, so the ScanResult itself no longer needs to stay open after warmup; retaining it holds ClassGraph's class-graph metadata (and any file handles) for the life of the process. Consider closing the ScanResult after populating the caches, or eagerly materializing the needed lookups and closing the scan.

Edge Case: Seed-data gate skips reseeding on DB drift when files are unchanged

📄 openmetadata-service/src/main/java/org/openmetadata/service/seeding/SeedDataGate.java:96-101 📄 openmetadata-service/src/main/java/org/openmetadata/service/seeding/SeedDataGate.java:173-187 📄 openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/EntityRepository.java:1408-1413
SeedDataGate.shouldSeed() decides whether to seed based solely on a fingerprint of the classpath seed resources plus server version, compared to the fingerprint persisted in the settings table. If seed rows (roles, policies, bootstrap types, etc.) are removed or corrupted out-of-band while the seed files remain unchanged, a restart will skip reseeding and leave the system missing required seed data. This is mitigated by STARTUP_FORCE_SEED_DATA / seedDataGateEnabled=false, but the failure mode is silent. Consider documenting this operational caveat prominently, or gating on a lightweight presence check in addition to the file fingerprint.

🤖 Prompt for agents
Code Review: Optimizes server startup and write-path latency through fingerprint-based seed gating, JSON reuse, and lineage index improvements. Addresses seed-data drift and classpath indexing, but requires attention to incomplete seed coverage for specific entities and potential naming collisions in FrameworkSeedLoader.

1. ⚠️ Edge Case: Warm-start seed gate reseeds every boot when email config is unset
   Files: openmetadata-service/src/main/java/org/openmetadata/service/seeding/SeedDataGate.java:313-324, openmetadata-service/src/main/java/org/openmetadata/service/seeding/RequiredSeedRows.java:55-67

   When no EMAIL_CONFIGURATION settings row exists, SeedDataGate.getEmailTemplateProvider() returns null (SeedDataGate.java:316-317), and RequiredSeedRows.selectEmailDocuments(null) then requires BOTH openmetadata and collate email documents to be present (RequiredSeedRows.java:58-60). However the actual seeder DocumentRepository.getEntitiesFromSeedData() loads only ONE provider's templates — defaulting to openmetadata (DocumentRepository.java:64-75) via SettingsCache which returns the OPENMETADATA default rather than null. As a result the collate email documents are never seeded, hasRequiredSeedRows always returns false, and the drift check reseeds on every warm boot — defeating the startup optimization this PR introduces. Make getEmailTemplateProvider default to OPENMETADATA when the config row is absent (mirroring the seeder / DefaultOperationalConfigProvider), instead of returning null.

   Fix (Default to the OPENMETADATA provider when no email config exists, matching the seeder which only loads one provider's documents.):
   private String getEmailTemplateProvider() {
     Settings emailSettings =
         systemRepository.getConfigWithKey(SettingsType.EMAIL_CONFIGURATION.toString());
     if (emailSettings == null || emailSettings.getConfigValue() == null) {
       return OPENMETADATA_EMAIL_TEMPLATE_PROVIDER;
     }
     SmtpSettings smtpSettings =
         JsonUtils.convertValue(emailSettings.getConfigValue(), SmtpSettings.class);
     return smtpSettings.getTemplates() == null
         ? OPENMETADATA_EMAIL_TEMPLATE_PROVIDER
         : smtpSettings.getTemplates().value();
   }

2. 💡 Bug: Flat control FQN can skip seeding controls sharing a name across frameworks
   Files: openmetadata-service/src/main/java/org/openmetadata/service/resources/ai/FrameworkSeedLoader.java:92-104, openmetadata-service/src/main/java/org/openmetadata/service/seeding/SeedDataGate.java:537-549

   FrameworkSeedLoader now stores each AIFrameworkControl with a flat FQN of just the control name (FrameworkSeedLoader.java:94,100) instead of framework.control, and dedups via findByNameOrNull on that flat name. If two frameworks define controls with the same name, the second control resolves to the first framework's already-seeded control and is silently skipped (continue at line 96-97), so it is never created. This matches the flat identity the drift check expects, but silently drops legitimately distinct controls when names collide across frameworks. Consider qualifying the control FQN with the framework (and updating the drift-check identity accordingly) if cross-framework control-name collisions are possible.

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

@sonarqubecloud

Copy link
Copy Markdown

@sonarqubecloud

Copy link
Copy Markdown

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.

Improve server startup and write-path latency

2 participants