Skip to content

feat: apps init speed improvements#4678

Merged
MarioCadenas merged 8 commits intomainfrom
init-speed-improvements
Mar 16, 2026
Merged

feat: apps init speed improvements#4678
MarioCadenas merged 8 commits intomainfrom
init-speed-improvements

Conversation

@MarioCadenas
Copy link
Contributor

@MarioCadenas MarioCadenas commented Mar 6, 2026

Changes

Speed up databricks apps init by parallelizing three slow I/O operations (template cloning, npm install, resource list fetching) that previously ran sequentially, so they overlap with user interaction time.

Main changes

Background template cloning (cmd/apps/init.go):
Template cloning now starts in a background goroutine immediately when the command runs, while the user is typing the project name. resolveTemplateAsync returns a channel; awaitTemplate either returns instantly (if already done) or shows a spinner for the remaining wait. This replaces the previous blocking resolveTemplate call that happened after the name prompt.

Background npm install (cmd/apps/init.go):
For Node.js templates, startBackgroundNpmInstall copies package.json/package-lock.json into the destination and kicks off npm ci while the user answers plugin/resource prompts. The result is awaited before template files are written to avoid concurrent writes. The Node.js initializer now skips redundant installs when node_modules already exists.

Prefetched resource lists (libs/apps/prompt/prefetch.go, libs/apps/prompt/prompt.go):
PrefetchResources fires background goroutines to fetch the first page of every resource type the manifest might need (warehouses, jobs, endpoints, experiments, Genie spaces, etc.) before the user selects plugins. A ResourceCache in the context stores PagedFetcher instances, so pickers render instantly when reached. Resource prompts now use promptFromPagedFetcher with "Load more..." and "Enter manually" / server-side search fallbacks, replacing the old promptForResourceFromLister that fetched everything in one blocking call.

Paged resource picker UX (libs/apps/prompt/prompt.go, libs/apps/prompt/listers.go):
All resource prompts switched from loading everything upfront to incremental paging (200 items/page, 10,000 cap). When capped, users can enter a name/ID manually or trigger a server-side search (currently supported for Jobs). Prompt titles now include the plugin display name for context (e.g. "Select SQL Warehouse for Analytics").

Supporting infrastructure

  • libs/apps/prompt/paged.goPagedFetcher struct with WaitForFirstPage, LoadMore, IsDone, and a generic collectN helper for SDK iterators.
  • libs/apps/prompt/cache.go — Thread-safe ResourceCache stored in context.Context via ContextWithCache/CacheFromContext.
  • libs/apps/prompt/prefetch.go — Maps resource types to paged constructors and launches background prefetch goroutines for both single-step and multi-step (catalog/instance/project) resources.
  • libs/apps/prompt/listers.go — Added NewPaged* constructors for all resource types and a SearchJobs server-side search function.

Other changes

  • Brand colors extracted into package-level colorRed/colorGray/colorYellow/colorOrange variables for consistency.
  • Filter hint changed from "type to filter" to "/ to filter" and explicit .Filtering(true) removed.
  • Volume IDs changed from catalog.schema.volume to /Volumes/catalog/schema/volume.
  • manifest.Resource gained a PluginDisplayName field (JSON-excluded) set during collection.
  • manifest.Manifest gained a GetPluginNames helper.
  • Checkmark output (/) consolidated into prompt.PrintDone across deploy_bundle.go, import.go, and init.go.
  • Existing List* functions now consistently apply capResults and use min(len, maxListResults) for initial slice capacity.

Why

Tests

@eng-dev-ecosystem-bot
Copy link
Collaborator

eng-dev-ecosystem-bot commented Mar 6, 2026

Commit: d5c0893

Run: 23145458252

