Skip to content

bundle: record and read deployment state via DMS with server-generated IDs - #6052

Draft
shreyas-goenka wants to merge 2 commits into
mainfrom
dms-state-recording
Draft

bundle: record and read deployment state via DMS with server-generated IDs#6052
shreyas-goenka wants to merge 2 commits into
mainfrom
dms-state-recording

Conversation

@shreyas-goenka

@shreyas-goenkashreyas-goenka commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

What

Wires the direct deployment engine into the Deployment Metadata Service (DMS) so that a bundle with experimental.record_deployment_history enabled:

  1. Records each deploy/destroy as a DMS version (with heartbeat + completion).
  2. Records each applied resource operation (create/update/delete) under that version.
  3. Reads resource state back from DMS on the next deploy.

The key design point: server-generated deployment IDs

The DMS API assigns the deployment ID server-side. Earlier prototypes used the local state lineage (a client-minted UUID) as the deployment ID; that no longer holds. The flow is now:

  • First deployCreateDeployment with an empty ID → the server mints one and returns it in deployment.Name → we parse it out and persist it in the direct-engine state header (Header.DeploymentID).
  • Later deploys → pass the stored ID back → GetDeployment to compute the next version number.

Because the ID lives in the state file (which is synced to the workspace), it survives deleting the local .databricks cache — the redeploy recovers the same deployment and just increments the version. No new CreateDeployment.

// libs/dms/recorder.go — first deploy lets the server assign the IDdep, _:=r.svc.CreateDeployment(ctx, bundledeployments.CreateDeploymentRequest{
Deployment: bundledeployments.Deployment{TargetName: r.targetName},
})
r.deploymentID, _=deploymentIDFromName(dep.Name) // "deployments/{id}" -> {id}

Reading DMS authority: last_successful_version_id

The read overlay only trusts DMS state when DMS holds a successfully completed version for the deployment. The deployment exposes last_successful_version_id (advanced only on success, unlike last_version_id), so a single GetDeployment answers that — no version listing.

That field is still stage: DEVELOPMENT in the proto, so it's stripped from the generated SDK. Until it's promoted to PRIVATE_PREVIEW and regenerated, the CLI reads it via a temporary raw GET into a local struct:

// bundle/direct/dstate/dms.go — TODO(DMS): drop once the SDK generates the fieldvardepstruct {
LastSuccessfulVersionIDstring`json:"last_successful_version_id"`
}
err=apiClient.Do(ctx, http.MethodGet, "/api/2.0/bundle/deployments/"+deploymentID,
auth.WorkspaceIDHeaders(cfg), nil, nil, &dep)
returndep.LastSuccessfulVersionID!="", nil

The proto flip to PRIVATE_PREVIEW is a separate universe change; once it lands and the SDK regenerates, the raw call collapses to client.GetDeployment(...).LastSuccessfulVersionId and the threaded cfg argument goes away.

Wiring

  • libs/dmsRecorder: create deployment (server-assigned ID) + version, heartbeat the lease, complete it, delete the deployment on destroy.
  • bundle/direct/oprecorder.gooperationRecorder: reports each applied operation. The wire resource_key drops the CLI-internal resources. prefix (resources.jobs.foojobs.foo); the read path re-adds it.
  • bundle/direct/dstateOpen overlays DMS resource state when DMS holds a successful version (last_successful_version_id set); DeploymentID persisted in the state header.
  • bundle/phases — create the version after plan approval, complete it under the lock (deferred before lock.Release), record operations during apply.
  • libs/testserver — a stateful fake DMS (deployments / versions / operations / resources) with server-generated IDs; GetDeployment serves last_successful_version_id on version completion.

Testing

  • Unit tests: recorder (first deploy / redeploy / destroy / nil no-op), op-recorder prefix stripping, deployment-ID state round-trip.
  • Acceptance test acceptance/bundle/dms/record covers the full arc: deploy → server assigns ID → redeploy after rm -rf .databricks (ID recovered, version increments, no new CreateDeployment) → destroy (delete op + deployment deletion).

Gated entirely behind experimental.record_deployment_history + the direct engine; terraform and non-opted bundles are untouched.

This pull request and its description were written by Isaac.

