Skip to content

Feat(Workflows): Auto-inject inclusive gateways for batch routing#26918

Open
yan-3005 wants to merge 32 commits intoram/workflow-improvementsfrom
ram/inclusive-gateway
Open

Feat(Workflows): Auto-inject inclusive gateways for batch routing#26918
yan-3005 wants to merge 32 commits intoram/workflow-improvementsfrom
ram/inclusive-gateway

Conversation

@yan-3005
Copy link
Copy Markdown
Contributor

@yan-3005 yan-3005 commented Apr 1, 2026

Describe your changes:

Phase 2 of the batch workflow improvements. Builds on Phase 1 (PR #26715) which made all task nodes batch-aware with entity lists.

Problem: Check nodes previously emitted a single RESULT_VARIABLE boolean, which drove exclusive gateway routing — only one branch (true OR false) could fire. In batch mode, a single check node can produce both passing and failing entities simultaneously, so both branches need to fire.

Solution: Auto-inject Flowable inclusive gateways at BPMN build time based on graph structure — no changes needed to workflow JSON definitions.

Changes:

  • InclusiveGatewayBuilder (new) — mirrors ExclusiveGatewayBuilder for InclusiveGateway elements
  • NodeInterface.getOutputPorts() (new default method) — nodes declare which output ports they produce. Only nodes overriding this with ≥2 ports get inclusive gateways injected. UserApprovalTask and other single-result nodes return the default empty set and are unaffected.
  • CheckEntityAttributesTask, CheckChangeDescriptionTask — override getOutputPorts() returning {"true", "false"}
  • DataCompletenessTask — overrides getOutputPorts() returning the configured quality band names (e.g. {"gold", "silver"})
  • Check node impls — removed RESULT_VARIABLE; now emit hasTrueEntities + hasFalseEntities boolean flags (check nodes), or has_<band>_entities per band (DataCompleteness)
  • MainWorkflow — detects split/join points at build time and injects inclusive gateways automatically:
    • Split gateway after any node with getOutputPorts().size() ≥ 2 and ≥2 conditional outgoing edges; conditions map to ${nodeName_hasTrueEntities}, ${nodeName_hasFalseEntities}, ${nodeName_has_<band>_entities}
    • Join gateway before any node with ≥2 incoming edges tracing back to a common split ancestor (BFS upward)

Before (exclusive — only one branch fires):

CheckNode → [${result=='true'}] → PassBranch
          → [${result=='false'}] → FailBranch

After (inclusive — both branches fire when batch has mixed results):

CheckNode → InclusiveGateway(split) → [${check_hasTrueEntities}] → PassBranch
                                    → [${check_hasFalseEntities}] → FailBranch
  • WorkflowEventConsumer.java, SetEntityAttributeImpl.java, EntityRepository.java — soft-deleted entity guard: entity lookups switched to Include.NON_DELETED; EntityRepository.bulkUpdateEntitiesForGovernanceWorkflow gained a skipDeleted parameter set to true for all governance callers; prevents governance workflows from inadvertently resurrecting glossary terms (or any entity) deleted while a workflow batch is in flight

Type of change:

  • New feature

Checklist:

  • I have read the CONTRIBUTING document.
  • I have added tests around the new logic.

Summary by Gitar

  • Governance workflow security:
    • Modified EntityRepository and SetEntityAttributeImpl to skip soft-deleted entities, preventing unauthorized restoration during workflow execution.
    • Updated WorkflowEventConsumer to exclude deleted entities when triggering workflows, ensuring governance actions only apply to active data.

This will update automatically on new commits.

Check nodes (CheckEntityAttributes, CheckChangeDescription, DataCompleteness)
now emit boolean flag variables (hasTrueEntities, hasFalseEntities,
has_<band>_entities) instead of a single RESULT_VARIABLE. MainWorkflow
auto-detects split/join points at BPMN build time via NodeInterface.getOutputPorts()
and injects InclusiveGateways so both true and false branches fire simultaneously
when a batch has mixed pass/fail entities.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@yan-3005 yan-3005 self-assigned this Apr 1, 2026
@yan-3005 yan-3005 requested a review from a team as a code owner April 1, 2026 08:06
@yan-3005 yan-3005 added safe to test Add this label to run secure Github workflows on PRs backend labels Apr 1, 2026
Copilot AI review requested due to automatic review settings April 1, 2026 08:06
@yan-3005 yan-3005 changed the base branch from main to ram/workflow-improvements April 1, 2026 08:08
@yan-3005 yan-3005 changed the title Ram/inclusive gateway Feat(Workflows): Auto-inject inclusive gateways for batch routing Apr 1, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR updates OpenMetadata governance workflows to support batch entity processing via a new entityList variable, adds inclusive gateway injection for batch “check” nodes (true/false and quality-band branching), and introduces a v1140 migration to update persisted workflow JSON accordingly.

Changes:

  • Replace/augment legacy relatedEntity usage with entityList across workflow triggers, node schemas, and runtime variable handling.
  • Inject inclusive split/join gateways in Flowable BPMN generation for nodes that produce multiple output ports (e.g., check tasks, data completeness bands).
  • Add v1140 migration + extensive unit/integration test updates for new variable semantics and batch entity fetching.

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
openmetadata-ui/src/main/resources/ui/src/generated/governance/workflows/elements/nodes/automatedTask/sinkTask.ts Generated UI type updates: InputNamespaceMap now uses entityList and allows additional properties.
openmetadata-ui/src/main/resources/ui/src/generated/governance/workflows/elements/nodes/automatedTask/setEntityAttributeTask.ts Generated UI type updates to entityList-based input namespace map.
openmetadata-ui/src/main/resources/ui/src/generated/governance/workflows/elements/nodes/automatedTask/rollbackEntityTask.ts Generated UI type updates + updated rollback description.
openmetadata-ui/src/main/resources/ui/src/generated/governance/workflows/elements/nodes/automatedTask/dataCompletenessTask.ts Generated UI type updates to entityList-based input namespace map.
openmetadata-ui/src/main/resources/ui/src/generated/governance/workflows/elements/nodes/automatedTask/checkEntityAttributesTask.ts Generated UI type updates + adds output?: string[] on task interface and entityList in namespace map.
openmetadata-ui/src/main/resources/ui/src/generated/governance/workflows/elements/nodes/automatedTask/checkChangeDescriptionTask.ts Generated UI type updates + adds output?: string[] on task interface and entityList in namespace map.
openmetadata-ui/src/main/resources/ui/src/generated/api/governance/createWorkflowDefinition.ts API types updated to include output?: string[] and allow entityList in trigger namespace mapping.
openmetadata-spec/src/main/resources/json/schema/governance/workflows/elements/triggers/periodicBatchEntityTrigger.json Trigger schema output default now includes entityList; relaxes single-item constraint.
openmetadata-spec/src/main/resources/json/schema/governance/workflows/elements/triggers/eventBasedEntityTrigger.json Trigger schema output default now includes entityList; relaxes single-item constraint.
openmetadata-spec/src/main/resources/json/schema/governance/workflows/elements/nodes/automatedTask/sinkTask.json Node schema migrated to require entityList and allow additional properties for conditional keys.
openmetadata-spec/src/main/resources/json/schema/governance/workflows/elements/nodes/automatedTask/setEntityAttributeTask.json Node schema migrated to require entityList and allow additional properties; input defaults/minItems adjusted.
openmetadata-spec/src/main/resources/json/schema/governance/workflows/elements/nodes/automatedTask/rollbackEntityTask.json Node schema migrated to require entityList; description updated to fallback chain.
openmetadata-spec/src/main/resources/json/schema/governance/workflows/elements/nodes/automatedTask/dataCompletenessTask.json Node schema migrated to require entityList; loosens input constraints.
openmetadata-spec/src/main/resources/json/schema/governance/workflows/elements/nodes/automatedTask/checkEntityAttributesTask.json Node schema switches to entityList + adds explicit output defaults for derived entity lists.
openmetadata-spec/src/main/resources/json/schema/governance/workflows/elements/nodes/automatedTask/checkChangeDescriptionTask.json Node schema switches to entityList + adds explicit output defaults for derived entity lists.
openmetadata-service/src/test/java/org/openmetadata/service/migration/utils/v1140/MigrationUtilTest.java New unit tests for v1140 workflow JSON migration behavior.
openmetadata-service/src/test/java/org/openmetadata/service/migration/utils/v1105/MigrationUtilTest.java Updates test fixtures to entityList in workflow JSON.
openmetadata-service/src/test/java/org/openmetadata/service/governance/workflows/flowable/MainWorkflowInclusiveGatewayTest.java New tests verifying inclusive split/join gateway injection logic.
openmetadata-service/src/test/java/org/openmetadata/service/governance/workflows/flowable/builders/InclusiveGatewayBuilderTest.java New unit tests for InclusiveGatewayBuilder.
openmetadata-service/src/test/java/org/openmetadata/service/governance/workflows/elements/nodes/automatedTask/sink/SinkTaskTest.java Updates sink workflow JSON fixtures to entityList.
openmetadata-service/src/test/java/org/openmetadata/service/governance/workflows/elements/nodes/automatedTask/sink/SinkTaskDelegateTest.java Updates delegate tests to batch entity fetching and entityList semantics.
openmetadata-service/src/test/java/org/openmetadata/service/governance/workflows/elements/nodes/automatedTask/SetEntityAttributeTaskTest.java New unit tests for BPMN construction of SetEntityAttribute task with entityList.
openmetadata-service/src/test/java/org/openmetadata/service/governance/workflows/elements/nodes/automatedTask/RollbackEntityTaskTest.java New unit tests for BPMN construction of RollbackEntity task with entityList.
openmetadata-service/src/test/java/org/openmetadata/service/governance/workflows/elements/nodes/automatedTask/impl/SetEntityAttributeImplTest.java New unit tests for batch SetEntityAttributeImpl execution.
openmetadata-service/src/test/java/org/openmetadata/service/governance/workflows/elements/nodes/automatedTask/impl/CheckEntityAttributesImplTest.java New unit tests for batch CheckEntityAttributesImpl execution and flags.
openmetadata-service/src/test/java/org/openmetadata/service/governance/workflows/elements/nodes/automatedTask/impl/CheckChangeDescriptionTaskImplTest.java New unit tests for batch CheckChangeDescriptionTaskImpl execution and flags.
openmetadata-service/src/test/java/org/openmetadata/service/governance/workflows/elements/nodes/automatedTask/DataCompletenessTaskTest.java New unit tests for BPMN construction of DataCompleteness task with entityList.
openmetadata-service/src/test/java/org/openmetadata/service/governance/workflows/elements/nodes/automatedTask/CheckEntityAttributesTaskTest.java New unit tests for BPMN construction of CheckEntityAttributes task with entityList.
openmetadata-service/src/test/java/org/openmetadata/service/governance/workflows/elements/nodes/automatedTask/CheckChangeDescriptionTaskTest.java New unit tests for BPMN construction of CheckChangeDescription task with entityList.
openmetadata-service/src/main/resources/json/data/governance/workflows/GlossaryApprovalWorkflow.json Updates default workflow to emit/use entityList and conditional *_entityList wiring.
openmetadata-service/src/main/java/org/openmetadata/service/migration/utils/v1140/MigrationUtil.java New v1140 migration utility to rewrite stored workflow JSON to entityList + conditional wiring.
openmetadata-service/src/main/java/org/openmetadata/service/migration/utils/v1105/MigrationUtil.java Adds migration logic to rewrite glossary status node namespace map to entityList.
openmetadata-service/src/main/java/org/openmetadata/service/migration/postgres/v1140/Migration.java New Postgres migration entrypoint invoking v1140 workflow migration.
openmetadata-service/src/main/java/org/openmetadata/service/migration/mysql/v1140/Migration.java New MySQL migration entrypoint invoking v1140 workflow migration.
openmetadata-service/src/main/java/org/openmetadata/service/governance/workflows/WorkflowVariableHandler.java Adds getEntityList() helper resolving entity lists from namespace maps (plain and conditional).
openmetadata-service/src/main/java/org/openmetadata/service/governance/workflows/WorkflowEventConsumer.java Ensures event-based trigger variables include both relatedEntity and entityList.
openmetadata-service/src/main/java/org/openmetadata/service/governance/workflows/Workflow.java Adds shared workflow constants for true/false lists and retry configuration.
openmetadata-service/src/main/java/org/openmetadata/service/governance/workflows/flowable/MainWorkflow.java Injects inclusive split/join gateways based on node output ports + conditional edges.
openmetadata-service/src/main/java/org/openmetadata/service/governance/workflows/flowable/builders/InclusiveGatewayBuilder.java New builder for Flowable InclusiveGateway.
openmetadata-service/src/main/java/org/openmetadata/service/governance/workflows/elements/triggers/PeriodicBatchEntityTrigger.java Adjusts periodic trigger call-activity wiring to pass entityList for both single and parallel modes.
openmetadata-service/src/main/java/org/openmetadata/service/governance/workflows/elements/triggers/impl/FilterEntityImpl.java Sets global entityList for event-based flows after filtering.
openmetadata-service/src/main/java/org/openmetadata/service/governance/workflows/elements/triggers/impl/FetchEntitiesImpl.java Sets entityList, numberOfEntities, and entityToListMap for periodic triggers.
openmetadata-service/src/main/java/org/openmetadata/service/governance/workflows/elements/triggers/EventBasedEntityTrigger.java Always passes entityList into the main workflow; avoids duplicating trigger outputs.
openmetadata-service/src/main/java/org/openmetadata/service/governance/workflows/elements/TriggerFactory.java Refines batch-mode detection documentation and logic for trigger execution mode.
openmetadata-service/src/main/java/org/openmetadata/service/governance/workflows/elements/nodes/automatedTask/SinkTask.java Ensures sink task namespace map handling uses defined map and always includes entityList.
openmetadata-service/src/main/java/org/openmetadata/service/governance/workflows/elements/nodes/automatedTask/sink/SinkTaskDelegate.java Switches sink execution to entityList and introduces batched entity fetch + retries.
openmetadata-service/src/main/java/org/openmetadata/service/governance/workflows/elements/nodes/automatedTask/SetEntityAttributeTask.java Ensures BPMN wiring includes default entityList namespace.
openmetadata-service/src/main/java/org/openmetadata/service/governance/workflows/elements/nodes/automatedTask/RollbackEntityTask.java Ensures BPMN wiring includes default entityList namespace.
openmetadata-service/src/main/java/org/openmetadata/service/governance/workflows/elements/nodes/automatedTask/impl/SetEntityAttributeImpl.java Converts to batch processing over entityList with retry + partial failure recording.
openmetadata-service/src/main/java/org/openmetadata/service/governance/workflows/elements/nodes/automatedTask/impl/RollbackEntityImpl.java Converts to batch rollback over entityList with retry + fallback target version selection.
openmetadata-service/src/main/java/org/openmetadata/service/governance/workflows/elements/nodes/automatedTask/impl/DataCompletenessImpl.java Converts to batch completeness evaluation, per-band entity lists, and gateway-ready flags.
openmetadata-service/src/main/java/org/openmetadata/service/governance/workflows/elements/nodes/automatedTask/impl/CheckEntityAttributesImpl.java Converts to batch rule evaluation producing true/false entity lists and flags.
openmetadata-service/src/main/java/org/openmetadata/service/governance/workflows/elements/nodes/automatedTask/impl/CheckChangeDescriptionTaskImpl.java Converts to batch change-description evaluation producing true/false entity lists and flags.
openmetadata-service/src/main/java/org/openmetadata/service/governance/workflows/elements/nodes/automatedTask/DataCompletenessTask.java Declares output ports (bands) and defaults entityList mapping for gateway injection.
openmetadata-service/src/main/java/org/openmetadata/service/governance/workflows/elements/nodes/automatedTask/CheckEntityAttributesTask.java Declares output ports (true/false) and defaults entityList mapping for gateway injection.
openmetadata-service/src/main/java/org/openmetadata/service/governance/workflows/elements/nodes/automatedTask/CheckChangeDescriptionTask.java Declares output ports (true/false) and defaults entityList mapping for gateway injection.
openmetadata-service/src/main/java/org/openmetadata/service/governance/workflows/elements/NodeInterface.java Adds getOutputPorts() hook used for inclusive gateway detection.
openmetadata-service/src/main/java/org/openmetadata/service/Entity.java Adds getEntitiesByLinks() helper for efficient batch entity fetch by entity link strings.
openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/WorkflowDefinitionResourceIT.java Updates integration tests to build/validate workflows using entityList and new outputs.

Comment on lines +33 to +47
// Workflow: start → check → cert(true) / notify(false) → independent ends
WorkflowDefinition workflow =
createWorkflow(
"SplitOnlyWorkflow",
List.of(
startEvent("start"),
checkAttributesNode("check"),
endEvent("certEnd"),
endEvent("notifyEnd")),
List.of(
edge("start", "check", null),
edge("check", "cert", "true"),
edge("check", "notify", "false"),
edge("cert", "certEnd", null),
edge("notify", "notifyEnd", null)));
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

This test workflow defines edges to node ids (e.g. cert, notify) that are not included in the nodes list. That can produce a BPMN model with SequenceFlow targets/sources that don't exist, making the test less representative and potentially hiding issues if workflow validation becomes stricter. Consider adding minimal node definitions for cert/notify (or update edges to point to existing nodes) so the constructed workflow is structurally valid.

Copilot uses AI. Check for mistakes.
…ferences

- Check nodes (CheckEntityAttributes, CheckChangeDescription) now set
  hasFalseEntities=true when entity list is empty, ensuring the split
  inclusive gateway always has at least one active branch and never stalls.
- DataCompletenessImpl activates the lowest-score band flag when no entities
  are assigned to any band (empty input or all entities failed processing).
- MainWorkflowInclusiveGatewayTest: replaced dangling edge targets (cert,
  notify, goldAction etc.) with actual declared end-event nodes so the
  constructed BPMN model is structurally valid.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 1, 2026

Jest test Coverage

UI tests summary

Lines Statements Branches Functions
Coverage: 62%
62.33% (62909/100924) 42.66% (33911/79487) 45.68% (10035/21968)

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 1, 2026

🔴 Playwright Results — 4 failure(s), 10 flaky

✅ 3665 passed · ❌ 4 failed · 🟡 10 flaky · ⏭️ 102 skipped

Shard Passed Failed Flaky Skipped
🟡 Shard 2 752 0 2 8
🔴 Shard 3 721 4 1 27
🟡 Shard 4 773 0 2 18
✅ Shard 5 687 0 0 41
🟡 Shard 6 732 0 5 8

Genuine Failures (failed on all attempts)

Features/Workflows/WorkflowOssRestrictions.spec.ts › create-workflow-button absent on OSS (shard 3)
Error: �[2mexpect(�[22m�[31mreceived�[39m�[2m).�[22mtoBeTruthy�[2m()�[22m

Received: �[31mfalse�[39m
Features/Workflows/WorkflowOssRestrictions.spec.ts › delete-node-button absent in node config sidebar (structural edit blocked) (shard 3)
Error: �[2mexpect(�[22m�[31mreceived�[39m�[2m).�[22mtoBeTruthy�[2m()�[22m

Received: �[31mfalse�[39m
Features/Workflows/WorkflowOssRestrictions.spec.ts › event-type-select is disabled in OSS (shard 3)
Error: �[2mexpect(�[22m�[31mreceived�[39m�[2m).�[22mtoBeTruthy�[2m()�[22m

Received: �[31mfalse�[39m
Pages/CSVImportWithQuotesAndCommas.spec.ts › Create glossary with CSV, export it, create new glossary and import exported data (shard 3)
Error: �[2mexpect(�[22m�[31mreceived�[39m�[2m).�[22mtoContain�[2m(�[22m�[32mexpected�[39m�[2m) // indexOf�[22m

Expected substring: �[32m"\"\"quotes\"\""�[39m
Received string:    �[31m"parent,name*,displayName,description,synonyms,relatedTerms,references,tags,reviewers,owner,glossaryStatus,color,iconURL,extension·�[39m
�[31m,Term1,TermAnuj,\"<p>Contains a timestamp for the most recent &#34;login&#34; of this feature user, to be used for PIN expiration.</p>\",,,,,,user:admin,Approved,,,·�[39m
�[31m,\"TermWithComma,AndQuote\",\"Display name with \"\"quoted\"\" text, and comma\",\"<p>Description with &#34;quotes&#34; and, commas</p>\",,,,,,user:admin,Approved,,,·�[39m
�[31m,Test1234,\"Contains a timestamp for the most recent \"\"login\"\" of this feature user, to be used for PIN expiration.\",\"<p>Contains a timestamp for the most recent &#34;login&#34; of this feature user, to be used for PIN expiration.</p>\",,,,,,user:admin,Approved,,,·�[39m
�[31m"�[39m
🟡 10 flaky test(s) (passed on retry)
  • Features/ActivityAPI.spec.ts › Activity event is created when description is updated (shard 2, 1 retry)
  • Features/ActivityAPI.spec.ts › Activity event shows the actor who made the change (shard 2, 1 retry)
  • Features/RTL.spec.ts › Verify Following widget functionality (shard 3, 1 retry)
  • Pages/CustomProperties.spec.ts › Should search custom properties for apiCollection in right panel (shard 4, 1 retry)
  • Pages/DataContractsSemanticRules.spec.ts › Validate Description Rule Is_Set (shard 4, 1 retry)
  • Pages/ExplorePageRightPanel.spec.ts › Should allow Data Consumer to edit tier for dashboardDataModel (shard 6, 1 retry)
  • Pages/Lineage/DataAssetLineage.spec.ts › Verify updating depth configuration (shard 6, 1 retry)
  • Pages/Lineage/LineageFilters.spec.ts › Verify lineage schema filter selection (shard 6, 1 retry)
  • Pages/Lineage/LineageRightPanel.spec.ts › Verify custom properties tab IS visible for supported type: searchIndex (shard 6, 1 retry)
  • Pages/Tags.spec.ts › Verify Owner Add Delete (shard 6, 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

- DataCompletenessTask: guard against null getQualityBands() with Optional
- DataCompletenessImpl: extract bandFlagVariable() static helper used by
  both the impl and MainWorkflow to avoid naming convention drift
- InclusiveGatewayBuilder: remove misleading exclusive() setter; async job
  exclusivity is hardcoded true (not a routing concern, matches ExclusiveGatewayBuilder)
- MainWorkflow: add @slf4j + warn log when a conditional edge targets a join
  node (condition is intentionally ignored); use DataCompletenessImpl.bandFlagVariable()
- DataCompletenessImplTest: add tests for empty entity list and all-entities-fail
  scenarios, both verifying lowest-band flag is activated as deadlock fallback

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@sonarqubecloud
Copy link
Copy Markdown

@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.

yan-3005 and others added 12 commits April 28, 2026 10:17
Resolved conflict in MainWorkflow.java: took base branch's 3-arg
NodeFactory.createNode() with workflowDefinition.getFullyQualifiedName();
kept nodeInstanceMap.put() from this branch.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…y PR

- InclusiveGatewayBuilder: remove setExclusive(true) — verified against
  Flowable 7.2.0 source that this flag only affects async-job scheduler
  exclusivity; InclusiveGatewayActivityBehavior never reads it; routing
  semantics come from the InclusiveGateway class type, not this property

- MainWorkflow: wire defaultFlow on each split gateway pointing to the
  corresponding join gateway (via new buildSplitToJoinMap helper).
  Verified against TakeOutgoingSequenceFlowsOperation source: without a
  defaultFlow, Flowable throws FlowableException when all conditions are
  false. buildGatewayCondition now emits ${var == true} instead of ${var}
  so UelExpressionCondition.evaluate never receives a bare null

- DataCompletenessImpl: remove anyBandActive fallback that forced the
  lowest-score band flag to true with an empty entity list. All flags are
  already written as !bandEntities.isEmpty() in the per-band loop, so
  when all conditions are false the split gateway safely falls through to
  its defaultFlow, eliminating spurious audit log entries from stage
  listeners firing on zero-entity SetEntityAttribute executions

- Tests updated throughout to reflect new condition format (== true),
  3-incoming-flows on join gateway (2 conditional + 1 defaultFlow), and
  all-false flags for empty/all-failed data completeness runs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Switch Include.ALL to Include.NON_DELETED in WorkflowEventConsumer and
SetEntityAttributeImpl so deleted entities are never picked up for
workflow processing. Add skipDeleted guard in bulkUpdateEntitiesForGovernanceWorkflow
so even if a deleted entity reaches the bulk-update path it is skipped
rather than restored.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@gitar-bot
Copy link
Copy Markdown

gitar-bot Bot commented May 5, 2026

Code Review 👍 Approved with suggestions 5 resolved / 6 findings

Auto-inject inclusive gateways feature adds batch routing logic but requires fixes: retry logic unnecessarily wraps in-memory computation in DataCompletenessImpl, split gateway detection ignores unconditional edges with null conditions (causing incorrect routing), and empty qualityBands fallback is non-functional. Five related issues were addressed but three remain open.

💡 Performance: Retry wraps pure in-memory computation in DataCompletenessImpl

📄 openmetadata-service/src/main/java/org/openmetadata/service/governance/workflows/elements/nodes/automatedTask/impl/DataCompletenessImpl.java:63 📄 openmetadata-service/src/main/java/org/openmetadata/service/governance/workflows/elements/nodes/automatedTask/impl/DataCompletenessImpl.java:74-80

In DataCompletenessImpl (lines 74-80), calculateCompleteness() is wrapped with Retry.decorateSupplier(). However, calculateCompleteness is a pure in-memory computation over JsonUtils.getMap(entity) — it performs no I/O, no database access, and no network calls. Retrying it will always produce the same result (or the same exception). The retry adds overhead (creating Retry instance, wrapping supplier) with no benefit.

Other task impls (CheckEntityAttributes, SetEntityAttribute) correctly apply retry only around I/O operations like RuleEngine or EntityFieldUtils.

✅ 5 resolved
Edge Case: Inclusive gateway deadlocks when entity list is empty

📄 openmetadata-service/src/main/java/org/openmetadata/service/governance/workflows/elements/nodes/automatedTask/impl/CheckEntityAttributesImpl.java:73-76 📄 openmetadata-service/src/main/java/org/openmetadata/service/governance/workflows/elements/nodes/automatedTask/impl/CheckChangeDescriptionTaskImpl.java:81-84 📄 openmetadata-service/src/main/java/org/openmetadata/service/governance/workflows/flowable/MainWorkflow.java:74-87 📄 openmetadata-service/src/main/java/org/openmetadata/service/Entity.java:659-662 📄 openmetadata-service/src/main/java/org/openmetadata/service/governance/workflows/elements/nodes/automatedTask/impl/CheckEntityAttributesImpl.java:45-46 📄 openmetadata-service/src/main/java/org/openmetadata/service/governance/workflows/elements/nodes/automatedTask/impl/CheckChangeDescriptionTaskImpl.java:49-50
When a check node (CheckEntityAttributesImpl, CheckChangeDescriptionTaskImpl) receives an empty entity list, both HAS_TRUE_ENTITIES_VARIABLE and HAS_FALSE_ENTITIES_VARIABLE are set to false. The split inclusive gateway then has no condition evaluating to true, so no outgoing branch activates. In Flowable, an inclusive gateway with zero active outgoing flows causes the process token to stall permanently — the workflow instance never completes.

This can happen if: (1) a conditional entity list from an upstream node is empty (all entities went to the other branch), (2) entities were deleted between trigger and task execution, or (3) getEntityList() returns an empty list due to missing variables.

The same issue applies to DataCompletenessImpl where all band flags could be false if all entities fail processing.

Consider adding a default flow to the split inclusive gateway, or guarding against empty entity lists before the gateway is reached (e.g., skip the node entirely if the list is empty).

Edge Case: RollbackEntityImpl silently continues on total failure

📄 openmetadata-service/src/main/java/org/openmetadata/service/governance/workflows/elements/nodes/automatedTask/impl/RollbackEntityImpl.java:91-101
When all entities fail in RollbackEntityImpl, the processingStatus is set to "failure" and the EXCEPTION_VARIABLE is populated, but no BpmnError is thrown. The task completes normally even though every entity failed to rollback. This means the workflow continues to downstream nodes as if rollback succeeded.

By contrast, the outer catch block (line 103-109) does throw BpmnError for unexpected exceptions. Consider throwing BpmnError (or at least setting the failure variable) when failedEntities.size() == entityList.size() to halt the workflow on complete failure.

Edge Case: Null guard on getQualityBands() but not on getConfig()

📄 openmetadata-service/src/main/java/org/openmetadata/service/governance/workflows/elements/nodes/automatedTask/DataCompletenessTask.java:44
Line 44 wraps getQualityBands() with Optional.ofNullable() to handle a null band list, but getConfig() in the same call chain is not guarded. If getConfig() ever returns null, this will throw an NPE before the Optional even kicks in. While the JSON schema marks config as required (reducing the risk), other task nodes like CheckChangeDescriptionTask defensively null-check getConfig() as well. For consistency and resilience, the entire chain should be guarded.

Edge Case: Split gateway wires unconditional edges with null condition (always-true)

📄 openmetadata-service/src/main/java/org/openmetadata/service/governance/workflows/flowable/MainWorkflow.java:83-89 📄 openmetadata-service/src/main/java/org/openmetadata/service/governance/workflows/flowable/MainWorkflow.java:237-239 📄 openmetadata-service/src/main/java/org/openmetadata/service/governance/workflows/flowable/MainWorkflow.java:77-89 📄 openmetadata-service/src/main/java/org/openmetadata/service/governance/workflows/flowable/MainWorkflow.java:101-109
In MainWorkflow, when a node is detected as a split point (≥2 conditional edges + ≥2 output ports), the loop at lines 83-89 iterates over all outgoing edges from that node — not just the conditional ones. If a split node has a mix of conditional and unconditional edges (e.g., 2 conditional + 1 unconditional), buildGatewayCondition() returns null for the unconditional edge (line 239), and SequenceFlow.setConditionExpression(null) makes Flowable treat it as an always-true/default flow.

This means the unconditional branch would always fire from the inclusive gateway regardless of the batch results, which is likely unintended. While current check nodes only produce conditional edges, this is a latent bug that will surface if any future node type mixes conditional and unconditional outgoing edges from a split point.

Edge Case: Empty qualityBands bypasses all band flags → gateway deadlock

📄 openmetadata-service/src/main/java/org/openmetadata/service/governance/workflows/elements/nodes/automatedTask/impl/DataCompletenessImpl.java:132-137 📄 openmetadata-service/src/main/java/org/openmetadata/service/governance/workflows/elements/nodes/automatedTask/DataCompletenessTask.java:43-49
In DataCompletenessImpl, the fallback logic (lines 132-137) calls qualityBands.stream().min(...) to activate the lowest-score band when no entities match any band. If qualityBands is empty (e.g., config deserialization yields an empty list), .min() returns Optional.empty() and ifPresent is a no-op — no band flag is set to true. The inclusive split gateway would then have zero active branches, causing a Flowable deadlock.

The JSON schema specifies minItems: 1, but this isn't enforced at runtime. While this is an unlikely edge case due to the schema constraint, a defensive check or early validation would prevent a hard-to-debug deadlock.

🤖 Prompt for agents
Code Review: Auto-inject inclusive gateways feature adds batch routing logic but requires fixes: retry logic unnecessarily wraps in-memory computation in DataCompletenessImpl, split gateway detection ignores unconditional edges with null conditions (causing incorrect routing), and empty qualityBands fallback is non-functional. Five related issues were addressed but three remain open.

1. 💡 Performance: Retry wraps pure in-memory computation in DataCompletenessImpl
   Files: openmetadata-service/src/main/java/org/openmetadata/service/governance/workflows/elements/nodes/automatedTask/impl/DataCompletenessImpl.java:63, openmetadata-service/src/main/java/org/openmetadata/service/governance/workflows/elements/nodes/automatedTask/impl/DataCompletenessImpl.java:74-80

   In DataCompletenessImpl (lines 74-80), `calculateCompleteness()` is wrapped with `Retry.decorateSupplier()`. However, `calculateCompleteness` is a pure in-memory computation over `JsonUtils.getMap(entity)` — it performs no I/O, no database access, and no network calls. Retrying it will always produce the same result (or the same exception). The retry adds overhead (creating Retry instance, wrapping supplier) with no benefit.
   
   Other task impls (CheckEntityAttributes, SetEntityAttribute) correctly apply retry only around I/O operations like RuleEngine or EntityFieldUtils.

Options

Display: compact → Showing less information.

Comment with these commands to change:

Compact
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented May 5, 2026

@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented May 5, 2026

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