Skip to content

Fix never-converging job diff on reordered webhook notifications - #6060

Merged
radakam merged 4 commits into
mainfrom
fix-job-webhook-notification-order
Jul 30, 2026
Merged

Fix never-converging job diff on reordered webhook notifications#6060
radakam merged 4 commits into
mainfrom
fix-job-webhook-notification-order

Conversation

@radakam

@radakamradakam commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Summary

The Jobs API treats webhook_notifications.on_* destination lists as unordered sets and may return them in a different order than submitted. The direct engine diffed these lists positionally (structdiff with no key func), so a stable server-side reorder produced a phantom diff that bundle plan / bundle deploy could never converge past (1 to change forever). This reproduces after bundle deployment migrate, and on any direct deploy where the remote returns a different-but-valid permutation.

Fix

Register the job-, task-, and for_each_task-level webhook_notifications.on_* paths in ResourceJob.KeyedSlices(), keyed by id, so the lists are compared as sets rather than by index — the same pattern already used for depends_on.

Tests

  • Acceptance test jobs/webhook-reorder-remote: deploys a job, reorders the remote lists via edit_resource.py, and asserts bundle plan reports 0 to change. Verified it fails (1 to change) with the fix removed.
  • Unit test covering the job, task, and nested for_each_task webhook paths.

The Jobs API treats webhook_notifications.on_* lists as unordered sets
and may return them in a different order than submitted. The direct
engine compared these lists positionally (structdiff with no key func),
so a stable server-side reordering produced a phantom diff that
"bundle plan"/"bundle deploy" could never converge past.
Add the job-, task-, and for_each-task-level webhook_notifications.on_*
paths to ResourceJob.KeyedSlices(), keyed by id, so the diff is
order-insensitive. This matches the existing pattern for depends_on.
Comment threadbundle/direct/dresources/job_test.go
Deploys a job with webhook_notifications.on_* destinations, then uses
edit_resource.py to reorder the remote lists (simulating the Jobs API
returning them in a different order) and asserts bundle plan reports
0 to change. Without the KeyedSlices fix this plan shows 1 to change.
@eng-dev-ecosystem-bot

eng-dev-ecosystem-bot commented Jul 24, 2026

Copy link
Copy Markdown
Collaborator

Integration test report

Commit: 9b1131e

Run: 30525878075

Env💚​RECOVERED🙈​SKIP✅​pass🙈​skipTime
💚​aws linux4432210664:30
💚​aws windows4432410646:19
💚​azure linux4432210654:59
💚​azure windows4432410634:26
💚​gcp linux1532110674:18
💚​gcp windows1532310655:14
8 interesting tests: 4 RECOVERED, 4 SKIP
Test Nameaws linuxaws windowsazure linuxazure windowsgcp linuxgcp windows
💚​TestAccept💚​R💚​R💚​R💚​R💚​R💚​R
🙈​TestAccept/bundle/invariant/no_drift🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S
🙈​TestAccept/bundle/resources/vector_search_endpoints/drift/recreated_same_name🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S
🙈​TestAccept/bundle/resources/vector_search_indexes/recreate/embedding_dimension🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S
🙈​TestAccept/ssh/connection🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S
💚​TestFetchRepositoryInfoAPI_FromRepo💚​R💚​R💚​R💚​R🙈​S🙈​S
💚​TestFetchRepositoryInfoAPI_FromRepo/root💚​R💚​R💚​R💚​R
💚​TestFetchRepositoryInfoAPI_FromRepo/subdir💚​R💚​R💚​R💚​R
Top 3 slowest tests (at least 2 minutes):
durationenvtestname
5:09aws windowsTestAccept
3:18gcp windowsTestAccept
3:06azure windowsTestAccept

@radakam
radakam marked this pull request as ready for review July 24, 2026 11:04
@radakam
radakam requested a review from denikJuly 24, 2026 11:49

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

Could you add changelog entry?

@radakam
radakam enabled auto-merge July 30, 2026 08:13
@radakam
radakam added this pull request to the merge queueJul 30, 2026
Merged via the queue into main with commit f496c89Jul 30, 2026
24 checks passed
@radakam
radakam deleted the fix-job-webhook-notification-order branch July 30, 2026 08:51
@eng-dev-ecosystem-bot

Copy link
Copy Markdown
Collaborator

Integration test report

Commit: f496c89

Run: 30528359966

