Skip to content

wip: feat(engine): dags-as-durable-tasks#4266

Draft
mrkaye97 wants to merge 81 commits into
belanger/operatorsfrom
mk/durable-tasks-as-operator
Draft

wip: feat(engine): dags-as-durable-tasks#4266
mrkaye97 wants to merge 81 commits into
belanger/operatorsfrom
mk/durable-tasks-as-operator

Conversation

@mrkaye97

@mrkaye97 mrkaye97 commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Description

WIP

Initial implementation of the dag operator, which basically creates an internal durable task that processes the dag via child spawning

to do:

  • render preview of dag even if nothing has run yet in the dash
  • make sure all the existing apis work (check everything that returns dags)
  • write more tests for all the different cases of conditions, skipping behavior, cancelling behavior, and concurrency
  • make sure that a dag that's registered with the flag enabled still works as expected after the flag is disabled, and vice versa
  • figure out what happens to running dags when this ships
  • fix the dashboard / runs list api's pagination when we're listing runs that include dag orchestrators (since they and their children get collapsed)
  • add link from task to orchestrator run in the dashboard like we have for dags
  • figure out how to enable replaying from a specific place in the DAG
  • trace view is broken since it tries to fetch the dag run by its external id
  • Make sure the workflow_run_id for the dag children correspond's to the durable task external id
  • There's a bug (need to reproduce) where if the first task in a dag has a wait_for (sleep), we get a bunch of "could not send task to worker'

Fixes # (issue)

Type of change

  • New feature (non-breaking change which adds functionality)

What's Changed

  • Add a list of tasks or features here...

Checklist

Changes have been:

  • Tested (unit, integration, or manually with steps specified)
  • Linted and formatted
  • Documented (where applicable)
  • Added to CHANGELOG (where applicable) -- see Keep a Changelog

🤖 AI Disclosure
  • I acknowledge that an LLM was used in the creation of this Pull Request, in accordance with Hatchet's AI_POLICY.md.
  • Details: [e.g. generating tests, writing docs]

@vercel

vercel Bot commented Jun 23, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
hatchet-docs Ready Ready Preview, Comment Jul 13, 2026 4:16pm

Request Review

@github-actions github-actions Bot added the engine Related to the core Hatchet engine label Jun 23, 2026
@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Optional test failure: The load-online-migrate job failed on this PR. This check is non-mandatory and does not block merging, but may be worth investigating. View logs

@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Optional test failure: The load-deadlock job failed on this PR (optimistic-scheduling=false). This check is non-mandatory and does not block merging, but may be worth investigating. View logs

@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Optional test failure: The load-deadlock job failed on this PR (optimistic-scheduling=true). This check is non-mandatory and does not block merging, but may be worth investigating. View logs

@github-actions github-actions Bot added the dashboard Related to the Hatchet dashboard label Jun 24, 2026
@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Optional test failure: The load-online-migrate job failed on this PR. This check is non-mandatory and does not block merging, but may be worth investigating. View logs

@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Optional test failure: The load-deadlock job failed on this PR (optimistic-scheduling=true). This check is non-mandatory and does not block merging, but may be worth investigating. View logs

@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Optional test failure: The load-deadlock job failed on this PR (optimistic-scheduling=false). This check is non-mandatory and does not block merging, but may be worth investigating. View logs

@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Optional test failure: The load-online-migrate job failed on this PR. This check is non-mandatory and does not block merging, but may be worth investigating. View logs

@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Optional test failure: The load-deadlock job failed on this PR (optimistic-scheduling=true). This check is non-mandatory and does not block merging, but may be worth investigating. View logs

@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Optional test failure: The old-engine-new-sdk (Python) job failed on this PR. This check is non-mandatory and does not block merging, but may be worth investigating. View logs

@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Optional test failure: The load-online-migrate job failed on this PR. This check is non-mandatory and does not block merging, but may be worth investigating. View logs

@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Optional test failure: The load-deadlock job failed on this PR (optimistic-scheduling=false). This check is non-mandatory and does not block merging, but may be worth investigating. View logs