…d IDs
Wire the direct engine into the Deployment Metadata Service (DMS) so that a
`record_deployment_history`-enabled bundle records each deploy/destroy as a
version and can read its resource state back from DMS.
The deployment ID is now assigned by the server: the first deploy calls
CreateDeployment with an empty ID, reads the assigned ID back from the response,
and persists it in the direct-engine state header (Header.DeploymentID). Later
deploys pass the stored ID back, so a bundle maps one-to-one to a DMS deployment
even after the local cache is deleted (the ID rides along in the
workspace-synced state file).
- libs/dms: Recorder creates the deployment (server-assigned ID) + version,
heartbeats the lease, completes it, and deletes the deployment on destroy.
- bundle/direct: operationRecorder reports each applied resource operation;
the wire resource_key drops the CLI-internal "resources." prefix.
- bundle/direct/dstate: Open takes a DMS client and overlays DMS resource state
when DMS holds a successful version; deployment ID persisted in the header.
- bundle/phases: create the version after plan approval, complete it under the
lock, record operations during apply.
- libs/testserver: stateful fake DMS (deployments/versions/operations/resources)
with server-generated IDs; acceptance test covers deploy, cache-loss redeploy,
and destroy.
Co-authored-by: Isaac
@eng-dev-ecosystem-bot

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

Copy link
Copy Markdown
Collaborator

Integration test report

Commit: da1ed9d

Run: 30102796660

Env🔄​flaky💚​RECOVERED🙈​SKIP✅​pass🙈​skipTime
💚​aws linux44322106110:56
🔄​aws windows134324105914:40
💚​azure linux44322106011:58
💚​azure windows4432410588:33
💚​gcp linux15321106211:23
🔄​gcp windows11532210608:15
9 interesting tests: 4 SKIP, 3 RECOVERED, 2 flaky
Test Nameaws linuxaws windowsazure linuxazure windowsgcp linuxgcp windows
🔄​TestAccept💚​R🔄​f💚​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
🔄​TestSyncIncrementalFileOverwritesFolder✅​p✅​p✅​p✅​p✅​p🔄​f
💚​TestFetchRepositoryInfoAPI_FromRepo💚​R💚​R💚​R💚​R🙈​S🙈​S
💚​TestFetchRepositoryInfoAPI_FromRepo/root💚​R💚​R💚​R💚​R
💚​TestFetchRepositoryInfoAPI_FromRepo/subdir💚​R💚​R💚​R💚​R
Top 13 slowest tests (at least 2 minutes):
durationenvtestname
5:18gcp windowsTestAccept
4:55azure windowsTestAccept
4:29aws linuxTestFilerWorkspaceFilesExtensionsReadDir
3:47aws windowsTestAccept
3:26azure linuxTestImportDir
3:09gcp linuxTestFilerWorkspaceFilesExtensionsReadDir
3:09azure linuxTestFilerWorkspaceFilesExtensionsRead
3:04azure windowsTestAccept/bundle/deploy/mlops-stacks/DATABRICKS_BUNDLE_ENGINE=direct
3:03aws linuxTestAccept/bundle/deploy/mlops-stacks/DATABRICKS_BUNDLE_ENGINE=terraform
3:01azure linuxTestFilerWorkspaceFilesExtensionsReadDir
2:21aws windowsTestImportDirWithOverwriteFlag
2:05azure windowsTestAccept/bundle/deploy/mlops-stacks/DATABRICKS_BUNDLE_ENGINE=terraform
2:01azure linuxTestAccept/bundle/deploy/mlops-stacks/DATABRICKS_BUNDLE_ENGINE=direct

The read overlay decided whether DMS owns a deployment's state by listing
versions and scanning for a successful one. The deployment now exposes
last_successful_version_id directly, so a single GetDeployment answers the
same question — no version listing.
The field is still stage:DEVELOPMENT in the proto and therefore stripped from
the generated SDK, so this reads the deployment via a raw GET into a local
struct as a temporary stub. Once the field is promoted to PRIVATE_PREVIEW and
regenerated, the raw call collapses to client.GetDeployment(...).
LastSuccessfulVersionId and the threaded config argument goes away (see the
TODO in deploymentHasSuccessfulVersion).
The testserver's GetDeployment now serves last_successful_version_id (tracked
on version completion), and the now-unused ListVersions fake is removed.
Co-authored-by: Isaac
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.

2 participants

@shreyas-goenka@eng-dev-ecosystem-bot