Skip to content

[wrangler] Add automatic queue provisioning to resource provisioning#11788

Closed
irvinebroque wants to merge 9 commits intomainfrom
bib/provision-queues
Closed

[wrangler] Add automatic queue provisioning to resource provisioning#11788
irvinebroque wants to merge 9 commits intomainfrom
bib/provision-queues

Conversation

@irvinebroque
Copy link
Copy Markdown
Contributor

@irvinebroque irvinebroque commented Jan 4, 2026

Extends the existing resource provisioning feature to support Cloudflare Queues. When users deploy with queue bindings configured, missing queues are now automatically created, just like KV, D1, and R2.

Changes:

  • Add QueueHandler class following the R2Handler pattern (name-based, no ID)
  • Add queue provisioning helper functions using ComplianceConfig
  • Collect and de-duplicate queues from both producers and consumers
  • Skip ensureQueuesExistByConfig when provisioning is enabled
  • Include delivery_delay in queue binding transformation
  • Update deploy tests to use --x-provision=false for legacy behavior tests

The feature is controlled by the existing --x-provision flag (enabled by default) and --x-auto-create flag for interactive vs automatic creation.

I've tested this out locally and deployed successfully. And reviewed code, left comments. Written with opencode.

Questions

  • Comfortable rolling this out as part of existing behavior without customers needing to add additional flags?

…system

Extends the existing resource provisioning feature to support Cloudflare Queues.
When users deploy with queue bindings configured, missing queues are now
automatically created, providing the same seamless experience as KV, D1, and R2.

Changes:
- Add QueueHandler class following the R2Handler pattern (name-based, no ID)
- Add queue provisioning helper functions using ComplianceConfig
- Collect and de-duplicate queues from both producers and consumers
- Skip ensureQueuesExistByConfig when provisioning is enabled
- Include delivery_delay in queue binding transformation
- Update deploy tests to use --x-provision=false for legacy behavior tests

The feature is controlled by the existing --x-provision flag (enabled by default)
and --x-auto-create flag for interactive vs automatic creation.
@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Jan 4, 2026

🦋 Changeset detected

Latest commit: 7192f85

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

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

@claude
Copy link
Copy Markdown
Contributor

claude bot commented Jan 4, 2026

Claude finished @irvinebroque's task —— View job


Changeset Review

Reviewing changeset files...

Tasks:

  • Read changeset guidelines
  • Review changeset file
  • Validate against criteria
  • Check for animal images

⚠️ Issues found

The changeset .changeset/queue-provisioning.md has the following issue:

Markdown Headers: The changeset uses code blocks with JSONC format which is good, but there's a comment in the review from petebacondarwin noting "We prefer JSONC config nowadays" on this file. Looking at the changeset, it already uses jsonc in the code block, so this appears to be addressed correctly.

However, the changeset has one minor formatting issue:

Line 11: The comment syntax in the example uses // wrangler.jsonc which suggests a JSONC file, but the actual Workers configuration files are typically named wrangler.toml (TOML format) or wrangler.json (JSON format). The comment should clarify this is showing JSONC format as an example, or use the more standard wrangler.toml TOML format that most users have.

Other validation results:
Version Type: Correctly set to minor (new feature)
Changelog Quality: Excellent description with clear examples and feature list
Markdown Headers: No h1/h2/h3 headers used (correctly uses h4 if any)
No forbidden major version for wrangler

The changeset is well-written overall with good examples and clear explanations. The only suggestion is to clarify the configuration format being shown or use the more standard TOML format.


@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Jan 4, 2026

create-cloudflare

npm i https://pkg.pr.new/create-cloudflare@11788

@cloudflare/kv-asset-handler

npm i https://pkg.pr.new/@cloudflare/kv-asset-handler@11788

miniflare

npm i https://pkg.pr.new/miniflare@11788

@cloudflare/pages-shared

npm i https://pkg.pr.new/@cloudflare/pages-shared@11788

@cloudflare/unenv-preset

npm i https://pkg.pr.new/@cloudflare/unenv-preset@11788

@cloudflare/vite-plugin

npm i https://pkg.pr.new/@cloudflare/vite-plugin@11788

@cloudflare/vitest-pool-workers

npm i https://pkg.pr.new/@cloudflare/vitest-pool-workers@11788

@cloudflare/workers-editor-shared

npm i https://pkg.pr.new/@cloudflare/workers-editor-shared@11788

@cloudflare/workers-utils

npm i https://pkg.pr.new/@cloudflare/workers-utils@11788

wrangler

npm i https://pkg.pr.new/wrangler@11788

commit: 7192f85

@irvinebroque irvinebroque marked this pull request as ready for review January 4, 2026 03:47
@irvinebroque irvinebroque requested a review from a team as a code owner January 4, 2026 03:47
@petebacondarwin
Copy link
Copy Markdown
Contributor

@irvinebroque - was this PR generated by AI? If so, although you said you have reviewed it, can you mention this for transparency in the PR description?

Comment thread .changeset/queue-provisioning.md Outdated
Comment thread packages/wrangler/src/deployment-bundle/bindings.ts
// Skip queues - they use queue name from config, no ID write-back needed
if (resourceType === "queues") {
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.

Not obvious to me why this ended up being necessary, given that Queues should behave like R2 and I don't see equivalent here for R2

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.

I think these are noops since the binding will be identical and the patch function below will find no difference.
So I think you can remove this check and the one below to simplify this code.

Comment thread packages/wrangler/src/triggers/deploy.ts
Comment thread packages/wrangler/src/versions/upload.ts
Comment thread packages/wrangler/src/__tests__/provision.test.ts Outdated
Copy link
Copy Markdown
Contributor

@petebacondarwin petebacondarwin left a comment

Choose a reason for hiding this comment

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

Exciting to see this implemented but I think we can be a bit cleaner with the approach.

return mock;
}

// Legacy helpers for backwards compatibility
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.

I am not sure this comment is accurate. These helpers are not legacy nor related to backward compatibility. I would just delete this comment.

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.

Comment thread packages/wrangler/src/deployment-bundle/bindings.ts
// Skip queues - they use queue name from config, no ID write-back needed
if (resourceType === "queues") {
continue;
}
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.

I think these are noops since the binding will be identical and the patch function below will find no difference.
So I think you can remove this check and the one below to simplify this code.

bindings: CfWorkerInit["bindings"],
requireRemote: boolean
requireRemote: boolean,
config?: Config
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.

Rather than add a new parameter, just repurpose the complianceConfig parameter above. The actual object that is passed in there is always a Config anyway...

async function collectPendingResources(
	config: Config,
	accountId: string,
	scriptName: string,
	bindings: CfWorkerInit["bindings"],
	requireRemote: boolean
}

Comment on lines +574 to +577
const resources =
resourceType === "queues" && config
? collectUniqueQueueBindings(config)
: bindings[resourceType] ?? [];
Copy link
Copy Markdown
Contributor

@petebacondarwin petebacondarwin Jan 6, 2026

Choose a reason for hiding this comment

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

Rather than special casing here, I would rather we use polymorphism by adding a new method to the ProvisionResourceHandler class that returns the binding:

getResourcesFromConfig(config: Config | undefined, bindings: B[]) {
  return bindings;
}

and then in the QueueHandler class:

getResourcesFromConfig(config: Config | undefined, bindings: B[]) {
  assert(config);
  return collectUniqueQueueBindings(config);
}

and the call site would look like:

		const resources = HANDLERS[resourceType].getResourcesFromConfig(config, bindings[resourceType] ?? []);

@github-project-automation github-project-automation bot moved this from Untriaged to In Review in workers-sdk Jan 6, 2026
Ensures queue producer bindings include the `remote` field, matching
the pattern used by R2 and other bindings that support remote mode.
Rather than special-casing queues in collectPendingResources, add a
getResourcesFromConfig method to each handler in HANDLERS that can be
overridden per resource type. QueueHandler's method calls
collectUniqueQueueBindings to collect both producers and consumers.

Also simplifies the function signature by using Config directly instead
of ComplianceConfig, since Config is always passed in practice.
Clarify that the queue skipping in config patching is required because:
1. config.queues has a different shape (producers/consumers) than other
   bindings which are flat arrays
2. Queues use name-based identification so no ID write-back is needed
The mockListQueues and mockCreateQueue helpers are standard test
utilities, not legacy helpers for backwards compatibility.
Comment on lines +448 to +451
getResourcesFromConfig: (
_config: Config,
bindings: CfKvNamespace[]
): CfKvNamespace[] => bindings,
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.