@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Optional test failure: The load-deadlock job failed on this PR (optimistic-scheduling=true). This check is non-mandatory and does not block merging, but may be worth investigating. View logs

@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Optional test failure: The load-online-migrate job failed on this PR. This check is non-mandatory and does not block merging, but may be worth investigating. View logs

@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Optional test failure: The load-deadlock job failed on this PR (optimistic-scheduling=true). This check is non-mandatory and does not block merging, but may be worth investigating. View logs

@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Optional test failure: The old-engine-new-sdk (Python) job failed on this PR. This check is non-mandatory and does not block merging, but may be worth investigating. View logs

@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Optional test failure: The old-engine-new-sdk (Python) job failed on this PR. This check is non-mandatory and does not block merging, but may be worth investigating. View logs

@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Optional test failure: The load-online-migrate job failed on this PR. This check is non-mandatory and does not block merging, but may be worth investigating. View logs

@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Optional test failure: The old-engine-new-sdk (Python) job failed on this PR. This check is non-mandatory and does not block merging, but may be worth investigating. View logs

@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Optional test failure: The load-deadlock job failed on this PR (optimistic-scheduling=false). This check is non-mandatory and does not block merging, but may be worth investigating. View logs

@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Optional test failure: The load-online-migrate job failed on this PR. This check is non-mandatory and does not block merging, but may be worth investigating. View logs

),
cell: ({ row }) => {
if (row.getCanExpand()) {
if (row.original.type === 'DAG') {

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.

this lets us open the workflow run detail page for an operator DAG before any of the tasks have actually been spawned

[taskRuns],
() =>
taskRuns
.filter((t) => t.isDurable && t.taskExternalId !== id)

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.

there's a bunch of stuff like this - basically removing the orchestrator from the ids

Comment on lines +408 to +410
if e.isDagOrchestrator {
continue
}

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.

don't want to render spans for the orchestrator task since it messes up nesting and is "internal" / not user facing

d.l.Warn().Ctx(ctx).Err(err).Msg("failed to unmarshal input")
continue
if len(currInput.DagParentTaskRunIds) > 0 {
dagParentOutputs, err := d.repov1.Tasks().GetDagParentOutputs(ctx, tenantId, currInput.DagParentTaskRunIds)

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.

not sure what to do about this - seems very suboptimal to need a db lookup here, will circle back

continue
}
} else {
childEvents, err := s.repov1.Tasks().ListDurableOrchestratorChildOutputEvents(ctx, tenantId, event.TaskExternalId)

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.

same here - really not ideal to need the db lookup, will circle back

return nil
}

func (d *DispatcherServiceImpl) TriggerDAGStep(ctx context.Context, tenantId uuid.UUID, req *operator.DAGStepTriggerRequest) (*operator.DAGStepTriggerResult, error) {

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.

this is one of the important methods, since it's what gets called by the operator to trigger tasks

Comment on lines +1283 to +1297
TriggerTaskData: &v1.TriggerTaskData{
WorkflowName: req.WorkflowName,
TargetActionId: &req.ActionId,
UserMessage: &stepLabel,
Data: []byte(req.Input),
AdditionalMetadata: task.AdditionalMetadata,
ParentExternalId: &task.ExternalID,
ParentTaskId: &task.ID,
ParentTaskInsertedAt: &task.InsertedAt.Time,
ChildIndex: &childIndex,
DagParentTaskRunIds: req.DagParentTaskRunIds,
IsSkipped: req.IsSkipped,
IsCancelled: req.IsCancelled,
DesiredWorkerLabels: req.DesiredWorkerLabels,
WorkflowRunId: &orchestratorWorkflowRunId,

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.

important stuff here:

  1. we basically pass most things along (e.g. the metadata, the input, etc.)
  2. we tie the workflow run id manually to the orchestrator's workflow run id

if t.output != nil {
b, err := json.Marshal(t.output)
if err == nil {
output[t.readableId] = json.RawMessage(b)

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.

this results in a DAG output that maps step ids to outputs, similar to what we see in the dash and over the API

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

Labels

dashboard Related to the Hatchet dashboard documentation Improvements or additions to documentation engine Related to the core Hatchet engine sdk-py Related to the Python sdk

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant