Skip to content

perf(cli): parallelize deploy metadata fetch and function uploads#1793

Open
dqn wants to merge 6 commits into
mainfrom
perf/deploy-parallel-rpc
Open

perf(cli): parallelize deploy metadata fetch and function uploads#1793
dqn wants to merge 6 commits into
mainfrom
perf/deploy-parallel-rpc

Conversation

@dqn

@dqn dqn commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Summary

Deploy fetched sdk-version metadata for every candidate resource one at a time
and uploaded function scripts serially; both now run concurrently, bounded by
the shared apply-concurrency budget (TAILOR_APPLY_CONCURRENCY, default 16).

Measured on example/ (37 metadata TRNs, 15 functions, 12 types)

Scenario Before After
SDK version change detection 3.3–3.7s 0.4–0.7s
No-change re-deploy 8.8s 5.5s
Update deploy (27 resources) 20.5s 7.7s

Notes

  • TailorDB type DDL stays serialized: concurrent DDL within a namespace races
    platform-side schema mutation (the invariant fc0c469 established); a test
    now pins the sequential order.
  • A detected sdk-version mismatch now takes priority over an unrelated
    metadata fetch failure instead of aborting the deploy.
  • Other per-resource apply loops (auth, executor, workflow, …) remain serial;
    their counts are small and can be parallelized later if profiling justifies it.

dqn added 4 commits July 17, 2026 02:01
Deploy issued platform RPCs one at a time in three hot paths: the
sdk-version change detection fetched metadata for every candidate TRN
serially, and the function-registry and TailorDB type applies looped
over creates/updates sequentially. Run each batch with Promise.all;
total in-flight RPCs stay bounded by the operator client's shared
concurrency limiter.

Measured on the example app (37 metadata TRNs, 19 functions, 12 types):
a no-change deploy drops from 8.8s to 4.1s, an update deploy from 20.5s
to 10.3s.
Re-serialize TailorDB type creates/updates: concurrent DDL within a
namespace races platform-side schema mutation (the invariant fc0c469
established), so only the metadata fetch and function-upload paths stay
parallel. Pin the invariant with a sequential-apply test.

Function upload streams bypass the operator client's unary concurrency
interceptor, so cap them explicitly with the shared apply-concurrency
limiter (TAILOR_APPLY_CONCURRENCY, default 16).
Run each function's upload and setMetadata inside a single limiter slot
so effective RPC concurrency stays within TAILOR_APPLY_CONCURRENCY, and
let a detected sdk-version mismatch win over an unrelated metadata fetch
failure instead of aborting the deploy.
@changeset-bot

changeset-bot Bot commented Jul 16, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 2bef878

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
@tailor-platform/sdk Patch
@tailor-platform/create-sdk Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new

pkg-pr-new Bot commented Jul 16, 2026

Copy link
Copy Markdown

Open in StackBlitz

pnpm add https://pkg.pr.new/@tailor-platform/create-sdk@2bef878
pnpm add https://pkg.pr.new/@tailor-platform/eslint-plugin-sdk@2bef878
pnpm add https://pkg.pr.new/@tailor-platform/sdk@2bef878

commit: 2bef878

@github-actions

This comment has been minimized.

@dqn
dqn marked this pull request as ready for review July 16, 2026 19:03
@dqn
dqn requested a review from a team as a code owner July 16, 2026 19:03
@dqn
dqn requested a review from toiroakr July 16, 2026 19:03

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📖 Docs Quality & Consistency Check

✅ Docs are consistent with the implementation and contain no user-facing internal-detail leaks.

Checked areas:

  • CLI environment variable documentation (TAILOR_APPLY_CONCURRENCY)
  • Implementation of concurrent metadata fetching in deploy.ts
  • Implementation of concurrent function uploads in function-registry.ts
  • Test coverage for new concurrent behavior
  • JSDoc comments on modified functions

Key findings:

  • The TAILOR_APPLY_CONCURRENCY documentation change accurately reflects the implementation change: streaming function uploads are now gated by the same concurrency budget (via createApplyLimiter()) instead of running unbounded
  • The simplified description ("Max concurrent platform RPCs" vs "Max concurrent unary platform RPCs... (streaming uploads are not gated)") improves clarity by removing technical implementation details that users don't need to understand
  • All code changes are internal to the CLI implementation and not part of the public SDK API
  • The shouldForceApplyAll function, now exported within its module for testing purposes, is not part of the package's public API exports