Env 🔄​flaky 💚​RECOVERED 🙈​SKIP ✅​pass 🙈​skip Time
💚​ aws linux 8 9 268 786 7:22
💚​ aws windows 8 9 270 784 7:43
🔄​ aws-ucws linux 2 7 9 364 701 11:49
🔄​ aws-ucws windows 2 7 9 366 699 10:37
💚​ azure linux 2 11 271 784 6:46
💚​ azure windows 2 11 273 782 6:29
🔄​ azure-ucws linux 2 1 11 369 697 11:55
🔄​ azure-ucws windows 2 1 11 371 695 10:20
💚​ gcp linux 2 11 267 787 6:34
💚​ gcp windows 2 11 269 785 5:30
18 interesting tests: 9 SKIP, 6 RECOVERED, 3 flaky
Test Name aws linux aws windows aws-ucws linux aws-ucws windows azure linux azure windows azure-ucws linux azure-ucws windows gcp linux gcp windows
🔄​ TestAccept 💚​R 💚​R 💚​R 🔄​f 💚​R 💚​R 🔄​f 🔄​f 💚​R 💚​R
🙈​ TestAccept/bundle/resources/permissions 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
💚​ TestAccept/bundle/resources/permissions/jobs/destroy_without_mgmtperms/with_permissions 💚​R 💚​R 💚​R 💚​R 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
💚​ TestAccept/bundle/resources/permissions/jobs/destroy_without_mgmtperms/with_permissions/DATABRICKS_BUNDLE_ENGINE=direct 💚​R 💚​R 💚​R 💚​R
💚​ TestAccept/bundle/resources/permissions/jobs/destroy_without_mgmtperms/with_permissions/DATABRICKS_BUNDLE_ENGINE=terraform 💚​R 💚​R 💚​R 💚​R
💚​ TestAccept/bundle/resources/permissions/jobs/destroy_without_mgmtperms/without_permissions 💚​R 💚​R 💚​R 💚​R 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
💚​ TestAccept/bundle/resources/permissions/jobs/destroy_without_mgmtperms/without_permissions/DATABRICKS_BUNDLE_ENGINE=direct 💚​R 💚​R 💚​R 💚​R
💚​ TestAccept/bundle/resources/permissions/jobs/destroy_without_mgmtperms/without_permissions/DATABRICKS_BUNDLE_ENGINE=terraform 💚​R 💚​R 💚​R 💚​R
🙈​ TestAccept/bundle/resources/postgres_branches/basic 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/postgres_branches/recreate 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/postgres_branches/update_protected 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/postgres_branches/without_branch_id 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/postgres_endpoints/basic 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/postgres_endpoints/recreate 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/postgres_projects/update_display_name 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/synced_database_tables/basic 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🔄​ TestAccept/ssh/connect-serverless-gpu 🙈​s 🙈​s 🔄​f 🔄​f 🙈​s 🙈​s 🔄​f 🔄​f 🙈​s 🙈​s
🔄​ TestAccept/ssh/connection 💚​R 💚​R 🔄​f 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R
Top 22 slowest tests (at least 2 minutes):
duration env testname
3:32 aws-ucws windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
3:26 gcp windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
3:24 aws-ucws linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
3:24 aws linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
3:24 aws-ucws windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
3:22 azure-ucws linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
3:18 gcp linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
3:14 gcp windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
3:06 gcp linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
3:05 aws windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
3:03 aws windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
2:54 aws linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
2:53 azure linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
2:45 aws-ucws windows TestAccept/bundle/resources/model_serving_endpoints/basic/DATABRICKS_BUNDLE_ENGINE=direct
2:44 aws-ucws linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
2:40 azure linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
2:37 azure-ucws windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
2:33 azure-ucws linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
2:29 azure-ucws windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
2:15 azure windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
2:15 azure windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
2:06 azure-ucws windows TestAccept/bundle/generate/auto-bind

@MarioCadenas MarioCadenas force-pushed the init-speed-improvements branch from bcc9026 to 1fb43f8 Compare March 6, 2026 17:59
@MarioCadenas MarioCadenas force-pushed the init-speed-improvements branch from 1fb43f8 to 7599847 Compare March 9, 2026 09:50
@MarioCadenas MarioCadenas force-pushed the init-speed-improvements branch from 7599847 to 257f2d6 Compare March 12, 2026 11:31
Copy link
Member

@pkosiec pkosiec left a comment

Choose a reason for hiding this comment

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

It's so fast now 🚀 awesome work! Just a few comments

@MarioCadenas MarioCadenas force-pushed the init-speed-improvements branch from 72d5b8f to f608c2d Compare March 13, 2026 15:57
@MarioCadenas MarioCadenas marked this pull request as ready for review March 13, 2026 15:59
@MarioCadenas MarioCadenas requested a review from a team as a code owner March 13, 2026 15:59
Copy link
Member

@pkosiec pkosiec left a comment

Choose a reason for hiding this comment

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

Works well 👌 Just small comment about the "load more" labels

Copy link
Member

Choose a reason for hiding this comment

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

Do we need them though? If we have Paged alternatives?

  1. Dead List* functions (~200 LOC) — listers.go
  Nine old List* functions (ListJobs, ListServingEndpoints, ListCatalogs, ListConnections, ListGenieSpaces, ListVectorSearchIndexes,
  ListDatabaseInstances, ListPostgresProjects, ListExperiments) are now dead code, replaced by NewPaged* counterparts. Also capResults
  and maxListResults become dead. ~200 lines to remove.

branchForClone = gitRef
subdirForClone = appkitTemplateDir
}
templateCh := resolveTemplateAsync(ctx, templateSrc, branchForClone, subdirForClone)
Copy link
Member

Choose a reason for hiding this comment

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