My suggestion was for this to be a method on the handler (e.g. here KVHandler. The "standard" implementation will be on ProvisionResourceHandler and only the QueueHandler needs the method to be overridden.

Moreover, I believe that this method can use a type parameter to avoid having to cast the return values.

@penalosa
Copy link
Copy Markdown
Contributor

/bonk can you fix the merge conflicts on this pr?

Copy link
Copy Markdown
Contributor

@ask-bonk ask-bonk bot left a comment

Choose a reason for hiding this comment

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

Review Summary

The feature looks solid overall — queue provisioning slots in cleanly alongside KV/D1/R2. Tests are good, the de-duplication logic is well-thought-out, and CI is green (the Windows failure is a flaky dev-registry timeout, unrelated).

There is one outstanding piece of feedback from @petebacondarwin that hasn't been fully addressed yet, plus a few smaller items I noticed.

Outstanding: getResourcesFromConfig should be a method on the handler class (petebacondarwin's feedback)

Pete's latest comment clarifies that getResourcesFromConfig should live as a method on ProvisionResourceHandler (with a default implementation returning bindings), not as a function on each entry in the HANDLERS map. The current implementation repeats the same trivial (_config, bindings) => bindings lambda for KV, D1, and R2, which is exactly the boilerplate that a base class default eliminates.

Additionally, using the existing generic type parameter B on the class would give proper typing without the intersection cast as CfR2Bucket[] & CfKvNamespace[] & CfD1Database[] & CfQueue[] at the call site.

Suggested shape:

// On ProvisionResourceHandler (static method or standalone):
static getResourcesFromConfig(config: Config, bindings: B[]): B[] {
  return bindings;
}

// Override only in QueueHandler:
static getResourcesFromConfig(config: Config, _bindings: CfQueue[]): CfQueue[] {
  return collectUniqueQueueBindings(config);
}

Since handlers are instantiated per-binding inside collectPendingResources, this would need to be a static method or a function on the Handler class itself (not an instance method), which is awkward with the current architecture. The simplest way to address Pete's feedback while keeping the current instantiation pattern would be to add it as a static method on each handler class, or keep it on the HANDLERS map but eliminate the repeated lambdas by having a shared default. Worth discussing which approach feels cleanest.

Minor items

  1. Synthetic binding name for consumer-only queues (bindings.ts:558): The name __queue_consumer_${consumer.queue}__ is used as a synthetic binding name for consumer-only queues. This binding name flows into the provisioning output messages (e.g., the user would see __queue_consumer_incoming__ provisioned). Consider whether this user-visible string should be more human-friendly, or whether consumer-only queues should show differently (e.g., just the queue name).

  2. listQueuesForProvisioning doesn't paginate (queues/client.ts:185): The function defaults page to 1 but doesn't loop through all pages. For isConnectedToExistingResource this is fine because it passes the name filter, but the LOADERS.queues call at bindings.ts:516 fetches the full list for the interactive picker — if a user has more than one page of queues, not all will appear.

  3. queue_name is always required on CfQueue (not optional): The isConnectedToExistingResource guard if (!this.binding.queue_name) at line 414 can never be true since CfQueue.queue_name is typed as string (not optional). This is fine as defensive code but the comment should reflect it's a type guard for safety rather than a realistic code path.

  4. The Windows CI failure is unrelated — it's a timeout in @fixture/dev-registry tail handler test, a known flaky test.

@ask-bonk
Copy link
Copy Markdown
Contributor

ask-bonk bot commented Mar 20, 2026

Review posted on PR #11788. Here's a summary of the key findings:

Overall: The feature is well-implemented — queue provisioning integrates cleanly with the existing KV/D1/R2 pattern. Tests cover inheritance, auto-creation, de-duplication, and consumer-only queues. CI is green (the Windows failure is an unrelated flaky dev-registry test).

Main outstanding issue: petebacondarwin's feedback about getResourcesFromConfig hasn't been fully addressed. He wanted it as a method on the ProvisionResourceHandler class with a default implementation, not repeated as lambdas on every entry in the HANDLERS map. The current implementation adds the same trivial (_config, bindings) => bindings to KV, D1, and R2 entries — exactly the boilerplate a base class default would eliminate.

Minor items flagged:

  1. Synthetic binding name __queue_consumer_<name>__ for consumer-only queues may be user-visible in provisioning output
  2. LOADERS.queues doesn't paginate, so the interactive picker may miss queues for users with many queues
  3. The !this.binding.queue_name guard is dead code since queue_name is typed as required string

github run

@penalosa
Copy link
Copy Markdown
Contributor

Closing this in favour of #13352, which will support Queues + all other bindings

@penalosa penalosa closed this Apr 13, 2026
@github-project-automation github-project-automation bot moved this from In Review to Done in workers-sdk Apr 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

4 participants