Re-run this check by adding the docs-check label to the PR.


return true;
}
}
const results = await Promise.allSettled(

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.

The concurrent getMetadata fetch has no concurrency cap.

function-registry.ts bounds its uploads with createApplyLimiter() (TAILOR_APPLY_CONCURRENCY), but this one fires getMetadata for every candidate TRN at once.

On projects with many resources this risks overloading the platform. Suggest limiting it with the same createApplyLimiter().

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.

getMetadata is a unary RPC, so these calls already pass through the concurrencyLimitInterceptor() installed by initOperatorClient(). That interceptor shares a single createApplyLimiter() budget across all unary calls on the client, so adding another call-site limiter here would only duplicate the existing cap.

await uploadFunctionScript(client, workspaceId, create.entry, true);
await client.setMetadata(create.metaRequest);
}
await Promise.all(

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.

Creates and updates run in two separate Promise.all waves, so updates never overlap with the tail of the creates wave.

Since both share the same limitFunction, merging both map calls into one array passed to a single Promise.all would let them overlap naturally.

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.

Fixed in b052079.

@toiroakr toiroakr assigned dqn and unassigned toiroakr Jul 17, 2026
@dqn

dqn commented Jul 17, 2026

Copy link
Copy Markdown
Contributor Author

Documentation states "streaming uploads are not gated"

Fixed in 2bef878.

@dqn dqn added the docs-check Trigger Docs Consistency Check label Jul 17, 2026
@dqn
dqn requested a review from toiroakr July 17, 2026 07:15
@dqn dqn assigned toiroakr and unassigned dqn Jul 17, 2026
@github-actions

Copy link
Copy Markdown

Code Metrics Report (packages/sdk)

main (fdd6254) #1793 (a0f50d9) +/-
Coverage 74.5% 74.5% +0.0%
Code to Test Ratio 1:0.4 1:0.4 +0.0
Details
  |                    | main (fdd6254) | #1793 (a0f50d9) |  +/-  |
  |--------------------|----------------|-----------------|-------|
+ | Coverage           |          74.5% |           74.5% | +0.0% |
  |   Files            |            457 |             458 |    +1 |
  |   Lines            |          16951 |           16965 |   +14 |
+ |   Covered          |          12634 |           12650 |   +16 |
+ | Code to Test Ratio |          1:0.4 |           1:0.4 |  +0.0 |
  |   Code             |         113290 |          113591 |  +301 |
+ |   Test             |          52562 |           52832 |  +270 |

Code coverage of files in pull request scope (90.0% → 90.5%)

Files Coverage +/- Status
packages/sdk/src/cli/commands/deploy/deploy.ts 90.3% +0.4% modified
packages/sdk/src/cli/commands/deploy/function-registry.ts 90.9% +0.1% modified
packages/sdk/src/cli/shared/test-helpers/concurrency-probe.ts 100.0% +100.0% added

SDK Configure Bundle Size

main (fdd6254) #1793 (a0f50d9) +/-
configure-index-size 32.17KB 32.17KB 0KB
dependency-chunks-size 29.88KB 29.88KB 0KB
total-bundle-size 62.05KB 62.05KB 0KB

Runtime Performance

main (fdd6254) #1793 (a0f50d9) +/-
Generate Median 1,990ms 2,614ms 624ms
Generate Max 2,006ms 2,792ms 786ms
Apply Build Median 2,034ms 2,650ms 616ms
Apply Build Max 2,051ms 2,669ms 618ms

Type Performance (instantiations)

main (fdd6254) #1793 (a0f50d9) +/-
tailordb-basic 42,869 42,869 0
tailordb-optional 4,451 4,451 0
tailordb-relation 6,220 6,220 0
tailordb-validate 753 753 0
tailordb-hooks 5,279 5,279 0
tailordb-object 12,547 12,547 0
tailordb-enum 1,486 1,486 0
resolver-basic 9,252 9,252 0
resolver-nested 26,119 26,119 0
resolver-array 18,059 18,059 0
executor-schedule 4,310 4,310 0
executor-webhook 949 949 0
executor-record 6,762 6,762 0
executor-resolver 4,090 4,090 0
executor-operation-function 937 937 0
executor-operation-gql 945 945 0
executor-operation-webhook 956 956 0
executor-operation-workflow 1,798 1,798 0

Reported by octocov

@github-actions github-actions Bot removed the docs-check Trigger Docs Consistency Check label Jul 17, 2026
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.

2 participants