3. Temp directory leak on early cancellation — init.go:744
  If the user cancels during the name prompt, resolveTemplateAsync may have cloned a repo to a temp directory. Since awaitTemplate is
  never called, cleanup is never invoked. Add a drain in the runCreate defer:
  defer func() {
      select {
      case res := <-templateCh:
          if res.cleanup != nil { res.cleanup() }
      default:
      }
  }()

Copy link

@atilafassina atilafassina left a comment

Choose a reason for hiding this comment

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

🏆 agree the cleanup would be great to have. But not a blocker.

@MarioCadenas MarioCadenas added this pull request to the merge queue Mar 16, 2026
Merged via the queue into main with commit f188a50 Mar 16, 2026
18 checks passed
@MarioCadenas MarioCadenas deleted the init-speed-improvements branch March 16, 2026 13:58
rauchy pushed a commit that referenced this pull request Mar 17, 2026
## Changes

Speed up `databricks apps init` by parallelizing three slow I/O
operations (template cloning, npm install, resource list fetching) that
previously ran sequentially, so they overlap with user interaction time.

### Main changes

**Background template cloning** (`cmd/apps/init.go`):
Template cloning now starts in a background goroutine immediately when
the command runs, while the user is typing the project name.
`resolveTemplateAsync` returns a channel; `awaitTemplate` either returns
instantly (if already done) or shows a spinner for the remaining wait.
This replaces the previous blocking `resolveTemplate` call that happened
after the name prompt.

**Background npm install** (`cmd/apps/init.go`):
For Node.js templates, `startBackgroundNpmInstall` copies
`package.json`/`package-lock.json` into the destination and kicks off
`npm ci` while the user answers plugin/resource prompts. The result is
awaited before template files are written to avoid concurrent writes.
The Node.js initializer now skips redundant installs when `node_modules`
already exists.

**Prefetched resource lists** (`libs/apps/prompt/prefetch.go`,
`libs/apps/prompt/prompt.go`):
`PrefetchResources` fires background goroutines to fetch the first page
of every resource type the manifest might need (warehouses, jobs,
endpoints, experiments, Genie spaces, etc.) before the user selects
plugins. A `ResourceCache` in the context stores `PagedFetcher`
instances, so pickers render instantly when reached. Resource prompts
now use `promptFromPagedFetcher` with "Load more..." and "Enter
manually" / server-side search fallbacks, replacing the old
`promptForResourceFromLister` that fetched everything in one blocking
call.

**Paged resource picker UX** (`libs/apps/prompt/prompt.go`,
`libs/apps/prompt/listers.go`):
All resource prompts switched from loading everything upfront to
incremental paging (200 items/page, 10,000 cap). When capped, users can
enter a name/ID manually or trigger a server-side search (currently
supported for Jobs). Prompt titles now include the plugin display name
for context (e.g. "Select SQL Warehouse for Analytics").

### Supporting infrastructure

- **`libs/apps/prompt/paged.go`** — `PagedFetcher` struct with
`WaitForFirstPage`, `LoadMore`, `IsDone`, and a generic `collectN`
helper for SDK iterators.
- **`libs/apps/prompt/cache.go`** — Thread-safe `ResourceCache` stored
in `context.Context` via `ContextWithCache`/`CacheFromContext`.
- **`libs/apps/prompt/prefetch.go`** — Maps resource types to paged
constructors and launches background prefetch goroutines for both
single-step and multi-step (catalog/instance/project) resources.
- **`libs/apps/prompt/listers.go`** — Added `NewPaged*` constructors for
all resource types and a `SearchJobs` server-side search function.

### Other changes

- Brand colors extracted into package-level
`colorRed`/`colorGray`/`colorYellow`/`colorOrange` variables for
consistency.
- Filter hint changed from "type to filter" to "/ to filter" and
explicit `.Filtering(true)` removed.
- Volume IDs changed from `catalog.schema.volume` to
`/Volumes/catalog/schema/volume`.
- `manifest.Resource` gained a `PluginDisplayName` field (JSON-excluded)
set during collection.
- `manifest.Manifest` gained a `GetPluginNames` helper.
- Checkmark output (`✔`/`✓`) consolidated into `prompt.PrintDone` across
`deploy_bundle.go`, `import.go`, and `init.go`.
- Existing `List*` functions now consistently apply `capResults` and use
`min(len, maxListResults)` for initial slice capacity.

## Why
<!-- Why are these changes needed? Provide the context that the reviewer
might be missing.
For example, were there any decisions behind the change that are not
reflected in the code itself? -->

## Tests
<!-- How have you tested the changes? -->

<!-- If your PR needs to be included in the release notes for next
release,
add a separate entry in NEXT_CHANGELOG.md as part of your PR. -->

---------

Co-authored-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
Sign up for free to 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.

4 participants