Skip to content

[Fix-17854] [Worker&SQL Task] Fix SQL task query result alert not being sent - #18549

Open
njnu-seafish wants to merge 65 commits into
apache:devfrom
njnu-seafish:Fix-17854-2
Open

njnu-seafish wants to merge 65 commits into
apache:devfrom
njnu-seafish:Fix-17854-2

Conversation

@njnu-seafish

Copy link
Copy Markdown
Contributor

Was this PR generated or assisted by AI?

Yes, I design the architecture and write the core code myself, then use an LLM to review and optimize the logic.

Purpose of the pull request

close #17854

Brief change log

Purpose

Fix #17854: the SQL task "query result" alert feature silently stopped working after the
task-executor refactoring (DSIP-73). The alert flag and payload (needAlert /
taskAlertInfo) used to live on AbstractTask, but no component consumed them anymore,
so enabling "Send Alert" on a SQL task had no effect.

Root cause

The needAlert / taskAlertInfo fields were only defined and set in the task plugin
(AbstractTask), while the Master never read them. After the task-executor module
refactor, the success lifecycle event did not carry the alert information to the Master,
so the alert was never persisted/sent.

What changed

  • Task plugin side

    • Moved needAlert / taskAlertInfo from AbstractTask into TaskExecutionContext
      so they can be carried across the Worker -> Master RPC.
    • SqlTask: prepare the alert info (title, alertGroupId, AlertType.TASK_RESULT)
      and truncate the query result to displayRows (default if unset) to avoid oversized
      RPC payloads; empty result sets are also covered.
    • Renamed the SQL task parameter sendEmail to sendAlert (kept @JsonAlias("sendEmail")
      for backward-compatible deserialization) and removed the obsolete showType field.
  • Event / Master

    • TaskExecutorSuccessLifecycleEvent now carries needAlert and taskAlertInfo.
    • TaskExecutorEventListenerImpl consumes the success event: when needAlert is true
      and a valid alertGroupId is present, it delegates to WorkflowAlertManager.sendTaskResultAlert
      (with project / workflow / task context filled in); otherwise it logs a warning instead
      of silently dropping the alert.
  • Alert chain

    • Added AlertType.TASK_RESULT (8).
    • AlertSendRequest now carries AlertType instead of a plain int warnType;
      AlertSender.syncHandler and AlertOperatorImpl propagate it into AlertData.
  • Data migration & docs

    • Upgrade DML for MySQL / PostgreSQL migrates sendEmail -> sendAlert in
      t_ds_task_definition and t_ds_task_definition_log (null-safe guards added).
    • Documented the incompatible change in incompatible.md (en/zh).

Verification

  • Unit tests added/updated:
    • SqlParametersTest: JSON backward compatibility (sendEmail -> sendAlert) and
      new field name.
    • AlertSenderTest: syncHandler with the new AlertType argument.
  • Local build of the touched modules passes (mvn compile).