Env❌​FAIL🟨​KNOWN🔄​flaky💚​RECOVERED🙈​SKIP✅​pass🙈​skipTime
❌​aws linux1012521144885225:35
❌​aws windows101521085904242:24
💚​azure linux621051921174:40
🔄​azure windows352988940201:24
💚​gcp linux331039927198:15
💚​gcp windows33978946209:29
22 interesting tests: 10 FAIL, 5 RECOVERED, 4 flaky, 2 SKIP, 1 KNOWN
Test Nameaws linuxaws windowsazure linuxazure windowsgcp linuxgcp windows
🟨​TestAccept🟨​K🟨​K💚​R🔄​f💚​R💚​R
🔄​TestAccept/bundle/invariant/delete_idempotent✅​p✅​p✅​p🔄​f✅​p✅​p
🔄​TestAccept/bundle/invariant/delete_idempotent/DATABRICKS_BUNDLE_ENGINE=direct/INPUT_CONFIG=cluster_apply_policy_default_values.yml.tmpl/READPLAN=✅​p✅​p✅​p🔄​f✅​p✅​p
💚​TestAccept/bundle/invariant/no_drift💚​R💚​R💚​R💚​R💚​R💚​R
🔄​TestAccept/bundle/resources/dashboards/change-name🔄​f✅​p✅​p✅​p✅​p✅​p
🔄​TestAccept/bundle/resources/dashboards/change-name/DATABRICKS_BUNDLE_ENGINE=terraform🔄​f✅​p✅​p✅​p✅​p✅​p
❌​TestAccept/bundle/resources/postgres_branches/update_protected❌​F❌​F🙈​s🙈​s🙈​s🙈​s
❌​TestAccept/bundle/resources/postgres_branches/update_protected/DATABRICKS_BUNDLE_ENGINE=direct❌​F❌​F
❌​TestAccept/bundle/resources/postgres_databases/update❌​F❌​F🙈​s🙈​s🙈​s🙈​s
❌​TestAccept/bundle/resources/postgres_databases/update/DATABRICKS_BUNDLE_ENGINE=direct❌​F❌​F
❌​TestAccept/bundle/resources/postgres_endpoints/update_autoscaling❌​F❌​F🙈​s🙈​s🙈​s🙈​s
❌​TestAccept/bundle/resources/postgres_endpoints/update_autoscaling/DATABRICKS_BUNDLE_ENGINE=direct❌​F❌​F
❌​TestAccept/bundle/resources/postgres_projects/update_display_name❌​F❌​F🙈​s🙈​s🙈​s🙈​s
❌​TestAccept/bundle/resources/postgres_projects/update_display_name/DATABRICKS_BUNDLE_ENGINE=direct❌​F❌​F
❌​TestAccept/bundle/resources/postgres_roles/update❌​F❌​F🙈​s🙈​s🙈​s🙈​s
❌​TestAccept/bundle/resources/postgres_roles/update/DATABRICKS_BUNDLE_ENGINE=direct❌​F❌​F
💚​TestAccept/bundle/resources/vector_search_endpoints/drift/recreated_same_name💚​R💚​R💚​R💚​R💚​R💚​R
🙈​TestAccept/bundle/resources/vector_search_indexes/recreate/embedding_dimension🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S
🙈​TestAccept/ssh/connection🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S
💚​TestFetchRepositoryInfoAPI_FromRepo💚​R💚​R💚​R💚​R🙈​S🙈​S
💚​TestFetchRepositoryInfoAPI_FromRepo/root💚​R💚​R💚​R💚​R
💚​TestFetchRepositoryInfoAPI_FromRepo/subdir💚​R💚​R💚​R💚​R
Top 50 slowest tests (at least 2 minutes):
durationenvtestname
13:18gcp linuxTestAccept/bundle/resources/apps/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
13:04gcp windowsTestAccept/bundle/resources/apps/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
12:59aws linuxTestAccept/bundle/config-remote-sync/multiple_resources/DATABRICKS_BUNDLE_ENGINE=terraform
12:07azure windowsTestAccept/bundle/resources/clusters/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
10:45gcp linuxTestAccept/bundle/resources/clusters/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
9:47aws windowsTestAccept/bundle/resources/clusters/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
9:01azure windowsTestAccept/bundle/resources/clusters/lifecycle-started-toggle/DATABRICKS_BUNDLE_ENGINE=direct
8:57aws windowsTestAccept/bundle/resources/clusters/lifecycle-started-toggle/DATABRICKS_BUNDLE_ENGINE=direct
8:54aws windowsTestAccept/bundle/resources/apps/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
8:45aws linuxTestAccept/bundle/resources/apps/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
8:26aws windowsTestAccept/bundle/invariant/delete_idempotent/DATABRICKS_BUNDLE_ENGINE=direct/INPUT_CONFIG=cluster.yml.tmpl/READPLAN=1
8:09azure windowsTestAccept/bundle/resources/apps/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
8:09aws linuxTestAccept/bundle/resources/clusters/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
8:09aws linuxTestAccept/bundle/resources/clusters/lifecycle-started-toggle/DATABRICKS_BUNDLE_ENGINE=direct
8:04gcp linuxTestAccept/bundle/resources/clusters/lifecycle-started-toggle/DATABRICKS_BUNDLE_ENGINE=direct
7:55azure linuxTestAccept/bundle/invariant/destroy_idempotent/DATABRICKS_BUNDLE_ENGINE=direct/INPUT_CONFIG=cluster.yml.tmpl/READPLAN=
7:53azure windowsTestAccept/bundle/resources/clusters/deploy/update-after-create/DATABRICKS_BUNDLE_ENGINE=terraform
7:37gcp linuxTestAccept/bundle/resources/clusters/deploy/simple/DATABRICKS_BUNDLE_ENGINE=direct
7:30aws linuxTestAccept/bundle/config-remote-sync/multiple_resources/DATABRICKS_BUNDLE_ENGINE=direct
7:28azure linuxTestAccept/bundle/config-remote-sync/multiple_resources/DATABRICKS_BUNDLE_ENGINE=terraform
7:26azure linuxTestAccept/bundle/resources/apps/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
7:17gcp windowsTestAccept/bundle/resources/clusters/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
7:01azure windowsTestAccept/bundle/invariant/destroy_idempotent/DATABRICKS_BUNDLE_ENGINE=direct/INPUT_CONFIG=cluster.yml.tmpl/READPLAN=
6:53gcp windowsTestAccept/bundle/resources/clusters/lifecycle-started-toggle/DATABRICKS_BUNDLE_ENGINE=direct
6:51azure linuxTestAccept/bundle/invariant/delete_idempotent/DATABRICKS_BUNDLE_ENGINE=direct/INPUT_CONFIG=cluster_apply_policy_default_values.yml.tmpl/READPLAN=
6:50azure windowsTestAccept/bundle/invariant/destroy_idempotent/DATABRICKS_BUNDLE_ENGINE=direct/INPUT_CONFIG=cluster_apply_policy_default_values.yml.tmpl/READPLAN=1
6:38gcp windowsTestAccept/bundle/templates/default-python/combinations/classic/DATABRICKS_BUNDLE_ENGINE=terraform/DLT=yes/NBOOK=no/PY=yes/READPLAN=
6:32gcp linuxTestAccept/bundle/config-remote-sync/multiple_resources/DATABRICKS_BUNDLE_ENGINE=direct
6:29gcp windowsTestAccept/bundle/resources/clusters/deploy/local_ssd_count/DATABRICKS_BUNDLE_ENGINE=direct
6:27azure linuxTestAccept/bundle/invariant/destroy_idempotent/DATABRICKS_BUNDLE_ENGINE=direct/INPUT_CONFIG=cluster_apply_policy_default_values.yml.tmpl/READPLAN=
6:24gcp linuxTestAccept/bundle/invariant/delete_idempotent/DATABRICKS_BUNDLE_ENGINE=direct/INPUT_CONFIG=cluster_apply_policy_default_values.yml.tmpl/READPLAN=1
6:24gcp linuxTestAccept/bundle/resources/clusters/deploy/local_ssd_count/DATABRICKS_BUNDLE_ENGINE=direct
6:21gcp windowsTestAccept/bundle/resources/apps/lifecycle-started-omitted/DATABRICKS_BUNDLE_ENGINE=direct
6:21azure linuxTestAccept/bundle/resources/clusters/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
6:12azure linuxTestAccept/bundle/invariant/delete_idempotent/DATABRICKS_BUNDLE_ENGINE=direct/INPUT_CONFIG=volume_uppercase_name.yml.tmpl/READPLAN=1
6:08gcp linuxTestAccept/bundle/deploy/spark-jar-task/DATABRICKS_BUNDLE_ENGINE=direct
5:58gcp linuxTestAccept/bundle/deploy/spark-jar-task/DATABRICKS_BUNDLE_ENGINE=terraform
5:53azure linuxTestAccept/bundle/deploy/spark-jar-task/DATABRICKS_BUNDLE_ENGINE=direct
5:45gcp linuxTestAccept/bundle/config-remote-sync/multiple_resources/DATABRICKS_BUNDLE_ENGINE=terraform
5:31gcp linuxTestAccept/bundle/resources/apps/lifecycle-started-omitted/DATABRICKS_BUNDLE_ENGINE=direct
5:31azure linuxTestAccept/bundle/resources/clusters/lifecycle-started-toggle/DATABRICKS_BUNDLE_ENGINE=direct
5:25aws linuxTestAccept/bundle/deploy/spark-jar-task/DATABRICKS_BUNDLE_ENGINE=terraform
5:19gcp windowsTestAccept/bundle/deploy/spark-jar-task/DATABRICKS_BUNDLE_ENGINE=terraform
5:18gcp windowsTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
5:16aws windowsTestAccept/bundle/deploy/spark-jar-task/DATABRICKS_BUNDLE_ENGINE=terraform
5:14gcp linuxTestAccept/bundle/invariant/no_drift/DATABRICKS_BUNDLE_ENGINE=direct/INPUT_CONFIG=cluster.yml.tmpl/READPLAN=1
5:03aws windowsTestAccept/bundle/invariant/no_drift/DATABRICKS_BUNDLE_ENGINE=direct/INPUT_CONFIG=cluster_apply_policy_default_values.yml.tmpl/READPLAN=1
5:01azure linuxTestAccept/bundle/deploy/spark-jar-task/DATABRICKS_BUNDLE_ENGINE=terraform
4:58gcp linuxTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
4:58gcp linuxTestAccept/bundle/resources/apps/lifecycle-started-toggle/DATABRICKS_BUNDLE_ENGINE=direct