I previously submitted a PR proposing that the Worker role should directly send RPC requests to the Master to transmit SQL result set alerts. The proposal was rejected. (#17856)

Verify this pull request

This pull request is code cleanup without any test coverage.

(or)

This pull request is already covered by existing tests, such as (please describe tests).

(or)

This change added tests and can be verified as follows:

(or)

Pull Request Notice

Pull Request Notice

If your pull request contains incompatible change, you should also add it to docs/docs/en/guide/upgrade/incompatible.md

@njnu-seafish

Copy link
Copy Markdown
Contributor Author

@SbloodyS #17854 This issue was automatically closed by the bot due to inactivity. Could a maintainer please reopen it? I've just submitted a more reasonable solution to fix the SQL query task result alerting issue. Thanks so much!

@github-actions github-actions Bot added UI ui and front end related backend test document labels Aug 12, 2026
@SbloodyS SbloodyS changed the title [Bug-17854] [Worker&SQL Task] Fix SQL task query result alert not being sent [Fix-17854] [Worker&SQL Task] Fix SQL task query result alert not being sent Aug 12, 2026
@SbloodyS SbloodyS added the bug Something isn't working label Aug 12, 2026
@SbloodyS SbloodyS added this to the 3.5.0 milestone Aug 12, 2026
Comment thread docs/docs/en/guide/upgrade/incompatible.md Outdated
@njnu-seafish
njnu-seafish requested a review from SbloodyS August 13, 2026 09:16

@SbloodyS SbloodyS left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Preserve the existing sendEmail field name

SqlParameters renames the persisted/API field from sendEmail to sendAlert, and the UI now only reads/writes sendAlert.

Although @JsonAlias("sendEmail") keeps deserialization compatible, serialization and UI payloads use the new name. This can cause existing clients, SDKs, integrations, or mixed-version components to silently lose the setting. It also introduces an unnecessary database migration and an incompatible public contract change.

Please keep sendEmail as the field name and only update the semantic description/UI label to indicate that alerts can use channels other than email. If the rename is still required, please provide an explicit compatibility strategy covering API clients, UI loading of legacy definitions, and rolling upgrades.

Keep AlertSendRequest wire-compatible

AlertSendRequest changes warnType: int to alertType: AlertType.

This changes both the field name and the serialized type of the Master–Alert RPC request. During a rolling upgrade, an old Alert Server will still expect warnType, while a new Alert Server may receive a request without alertType from an old Master. The result can be a default/incorrect alert type or a NullPointerException at alertType.getCode().

Please retain the existing warnType field for compatibility, or support both fields with explicit conversion and add a mixed-version serialization test.

Comment on lines +61 to +75
<insert id="insertTaskResultAlertIfAbsent">
INSERT INTO t_ds_alert(sign, title, content, alert_status, warning_type, log, alertgroup_id,
create_time, update_time, project_code, workflow_definition_code,
workflow_instance_id, alert_type)
SELECT #{alert.sign}, #{alert.title}, #{alert.content}, #{alert.alertStatus.code},
#{alert.warningType.code}, #{alert.log}, #{alert.alertGroupId}, #{alert.createTime},
#{alert.updateTime}, #{alert.projectCode}, #{alert.workflowDefinitionCode},
#{alert.workflowInstanceId}, #{alert.alertType.code}
WHERE NOT EXISTS (
SELECT 1 FROM t_ds_alert
WHERE sign = #{alert.sign}
AND workflow_instance_id = #{alert.workflowInstanceId}
AND alert_type = #{alert.alertType.code}
)
</insert>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Such logic should not be handled in the database, but should be handled by code.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Such logic should not be handled in the database, but should be handled by code.

Thanks for the feedback. I've moved the dedup logic out of SQL and into application code.

The INSERT ... SELECT ... WHERE NOT EXISTS statement has been replaced with a plain INSERT ... VALUES. Idempotency is now handled entirely in code: AlertDao#addTaskResultAlert performs a direct insert and catches DuplicateKeyException (thrown by the uk_alert_dedup unique constraint) to treat the duplicate as a skip. The unique constraint remains as the DB-level safety net, but no dedup logic lives in the SQL itself.

Comment on lines +22 to +27
DELETE FROM t_ds_alert a
USING t_ds_alert b
WHERE a.id < b.id
AND a.sign = b.sign
AND a.workflow_instance_id = b.workflow_instance_id
AND a.alert_type = b.alert_type;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Deleting user data is a very dangerous action, and we should find a better compatible way.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Deleting user data is a very dangerous action, and we should find a better compatible way.

done.
The upgrade now only adds the unique index without touching any existing data. If duplicate rows already exist, the index creation will fail — the user can review and resolve them manually.

Comment on lines +18 to +21
-- Enforce idempotent task-result alerts at the database level.
-- Allows INSERT IGNORE (MySQL) / ON CONFLICT DO NOTHING (PostgreSQL) to atomically
-- prevent duplicates without check-then-insert race conditions.
-- Clean up any existing duplicate rows before adding the unique constraint.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

You should check for yourself to avoid invalid comments generated by AI.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

You should check for yourself to avoid invalid comments generated by AI.

sorry, it have been simplified to a single line.

@njnu-seafish

Copy link
Copy Markdown
Contributor Author

@SbloodyS When you have a moment, could you please review the code again? Thanks so much!

@SbloodyS SbloodyS left a comment •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Keep the log display limit separate from alert content.

In SqlTask.java, lines 337–344, displayRows now truncates the notification payload. The UI labels this setting “Log display,” and the previous implementation passed the complete result to the alert. With 50 result rows and displayRows=10, recipients now receive only 10 rows without any truncation notice. Please preserve the full alert result or introduce a separate, explicit alert limit.

The compatibility bridge ignores the retained protected fields.

In AbstractTask.java, lines 133–164, getters and setters use only taskRequest, while needAlert and taskAlertInfo remain independently writable protected fields. An existing subclass that assigns these fields directly now gets false/null from the getters, and its alert data never reaches the success event. Please synchronize legacy field writes into the context before reporting task completion.

@njnu-seafish

Copy link
Copy Markdown
Contributor Author

Keep the log display limit separate from alert content.

In SqlTask.java, lines 337–344, displayRows now truncates the notification payload. The UI labels this setting “Log display,” and the previous implementation passed the complete result to the alert. With 50 result rows and displayRows=10, recipients now receive only 10 rows without any truncation notice. Please preserve the full alert result or introduce a separate, explicit alert limit.

The compatibility bridge ignores the retained protected fields.

In AbstractTask.java, lines 133–164, getters and setters use only taskRequest, while needAlert and taskAlertInfo remain independently writable protected fields. An existing subclass that assigns these fields directly now gets false/null from the getters, and its alert data never reaches the success event. Please synchronize legacy field writes into the context before reporting task completion.

Thanks for the review — both points are valid and have been fixed in commit 004a7c5f0.

  1. Keep the log display limit separate from alert content

Agreed. displayRows is only a log-display setting, so reusing it as the alert payload limit was a silent behavior change. The truncation has been removed from prepareTaskResultAlert: the alert now carries the complete query result (empty result sets still get a placeholder row, as before). The payload stays bounded by the query limit (QUERY_LIMIT, default 10000).

  1. Compatibility bridge ignores the retained protected fields

The @deprecated getters/setters in AbstractTask now merge the legacy fields with the context: getNeedAlert() returns the field OR the context value, getTaskAlertInfo() prefers the field and falls back to the context, and both setters write through to both locations. On the executor side, PhysicalTaskExecutor.doTrackTaskPluginStatus() synchronizes these legacy values into the TaskExecutionContext once the task reaches SUCCEEDED — right before the success lifecycle event is built — so alert info written by third-party plugins via direct field assignment is no longer lost.

Tests added/updated: AbstractTaskTest covers direct field assignment, setter write-through, and context fallback; SqlTaskTest now asserts the alert content keeps all rows regardless of displayRows. All tests pass (AbstractTaskTest 6/6, SqlTaskTest 27/27).

// Synchronize the legacy needAlert/taskAlertInfo fields written directly
// by AbstractTask subclasses into the context, so that the success
// lifecycle event carries the alert info of third-party plugins.
taskExecutionContext.setNeedAlert(physicalTask.getNeedAlert());
// by AbstractTask subclasses into the context, so that the success
// lifecycle event carries the alert info of third-party plugins.
taskExecutionContext.setNeedAlert(physicalTask.getNeedAlert());
taskExecutionContext.setTaskAlertInfo(physicalTask.getTaskAlertInfo());

@SbloodyS SbloodyS left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Do not persist result alerts for rejected success events

TaskSuccessLifecycleEventHandler.java:56–60 checks only needAlert after calling onSucceedEvent(). However, TaskPauseStateAction and TaskKillStateAction merely log and ignore a late success event; they return normally without transitioning the task to SUCCESS. The handler still persists a TASK_RESULT alert for the paused/killed task.

Gate alert persistence on the effective SUCCESS state, while preserving idempotent handling of repeated success events. Add handler-level regression tests for rejected late success events and accepted/repeated success events; the current SQL preparation and DAO tests do not cover this behavior.

@njnu-seafish
njnu-seafish requested a review from SbloodyS October 8, 2026 06:07
@njnu-seafish

Copy link
Copy Markdown
Contributor Author

Do not persist result alerts for rejected success events

TaskSuccessLifecycleEventHandler.java:56–60 checks only needAlert after calling onSucceedEvent(). However, TaskPauseStateAction and TaskKillStateAction merely log and ignore a late success event; they return normally without transitioning the task to SUCCESS. The handler still persists a TASK_RESULT alert for the paused/killed task.

Gate alert persistence on the effective SUCCESS state, while preserving idempotent handling of repeated success events. Add handler-level regression tests for rejected late success events and accepted/repeated success events; the current SQL preparation and DAO tests do not cover this behavior.

Good catch. The success-event handler now gates alert persistence on the effective task state, fixed in 15c18bd.

  1. Gate alert persistence on the effective SUCCESS state — TaskSuccessLifecycleEventHandler now checks taskExecution.getTaskInstance().getState() == SUCCESS before calling sendTaskResultAlert(). Since TaskPauseStateAction / TaskKillStateAction keep the task in PAUSE/KILL on a late success event, the TASK_RESULT alert is no longer persisted for paused/killed tasks.

  2. Preserve idempotent handling of repeated success events — a repeated success event on an already-SUCCESS task still takes the send path; deduplication stays at the DB level via the uk_alert_dedup unique constraint, so no duplicate alert is persisted and the event is acked as before.

  3. Handler-level regression tests — added TaskSuccessLifecycleEventHandlerTest (5 cases): accepted success event sends the alert; late success events on a paused task and on a killed task send none (verifyNoInteractions); a repeated success event still sends and acks; and needAlert=false sends none. All tests pass, and a mutation check (reversing the SUCCESS gate) turns 4/5 red, confirming the tests lock the behavior.


When performing a rolling upgrade, **the Alert Server must be upgraded before Master/Worker**.

Starting from 3.5.0, the Master may persist new `alert_type` enum values (e.g. `TASK_RESULT`) into the `t_ds_alert` table. If the Alert Server has not yet been upgraded to a version that includes the new enum value, MyBatis will deserialize the unknown value as `null`, causing `AlertSender` to throw a `NullPointerException` while building the alert data. The alert will remain stuck in `WAIT_EXECUTION` and never be delivered.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

If the upgrade fails, users need to be provided with specific operation steps.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

If the upgrade fails, users need to be provided with specific operation steps.

Thanks for the feedback. Fixed in be65492 — the upgrade doc now provides concrete recovery steps for a failed upgrade.

The new "If the upgrade fails" section (after the Rolling Upgrade Order) covers the case where Master/Worker were already upgraded before the Alert Server and new alert_type values were persisted to t_ds_alert:

Upgrade the Alert Server to a version that includes the new enum values and restart it — alerts stuck in WAIT_EXECUTION will be picked up and delivered normally.
If the Alert Server cannot be upgraded immediately, stop the old Alert Server first, then back up and clean up the affected rows (create table t_ds_alert_bak as select * from t_ds_alert where alert_type = 8; followed by delete from t_ds_alert where alert_type = 8;), so they cannot keep blocking alert delivery.

The Chinese upgrade doc is updated in sync.

@njnu-seafish
njnu-seafish requested a review from SbloodyS October 8, 2026 07:20

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend bug Something isn't working document test

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] [Worker&SQL Task] In the SQL task type, when querying data, the configured alert did not take effect.

3 participants