deco-sdk-taggingBot added a commit that referenced this pull request Aug 6, 2026
## Release v1.11.0
### CLI
* Fixed `databricks repos get/update/delete` failing with `object at path "..." is not a repo` for Git-CLI-enabled folders (currently in preview), which the workspace API reports as directories rather than repos ([#6181](#6181)).
* Support `dbfs:/Skills/...` paths in `databricks fs` commands, routed to the Files API. ([#6147](#6147))
### Bundles
* For jobs where `ai_runtime_task.code_source_path` is a relative path to a local directory, the directory is now packaged into a tarball (honoring `.gitignore` and `sync.include`/`sync.exclude`), uploaded during deployment, and `code_source_path` is rewritten to the uploaded workspace path. ([#6110](#6110))
* Added JSON output to `bundle init`. Running `databricks bundle init <template> -o json` now reports the files the template wrote, relative to the output directory. This lets callers that pass `--output-dir` learn where the template materialized instead of assuming the output is a single directory named after the project. The default text output is unchanged. ([#6161](#6161))
* The terraform deployment engine is deprecated and will stop working in a future version of the CLI. Setting `bundle.engine: terraform` now emits a deprecation warning. See https://docs.databricks.com/aws/en/dev-tools/bundles/direct for how to migrate to the direct deployment engine. ([#6099](#6099))
* Fixed the direct deployment engine planning a spurious `create` for an empty `grants: []` list. Terraform records no grants resource for such a list, so `bundle plan` after `bundle deployment migrate` no longer reports an action for it. Emptying a previously deployed list still revokes the grants, after which the node is dropped from the deployment state instead of being reported as unchanged forever. ([#6039](#6039))
* Fixed `bundle generate` downloading notebooks found inside a folder without their file extension. They are now exported like top-level notebooks, so a Python notebook lands as `notebook.py` instead of an extensionless file ([#6144](#6144)).
* direct: `webhook_notifications.on_*` destinations on jobs, tasks, and `for_each_task` are now compared as unordered sets. Previously the Jobs API returning these lists in a different order than submitted produced a phantom diff that `bundle plan` and `bundle deploy` could never converge past, reporting `1 to change` on every run ([#6060](#6060)).
* Fixed a pipeline with `allow_duplicate_names: true` never converging on the direct engine: the field is only accepted on create/update and is never returned by the pipelines GET API, so every subsequent `bundle plan` reported the pipeline as a perpetual update. ([#6076](#6076))
* direct: A local change to an input-only field (one the API accepts on write but never returns on read, e.g. pipelines' `run_as` or external locations' `skip_validation`) is no longer silently skipped when the new value coincidentally matches the field's fabricated remote value. Previously such a change could hit the `remote_already_set` shortcut and be dropped from the plan. ([#6112](#6112))
* Revert usage of RedactiveSenstiveFields (added in [#5896](#5896), released in 1.10.0) which lead to incorrect behaviour (permanent drift) for duration field in Postgres resources ([#6179](#6179)).
* Document postgres resource fields in the json schema ([#6164](#6164), [#6163](#6163)).
* direct: Recreating a `vector_search_indexes` resource no longer fails with "Index ... is currently pending deletion" when the backend has not yet released the index name. The create is now retried until the name becomes available. ([#6143](#6143))
### Dependency Updates
* Bump `github.com/databricks/databricks-sdk-go` from v0.165.0 to v0.166.0. ([#6175](#6175))
* Upgrade Terraform provider to 1.124.0. ([#6174](#6174))
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@radakam@eng-dev-ecosystem-bot@denik