diff --git a/src/content/changelog/workers/2026-01-03-cohort-based-gradual-deployments.mdx b/src/content/changelog/workers/2026-01-03-cohort-based-gradual-deployments.mdx new file mode 100644 index 00000000000000..0950906fe7a090 --- /dev/null +++ b/src/content/changelog/workers/2026-01-03-cohort-based-gradual-deployments.mdx @@ -0,0 +1,81 @@ +--- +title: Cohort-based gradual deployments (Beta) +description: Deploy Worker versions to specific user groups before rolling out to all traffic. +products: + - workers +date: 2026-01-03 +--- + +You can now deploy Worker versions to specific groups of users — employees, beta testers, free tier, enterprise — before deploying to everyone. Cohort-based deployments extend [gradual deployments](/workers/configuration/versions-and-deployments/gradual-deployments/) with named cohorts, each with its own version configuration. + +With percentage-based deployments, all users have equal probability of hitting a new version. You cannot test with employees first, then free users, then paid users. Cohort-based deployments solve this by letting you define named groups and route requests to them using the `Cloudflare-Workers-Cohort` header. + +## Create a deployment with cohorts + +Use the REST API to create a cohort-based deployment: + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/{account_id}/workers/scripts/{script_name}/deployments" \ + -H "Authorization: Bearer {api_token}" \ + -H "Content-Type: application/json" \ + -d '{ + "strategy": "cohorts", + "versions": [ + { "version_id": "{stable_version_id}", "percentage": 100 } + ], + "cohorts": [ + { + "name": "employee", + "versions": [{ "version_id": "{new_version_id}", "percentage": 100 }] + }, + { + "name": "beta", + "versions": [ + { "version_id": "{new_version_id}", "percentage": 50 }, + { "version_id": "{stable_version_id}", "percentage": 50 } + ] + } + ] + }' +``` + +This deployment routes 100% of `employee` traffic to the new version, 50% of `beta` traffic to the new version, and all other traffic to the stable version. + +## Route requests to cohorts + +Set the `Cloudflare-Workers-Cohort` header from an upstream Worker or using Transform Rules: + +```js +export default { + async fetch(request, env) { + const user = await getUser(request); + + const headers = new Headers(request.headers); + if (user.email.endsWith("@yourcompany.com")) { + headers.set("Cloudflare-Workers-Cohort", "employee"); + } else if (user.betaTester) { + headers.set("Cloudflare-Workers-Cohort", "beta"); + } + + return env.API_SERVICE.fetch(new Request(request, { headers })); + }, +}; +``` + +## Observability + +The cohort that served each request is available in: + +- [Workers Logpush](/workers/observability/logs/logpush/) via the `ScriptVersion.cohort` field +- [Version metadata binding](/workers/runtime-apis/bindings/version-metadata/) via `env.CF_VERSION_METADATA.cohort` +- [Workers Traces](/workers/observability/traces/) via the `cloudflare.cohort` attribute +- [Workers Logs](/workers/observability/logs/workers-logs/) via the `$workers.cohort` filter in Query Builder + +## Current limitations + +- Cohort deployments are API-only. Wrangler CLI support is not yet available. + +## Learn more + +- [Cohort-based deployments](/workers/configuration/versions-and-deployments/gradual-deployments/cohort-deployments/) +- [Gradual deployments overview](/workers/configuration/versions-and-deployments/gradual-deployments/) diff --git a/src/content/docs/workers/configuration/versions-and-deployments/gradual-deployments.mdx b/src/content/docs/workers/configuration/versions-and-deployments/gradual-deployments.mdx deleted file mode 100644 index a39a086eee2ce8..00000000000000 --- a/src/content/docs/workers/configuration/versions-and-deployments/gradual-deployments.mdx +++ /dev/null @@ -1,296 +0,0 @@ ---- -pcx_content_type: configuration -title: Gradual deployments -head: [] -description: Incrementally deploy code changes to your Workers with gradual deployments. ---- - -import { Example, DashButton } from "~/components"; - -Gradual Deployments give you the ability to incrementally deploy new [versions](/workers/configuration/versions-and-deployments/#versions) of Workers by splitting traffic across versions. - -![Gradual Deployments](~/assets/images/workers/platform/versions-and-deployments/gradual-deployments.png) - -Using gradual deployments, you can: - -- Gradually shift traffic to a newer version of your Worker. -- Monitor error rates and exceptions across versions using [analytics and logs](/workers/configuration/versions-and-deployments/gradual-deployments/#observability) tooling. -- [Roll back](/workers/configuration/versions-and-deployments/rollbacks/) to a previously stable version if you notice issues when deploying a new version. - -## Use gradual deployments - -The following section guides you through an example usage of gradual deployments. You will choose to use either [Wrangler](/workers/configuration/versions-and-deployments/gradual-deployments/#via-wrangler) or the Cloudflare dashboard to: - -- Create a new Worker. -- Publish a new version of that Worker without deploying it. -- Create a gradual deployment between the two versions. -- Progress the deployment of the new version to 100% of traffic. - -### Via Wrangler - -:::note - -Minimum required Wrangler version: 3.40.0. Versions before 3.73.0 require you to specify a `--x-versions` flag. - -::: - -#### 1. Create and deploy a new Worker - -Create a new `"Hello World"` Worker using the [`create-cloudflare` CLI (C3)](/pages/get-started/c3/) and deploy it. - -```sh -npm create cloudflare@latest -- --type=hello-world -``` - -Answer `yes` or `no` to using TypeScript. Answer `yes` to deploying your application. This is the first version of your Worker. - -#### 2. Create a new version of the Worker - -To create a new version of the Worker, edit the Worker code by changing the `Response` content to your desired text and upload the Worker by using the [`wrangler versions upload`](/workers/wrangler/commands/#versions-upload) command. - -```sh -npx wrangler versions upload -``` - -This will create a new version of the Worker that is not automatically deployed. - -#### 3. Create a new deployment - -Use the [`wrangler versions deploy`](/workers/wrangler/commands/#versions-deploy) command to -create a new deployment that splits traffic between two versions of the Worker. Follow the interactive prompts to create a deployment with the versions uploaded in [step #1](/workers/configuration/versions-and-deployments/gradual-deployments/#1-create-and-deploy-a-new-worker) and [step #2](/workers/configuration/versions-and-deployments/gradual-deployments/#2-create-a-new-version-of-the-worker). Select your desired percentages for each version. - -```sh -npx wrangler versions deploy -``` - -#### 4. Test the split deployment - -Run a cURL command on your Worker to test the split deployment. - -```bash -for j in {0..10} -do - curl -s https://$WORKER_NAME.$SUBDOMAIN.workers.dev -done -``` - -You should see 10 responses. Responses will reflect the content returned by the versions in your deployment. Responses will vary depending on the percentages configured in [step #3](/workers/configuration/versions-and-deployments/gradual-deployments/#3-create-a-new-deployment). - -You can test also target a specific version using [version overrides](#version-overrides). - -#### 5. Set your new version to 100% deployment - -Run `wrangler versions deploy` again and follow the interactive prompts. Select the version uploaded in [step 2](/workers/configuration/versions-and-deployments/gradual-deployments/#2-create-a-new-version-of-the-worker) and set it to 100% deployment. - -```sh -npx wrangler versions deploy -``` - -### Via the Cloudflare dashboard - -1. In the Cloudflare dashboard, go to the **Workers & Pages** page. - - - -2. Select **Create application** > **Hello World** template > deploy your Worker. -3. Once the Worker is deployed, go to the online code editor through **Edit code**. Edit the Worker code (change the `Response` content) and upload the Worker. -4. To save changes, select the **down arrow** next to **Deploy** > **Save**. This will create a new version of your Worker. -5. Create a new deployment that splits traffic between the two versions created in step 3 and 5 by going to **Deployments** and selecting **Deploy Version**. -6. cURL your Worker to test the split deployment. - -```bash -for j in {0..10} -do - curl -s https://$WORKER_NAME.$SUBDOMAIN.workers.dev -done -``` - -You should see 10 responses. Responses will reflect the content returned by the versions in your deployment. Responses will vary depending on the percentages configured in step #6. - -## Gradual deployments with static assets - -When your Worker serves [static assets](/workers/static-assets/), gradual deployments can cause asset compatibility issues where users receive HTML from one version that references assets only available in another version, leading to 404 errors. - -For detailed guidance on handling static assets during gradual rollouts, including specific examples and configuration steps, refer to [Gradual rollouts](/workers/static-assets/routing/advanced/gradual-rollouts/). - -## Version affinity - -By default, the percentages configured when using gradual deployments operate on a per-request basis — a request has a X% probability of invoking one of two versions of the Worker in the [deployment](/workers/configuration/versions-and-deployments/#deployments). - -You may want requests associated with a particular identifier (such as user, session, or any unique ID) to be handled by a consistent version of your Worker to prevent version skew. Version skew occurs when there are multiple versions of an application deployed that are not forwards/backwards compatible. You can configure version affinity to prevent the Worker's version from changing back and forth on a per-request basis. - -You can do this by setting the `Cloudflare-Workers-Version-Key` header on the incoming request to your Worker. For example: - -```sh -curl -s https://example.com -H 'Cloudflare-Workers-Version-Key: foo' -``` - -For a given [deployment](/workers/configuration/versions-and-deployments/#deployments), all requests with a version key set to `foo` will be handled by the same version of your Worker. The specific version of your Worker that the version key `foo` corresponds to is determined by the percentages you have configured for each Worker version in your deployment. - -You can set the `Cloudflare-Workers-Version-Key` header both when making an external request from the Internet to your Worker, as well as when making a subrequest from one Worker to another Worker using a [service binding](/workers/runtime-apis/bindings/service-bindings/). - -### Setting `Cloudflare-Workers-Version-Key` using Ruleset Engine - -You may want to extract a version key from certain properties of your request such as the URL, headers or cookies. You can configure a [Ruleset Engine](/ruleset-engine/) rule on your zone to do this. This allows you to specify version affinity based on these properties without having to modify the external client that makes the request. - -For example, if your worker serves video assets under the URI path `/assets/` and you wanted requests to each unique asset to be handled by a consistent version, you could define the following [request header transform rule](/rules/transform/request-header-modification/): - - - -Text in **Expression Editor**: - -```txt -starts_with(http.request.uri.path, "/asset/") -``` - -Selected operation under **Modify request header**: _Set dynamic_ - -**Header name**: `Cloudflare-Workers-Version-Key` - -**Value**: `regex_replace(http.request.uri.path, "/asset/(.*)", "${1}")` - - - -## Version overrides - -You can use version overrides to send a request to a specific version of your Worker in your gradual deployment. - -To specify a version override in your request, you can set the `Cloudflare-Workers-Version-Overrides` header on the request to your Worker. For example: - -```sh -curl -s https://example.com -H 'Cloudflare-Workers-Version-Overrides: my-worker-name="dc8dcd28-271b-4367-9840-6c244f84cb40"' -``` - -`Cloudflare-Workers-Version-Overrides` is a [Dictionary Structured Header](https://www.rfc-editor.org/rfc/rfc8941#name-dictionaries). - -The dictionary can contain multiple key-value pairs. Each key indicates the name of the Worker the override should be applied to. The value indicates the version ID that should be used and must be a [String](https://www.rfc-editor.org/rfc/rfc8941#name-strings). - -A version override will only be applied if the specified version is in the current deployment. The versions in the current deployment can be found using the [`wrangler deployments list`](/workers/wrangler/commands/#deployments-list) command or on the **Workers & Pages** page of the Cloudflare dashboard > Select your Workers > Deployments > Active Deployment. - -:::note[Verifying that the version override was applied] - -There are a number of reasons why a request's version override may not be applied. For example: - -- The deployment containing the specified version may not have propagated yet. -- The header value may not be a valid [Dictionary](https://www.rfc-editor.org/rfc/rfc8941#name-dictionaries). - -In the case that a request's version override is not applied, the request will be routed according to the percentages set in the gradual deployment configuration. - -To make sure that the request's version override was applied correctly, you can [observe](#observability) the version of your Worker that was invoked. You could even automate this check by using the [runtime binding](#runtime-binding) to return the version in the Worker's response. - -::: - -### Example - -You may want to test a new version in production before gradually deploying it to an increasing proportion of external traffic. - -In this example, your deployment is initially configured to route all traffic to a single version: - -| Version ID | Percentage | -| :----------------------------------: | :--------: | -| db7cd8d3-4425-4fe7-8c81-01bf963b6067 | 100% | - -Create a new deployment using [`wrangler versions deploy`](/workers/wrangler/commands/#versions-deploy) and specify 0% for the new version whilst keeping the previous version at 100%. - -| Version ID | Percentage | -| :----------------------------------: | :--------: | -| dc8dcd28-271b-4367-9840-6c244f84cb40 | 0% | -| db7cd8d3-4425-4fe7-8c81-01bf963b6067 | 100% | - -Now test the new version with a version override before gradually progressing the new version to 100%: - -```sh -curl -s https://example.com -H 'Cloudflare-Workers-Version-Overrides: my-worker-name="dc8dcd28-271b-4367-9840-6c244f84cb40"' -``` - -## Gradual deployments for Durable Objects - -To provide [global uniqueness](/durable-objects/platform/known-issues/#global-uniqueness), only one version of each [Durable Object](/durable-objects/) can run at a time. This means that gradual deployments work slightly differently for Durable Objects. - -When you create a new gradual deployment for a Worker with Durable Objects, each Durable Object is assigned a Worker version based on the percentages you configured in your [deployment](/workers/configuration/versions-and-deployments/#deployments). This version will not change until you create a new deployment. - -![Gradual Deployments Durable Objects](~/assets/images/workers/platform/versions-and-deployments/durable-objects.png) - -### Example - -This example assumes that you have previously created 3 Durable Object instances with names "foo", "bar" and "baz". - -Your Worker is currently on a version that we will call version "A" and you want to gradually deploy a new version "B" of your Worker. - -Here is how the versions of your Durable Objects might change as you progress your gradual deployment: - -| Deployment config | "foo" | "bar" | "baz" | -| :---------------------------------: | :---: | :---: | :---: | -| Version A: 100%
| A | A | A | -| Version B: 20%
Version A: 80% | B | A | A | -| Version B: 50%
Version A: 50% | B | B | A | -| Version B: 100%
| B | B | B | - -This is only an example, so the versions assigned to your Durable Objects may be different. However, the following is guaranteed: - -- For a given deployment, requests to each Durable Object will always use the same Worker version. -- When you specify each version in the same order as the previous deployment and increase the percentage of a version, Durable Objects which were previously assigned that version will not be assigned a different version. In this example, Durable Object "foo" would never revert from version "B" to version "A". -- The Durable Object will only be [reset](/durable-objects/observability/troubleshooting/#durable-object-reset-because-its-code-was-updated) when it is assigned a different version, so each Durable Object will only be reset once in this example. - -:::note -Typically, a Worker bundle will define both the Durable Object class and a Worker that interacts with it. In this case, you cannot deploy changes to your Durable Object and its Worker independently. - -You should ensure that API changes between your Durable Object and its Worker are [forwards and backwards compatible](/durable-objects/platform/known-issues/#code-updates) whether you are using gradual deployments or not. However, using gradual deployments will make it even more likely that different versions of your Durable Objects and its Worker will interact with each other. -::: - -### Migrations - -Versions of Worker bundles containing new Durable Object migrations cannot be uploaded. This is because Durable Object migrations are atomic operations. Durable Object migrations can be deployed with the following command: - -```sh -npx wrangler versions deploy -``` - -To limit the blast radius of Durable Object migration deployments, migrations should be deployed independently of other code changes. - -To understand why Durable Object migrations are atomic operations, consider the hypothetical example of gradually deploying a delete migration. If a delete migration were applied to 50% of Durable Object instances, then Workers requesting those Durable Object instances would fail because they would have been deleted. - -To do this without producing errors, a version of the Worker which does not depend on any Durable Object instances would have to have already been rolled out. Then, you can deploy a delete migration without affecting any traffic and there is no reason to do so gradually. - -## Observability - -When using gradual deployments, you may want to attribute Workers invocations to a specific version in order to get visibility into the impact of deploying new versions. - -### Logpush - -A new `ScriptVersion` object is available in [Workers Logpush](/workers/observability/logs/logpush/). `ScriptVersion` can only be added through the Logpush API right now. Sample API call: - -```bash -curl -X POST 'https://api.cloudflare.com/client/v4/accounts//logpush/jobs' \ --H 'Authorization: Bearer ' \ --H 'Content-Type: application/json' \ --d '{ -"name": "workers-logpush", -"output_options": { - "field_names": ["Event", "EventTimestampMs", "Outcome", "Logs", "ScriptName", "ScriptVersion"], -}, -"destination_conf": "", -"dataset": "workers_trace_events", -"enabled": true -}'| jq . -``` - -`ScriptVersion` is an object with the following structure: - -```json -scriptVersion: { - id: "", - message: "", - tag: "" -} -``` - -### Runtime binding - -Use the [Version metadata binding](/workers/runtime-apis/bindings/version-metadata/) in to access version ID or version tag in your Worker. - -## Limits - -### Deployments limit - -You can only create a new deployment with the last 100 uploaded versions of your Worker. diff --git a/src/content/docs/workers/configuration/versions-and-deployments/gradual-deployments/cohort-deployments.mdx b/src/content/docs/workers/configuration/versions-and-deployments/gradual-deployments/cohort-deployments.mdx new file mode 100644 index 00000000000000..d4a6357f806b4c --- /dev/null +++ b/src/content/docs/workers/configuration/versions-and-deployments/gradual-deployments/cohort-deployments.mdx @@ -0,0 +1,220 @@ +--- +pcx_content_type: configuration +title: Cohort-based deployments +head: [] +description: Deploy Worker versions to specific user groups before rolling out to all traffic. +sidebar: + order: 3 + badge: + text: Beta +--- + +import { Example } from "~/components"; + +Cohort-based deployments let you roll out Worker versions to specific groups of users before deploying to everyone. You define cohorts when creating a deployment via the REST API, then route requests to cohorts using the `Cloudflare-Workers-Cohort` header. + +Use cohort-based deployments when you want to: + +- Deploy to internal employees first, where breaking changes have lower impact +- Roll out to beta testers who opted in to early access +- Deploy to free tier users before paid or enterprise customers +- Use different rollout speeds for different user segments + +For simple traffic splitting without user segmentation, use [percentage-based deployments](/workers/configuration/versions-and-deployments/gradual-deployments/percentage-deployments/). + +## How cohorts work + +Each cohort has its own version configuration with independent percentages. Requests with the `Cloudflare-Workers-Cohort` header route to the matching cohort's configuration. Requests without the header use the default `versions` configuration. + +```mermaid +sequenceDiagram + participant User as User Request + participant GW as Gateway Worker + participant API as API Worker (v1 + v2) + + Note over API: Deployment:
employee → v2 (100%)
beta → v2 (50%), v1 (50%)
default → v1 (100%) + + User->>GW: Request from employee + GW->>API: Cloudflare-Workers-Cohort: employee + API-->>User: Response from v2 + + User->>GW: Request from beta tester + GW->>API: Cloudflare-Workers-Cohort: beta + API-->>User: Response from v2 or v1 (50/50) + + User->>GW: Request from regular user + GW->>API: (no cohort header) + API-->>User: Response from v1 +``` + +## Create a deployment with cohorts + +Use the REST API to create a cohort-based deployment. This example deploys a new version to 100% of employees, 50% of beta testers, and 0% of everyone else: + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/{account_id}/workers/scripts/{script_name}/deployments" \ + -H "Authorization: Bearer {api_token}" \ + -H "Content-Type: application/json" \ + -d '{ + "strategy": "cohorts", + "versions": [ + { "version_id": "{stable_version_id}", "percentage": 100 } + ], + "cohorts": [ + { + "name": "employee", + "versions": [ + { "version_id": "{new_version_id}", "percentage": 100 } + ] + }, + { + "name": "beta", + "versions": [ + { "version_id": "{new_version_id}", "percentage": 50 }, + { "version_id": "{stable_version_id}", "percentage": 50 } + ] + } + ] + }' +``` + +### Request fields + +| Field | Type | Required | Description | +| -------------------- | ------ | -------- | ---------------------------------------------------------------------------- | +| `strategy` | string | Yes | Set to `"cohorts"` | +| `versions` | array | Yes | Default version configuration when no cohort matches | +| `cohorts` | array | Yes | Array of cohort configurations | +| `cohorts[].name` | string | Yes | Cohort identifier (1-64 chars, alphanumeric, hyphens, underscores) | +| `cohorts[].versions` | array | Yes | Version configuration for this cohort (1-2 versions, percentages sum to 100) | + +## Route requests to cohorts + +Set the `Cloudflare-Workers-Cohort` header on incoming requests. You can do this from an upstream Worker or using Transform Rules. + +### From an upstream Worker + +Use a gateway Worker to determine the cohort based on user properties: + +```js +export default { + async fetch(request, env) { + const user = await getUser(request); + const cohort = determineCohort(user); + + const headers = new Headers(request.headers); + headers.set("Cloudflare-Workers-Cohort", cohort); + + return env.API_SERVICE.fetch(new Request(request, { headers })); + }, +}; + +function determineCohort(user) { + if (user.email.endsWith("@yourcompany.com")) return "employee"; + if (user.betaTester) return "beta"; + if (user.plan === "free") return "free"; + return "paid"; +} +``` + +### Using Transform Rules + +Set the header based on request properties using a [request header modification rule](/rules/transform/request-header-modification/): + + + +Text in **Expression Editor**: + +```txt +http.cookie contains "employee=true" +``` + +Selected operation under **Modify request header**: _Set static_ + +**Header name**: `Cloudflare-Workers-Cohort` + +**Value**: `employee` + + + +### Cohort header behavior + +- **Case-insensitive matching**: `EMPLOYEE` matches cohort `employee` +- **Unmatched cohorts**: If the header value does not match any cohort, the default `versions` configuration is used +- **Maximum length**: 64 characters +- **Valid characters**: `a-z`, `A-Z`, `0-9`, `-`, `_` + +## Advance the rollout + +Create a new deployment with updated percentages. This example increases the rollout to beta users and starts rolling out to the default (non-cohort) traffic: + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/{account_id}/workers/scripts/{script_name}/deployments" \ + -H "Authorization: Bearer {api_token}" \ + -H "Content-Type: application/json" \ + -d '{ + "strategy": "cohorts", + "versions": [ + { "version_id": "{new_version_id}", "percentage": 25 }, + { "version_id": "{stable_version_id}", "percentage": 75 } + ], + "cohorts": [ + { + "name": "employee", + "versions": [{ "version_id": "{new_version_id}", "percentage": 100 }] + }, + { + "name": "beta", + "versions": [{ "version_id": "{new_version_id}", "percentage": 100 }] + } + ] + }' +``` + +## Complete the rollout + +Once validation is complete, switch to the percentage strategy to deploy to 100% of all traffic: + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/{account_id}/workers/scripts/{script_name}/deployments" \ + -H "Authorization: Bearer {api_token}" \ + -H "Content-Type: application/json" \ + -d '{ + "strategy": "percentage", + "versions": [{ "version_id": "{new_version_id}", "percentage": 100 }] + }' +``` + +## Get current deployment + +Retrieve the active deployment to see the current cohort configuration: + +```bash +curl "https://api.cloudflare.com/client/v4/accounts/{account_id}/workers/scripts/{script_name}/deployments" \ + -H "Authorization: Bearer {api_token}" +``` + +## Constraints + +| Constraint | Limit | +| ------------------------------ | ------------------------------------------------------- | +| Maximum cohorts per deployment | 10 | +| Maximum versions per cohort | 2 | +| Cohort name length | 1-64 characters | +| Cohort name pattern | Alphanumeric, hyphens, underscores (`^[a-zA-Z0-9_-]+$`) | + +Additional requirements: + +- Percentages within each cohort must sum to 100 +- Cohort names must be unique within a deployment +- You must provide a `versions` array as the default configuration + +## Current limitations + +- **Wrangler CLI**: Cohort deployments are API-only. The `wrangler versions deploy` command does not yet support cohorts. + +## Related + +- [Version affinity](/workers/configuration/versions-and-deployments/gradual-deployments/#version-affinity) - ensure consistent version selection per user within a cohort +- [Version overrides](/workers/configuration/versions-and-deployments/gradual-deployments/#version-overrides) - test a specific version +- [Observability](/workers/configuration/versions-and-deployments/gradual-deployments/#observability) - track which version and cohort handled each request diff --git a/src/content/docs/workers/configuration/versions-and-deployments/gradual-deployments/index.mdx b/src/content/docs/workers/configuration/versions-and-deployments/gradual-deployments/index.mdx new file mode 100644 index 00000000000000..b4d3e5b810a854 --- /dev/null +++ b/src/content/docs/workers/configuration/versions-and-deployments/gradual-deployments/index.mdx @@ -0,0 +1,188 @@ +--- +pcx_content_type: concept +title: Gradual deployments +head: [] +description: Incrementally deploy changes by splitting traffic across Worker versions. +--- + +import { CardGrid, Example, LinkTitleCard } from "~/components"; + +Gradual deployments let you incrementally roll out new [versions](/workers/configuration/versions-and-deployments/) of your Worker by splitting traffic across versions. Instead of deploying to 100% of traffic immediately, you can shift traffic gradually while monitoring for errors. + +![Gradual Deployments](~/assets/images/workers/platform/versions-and-deployments/gradual-deployments.png) + +## Benefits + +- **Reduce risk**: Limit exposure to bugs by starting with a small percentage of traffic +- **Monitor impact**: Compare error rates and performance across versions before full rollout +- **Quick rollback**: Shift traffic back to the previous version if issues arise + +## Choose a strategy + +Gradual deployments support two strategies, Percentage-based deployments, and Cohort-based deployments, which can be used for different scenarios: + +| Scenario | Strategy | +| ------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | +| Split traffic randomly between versions | [Percentage-based](/workers/configuration/versions-and-deployments/gradual-deployments/percentage-deployments/) | +| Deploy to specific user groups first (employees, beta testers, free tier) | [Cohort-based](/workers/configuration/versions-and-deployments/gradual-deployments/cohort-deployments/) | +| Different rollout speeds for different user segments | [Cohort-based](/workers/configuration/versions-and-deployments/gradual-deployments/cohort-deployments/) | + +## Version affinity + +By default, each request is independently routed based on the configured percentages. A user might hit version A on one request and version B on the next. + +To ensure a user consistently hits the same version, set the `Cloudflare-Workers-Version-Key` header. All requests with the same key value route to the same version for a given deployment. + +```sh +curl https://example.com -H 'Cloudflare-Workers-Version-Key: user-12345' +``` + +Version affinity works with both percentage-based and cohort-based deployments. For cohort-based deployments, the cohort header selects the version configuration, and the version key ensures consistency within that configuration. + +### Set version key using Transform Rules + +Extract a version key from request properties using a [request header modification rule](/rules/transform/request-header-modification/): + + + +Text in **Expression Editor**: + +```txt +http.cookie contains "session_id" +``` + +Selected operation under **Modify request header**: _Set dynamic_ + +**Header name**: `Cloudflare-Workers-Version-Key` + +**Value**: `http.request.cookies["session_id"][0]` + + + +## Version overrides + +Force a request to a specific version using the `Cloudflare-Workers-Version-Overrides` header. This is useful for testing a new version before shifting any traffic to it. + +```sh +curl https://example.com -H 'Cloudflare-Workers-Version-Overrides: my-worker="dc8dcd28-271b-4367-9840-6c244f84cb40"' +``` + +The header is a [Dictionary Structured Header](https://www.rfc-editor.org/rfc/rfc8941#name-dictionaries). Each key is a Worker name, and each value is a version ID as a string. + +Version overrides only work for versions in the current deployment. Find version IDs with `wrangler deployments list` or in the Dashboard under **Deployments**. + +:::note +If the override cannot be applied (version not in deployment, invalid header format), the request routes normally based on percentages or cohorts. +::: + +## Header precedence + +Multiple headers can affect version routing. They are evaluated in this order: + +| Priority | Header | Behavior | +| ----------- | -------------------------------------- | ------------------------------------------ | +| 1 (highest) | `Cloudflare-Workers-Version-Overrides` | Routes to the specified version directly | +| 2 | `Cloudflare-Workers-Cohort` | Selects the cohort's version configuration | +| 3 | `Cloudflare-Workers-Version-Key` | Ensures consistent version selection | +| 4 (lowest) | (none) | Random selection based on percentages | + +## Static assets + +When your Worker serves [static assets](/workers/static-assets/), gradual deployments can cause asset mismatches. A user might receive HTML from version A that references JavaScript files only available in version B. + +Use [version affinity](#version-affinity) to ensure users consistently hit the same version. Refer to [Gradual rollouts for static assets](/workers/static-assets/routing/advanced/gradual-rollouts/) for detailed guidance. + +## Durable Objects + +[Durable Objects](/durable-objects/) require [global uniqueness](/durable-objects/platform/known-issues/#global-uniqueness) — only one instance of a Durable Object can run at a time. During gradual deployments, each Durable Object instance is assigned to a single version based on the deployment percentages. This assignment remains stable until you create a new deployment. + +![Gradual Deployments Durable Objects](~/assets/images/workers/platform/versions-and-deployments/durable-objects.png) + +| Deployment config | DO "foo" | DO "bar" | DO "baz" | +| :----------------------------- | :------: | :------: | :------: | +| Version A: 100% | A | A | A | +| Version B: 20%, Version A: 80% | B | A | A | +| Version B: 50%, Version A: 50% | B | B | A | +| Version B: 100% | B | B | B | + +**Guarantees:** + +- For a given deployment, a Durable Object always uses the same version +- When increasing a version's percentage, DOs assigned to that version stay on it +- A DO only [resets](/durable-objects/observability/troubleshooting/#durable-object-reset-because-its-code-was-updated) when assigned to a different version + +:::note +Ensure API changes between your Durable Object and Worker are forwards and backwards compatible. Different versions may interact during gradual rollouts. +::: + +### Durable Object migrations + +[DO migrations](/durable-objects/reference/durable-objects-migrations/) are atomic and cannot be deployed gradually. Use `wrangler deploy` for migrations, and deploy them separately from code changes. + +## Observability + +Track which version handled each request to monitor the impact of deployments. + +### Logpush + +Include `ScriptVersion` in your [Workers Logpush](/workers/observability/logs/logpush/) configuration: + +```bash +curl -X POST 'https://api.cloudflare.com/client/v4/accounts/{account_id}/logpush/jobs' \ +-H 'Authorization: Bearer {api_token}' \ +-H 'Content-Type: application/json' \ +-d '{ + "name": "workers-logpush", + "output_options": { + "field_names": ["Event", "EventTimestampMs", "Outcome", "Logs", "ScriptName", "ScriptVersion"] + }, + "destination_conf": "{destination_url}", + "dataset": "workers_trace_events", + "enabled": true +}' +``` + +`ScriptVersion` contains: + +```json +{ + "scriptVersion": { + "id": "", + "message": "", + "tag": "", + "cohort": "" + } +} +``` + +The `cohort` field contains the matched cohort name, or `null` for percentage-based deployments. + +### Runtime binding + +Access version metadata in your Worker using the [version metadata binding](/workers/runtime-apis/bindings/version-metadata/): + +```js +export default { + async fetch(request, env) { + const { id, tag, cohort } = env.CF_VERSION_METADATA; + console.log({ versionId: id, cohort }); + // ... + }, +}; +``` + +### Workers Logs and Traces + +- **[Workers Logs](/workers/observability/logs/workers-logs/)**: Filter by `$workers.cohort` in [Query Builder](/workers/observability/query-builder/) +- **[Workers Traces](/workers/observability/traces/)**: The `cloudflare.cohort` attribute is available on root spans + +## Limits + +| Limit | Value | +| --------------------------------- | ------------------ | +| Versions available for deployment | Last 100 | +| Versions per deployment | 2 | +| Maximum cohorts per deployment | 10 | +| Maximum versions per cohort | 2 | +| Cohort name length | 1-64 characters | +| Cohort name pattern | `^[a-zA-Z0-9_-]+$` | diff --git a/src/content/docs/workers/configuration/versions-and-deployments/gradual-deployments/percentage-deployments.mdx b/src/content/docs/workers/configuration/versions-and-deployments/gradual-deployments/percentage-deployments.mdx new file mode 100644 index 00000000000000..1000dc1b8ebabd --- /dev/null +++ b/src/content/docs/workers/configuration/versions-and-deployments/gradual-deployments/percentage-deployments.mdx @@ -0,0 +1,150 @@ +--- +pcx_content_type: configuration +title: Percentage-based deployments +head: [] +description: Split traffic randomly between Worker versions based on configured percentages. +sidebar: + order: 2 +--- + +import { DashButton } from "~/components"; + +Percentage-based deployments split traffic randomly between two versions. Each request has an X% probability of being routed to a given version, regardless of user or session. + +```json +{ + "strategy": "percentage", + "versions": [ + { "version_id": "abc123...", "percentage": 10 }, + { "version_id": "def456...", "percentage": 90 } + ] +} +``` + +Use percentage-based deployments when you want to gradually shift traffic without distinguishing between user types. For deploying to specific user groups first (employees, beta testers), use [cohort-based deployments](/workers/configuration/versions-and-deployments/gradual-deployments/cohort-deployments/). + +## Create a percentage-based deployment + +### Via Wrangler + +:::note +Minimum required Wrangler version: 3.40.0. Versions before 3.73.0 require the `--x-versions` flag. +::: + +#### 1. Create and deploy a Worker + +Create a new Worker using the [`create-cloudflare` CLI (C3)](/pages/get-started/c3/) and deploy it: + +```sh +npm create cloudflare@latest -- --type=hello-world +``` + +Answer `yes` to deploy. This creates the first version of your Worker. + +#### 2. Upload a new version + +Edit the Worker code, then upload without deploying: + +```sh +npx wrangler versions upload +``` + +This creates a new version that is not yet serving traffic. + +#### 3. Create a split deployment + +Run `wrangler versions deploy` and follow the prompts to split traffic between versions: + +```sh +npx wrangler versions deploy +``` + +Select both versions and assign percentages (for example, 10% new version, 90% old version). + +#### 4. Test the deployment + +Send multiple requests to see responses from both versions: + +```bash +for i in {1..10}; do + curl -s https://$WORKER_NAME.$SUBDOMAIN.workers.dev +done +``` + +Responses vary based on the configured percentages. + +#### 5. Complete the rollout + +Run `wrangler versions deploy` again and set the new version to 100%: + +```sh +npx wrangler versions deploy +``` + +### Via the Dashboard + +1. Go to **Workers & Pages** in the Cloudflare dashboard. + + + +2. Select **Create application** > **Hello World** template > deploy your Worker. +3. Select **Edit code**, make changes, then select the dropdown next to **Deploy** > **Save**. This creates a new version without deploying. +4. Go to **Deployments** > **Deploy Version**. +5. Select both versions and assign percentages. +6. Test with multiple requests, then increase the new version to 100% when ready. + +### Via REST API + +Create a deployment with the [Deployments API](https://developers.cloudflare.com/api/resources/workers/subresources/scripts/subresources/deployments/methods/create/): + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/{account_id}/workers/scripts/{script_name}/deployments" \ + -H "Authorization: Bearer {api_token}" \ + -H "Content-Type: application/json" \ + -d '{ + "strategy": "percentage", + "versions": [ + { "version_id": "{new_version_id}", "percentage": 10 }, + { "version_id": "{stable_version_id}", "percentage": 90 } + ] + }' +``` + +## Advance the rollout + +Create a new deployment with updated percentages: + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/{account_id}/workers/scripts/{script_name}/deployments" \ + -H "Authorization: Bearer {api_token}" \ + -H "Content-Type: application/json" \ + -d '{ + "strategy": "percentage", + "versions": [ + { "version_id": "{new_version_id}", "percentage": 50 }, + { "version_id": "{stable_version_id}", "percentage": 50 } + ] + }' +``` + +## Complete the rollout + +Deploy the new version to 100% of traffic: + +```bash +curl -X POST "https://api.cloudflare.com/client/v4/accounts/{account_id}/workers/scripts/{script_name}/deployments" \ + -H "Authorization: Bearer {api_token}" \ + -H "Content-Type: application/json" \ + -d '{ + "strategy": "percentage", + "versions": [ + { "version_id": "{new_version_id}", "percentage": 100 } + ] + }' +``` + +## Related + +- [Version affinity](/workers/configuration/versions-and-deployments/gradual-deployments/#version-affinity) - ensure consistent version selection per user +- [Version overrides](/workers/configuration/versions-and-deployments/gradual-deployments/#version-overrides) - test a specific version +- [Observability](/workers/configuration/versions-and-deployments/gradual-deployments/#observability) - track which version handled each request diff --git a/src/content/docs/workers/configuration/versions-and-deployments/index.mdx b/src/content/docs/workers/configuration/versions-and-deployments/index.mdx index 5aba8e440346e4..16ce1184619446 100644 --- a/src/content/docs/workers/configuration/versions-and-deployments/index.mdx +++ b/src/content/docs/workers/configuration/versions-and-deployments/index.mdx @@ -1,109 +1,115 @@ --- -pcx_content_type: concept -title: Versions & Deployments +pcx_content_type: overview +title: Versions and deployments head: [] description: Upload versions of Workers and create deployments to release new versions. --- -import { DashButton } from "~/components"; -Versions track changes to your Worker. Deployments configure how those changes are deployed to your traffic. +import { + CardGrid, + Description, + DirectoryListing, + LinkButton, + LinkTitleCard, + Plan, + RelatedProduct, +} from "~/components"; -You can upload changes (versions) to your Worker independent of changing the version that is actively serving traffic (deployment). - -![Versions and Deployments](~/assets/images/workers/platform/versions-and-deployments/versions-and-deployments.png) - -Using versions and deployments is useful if: - -- You are running critical applications on Workers and want to reduce risk when deploying new versions of your Worker using a rolling deployment strategy. -- You want to monitor for performance differences when deploying new versions of your Worker. -- You have a CI/CD pipeline configured for Workers but want to cut manual releases. +Versions and deployments separate uploading code from deploying it. You can upload changes to your Worker without affecting live traffic, then control exactly how those changes roll out — by deploying to [a percentage of traffic](/workers/configuration/versions-and-deployments/gradual-deployments/percentage-deployments/) or to [specific cohorts of users](/workers/configuration/versions-and-deployments/gradual-deployments/cohort-deployments/). ## Versions -A version is defined by the state of code as well as the state of configuration in a Worker's [Wrangler configuration file](/workers/wrangler/configuration/). Versions track historical changes to [bundled code](/workers/wrangler/bundling/), [static assets](/workers/static-assets/) and changes to configuration like [bindings](/workers/runtime-apis/bindings/) and [compatibility date and compatibility flags](/workers/configuration/compatibility-dates/) over time. +A version is a snapshot of your Worker's code and configuration at a point in time. Each version includes: -Versions also track metadata associated with a version, including: the version ID, the user that created the version, deploy source, and timestamp. Optionally, a version message and version tag can be configured on version upload. - -:::note -State changes for associated Workers [storage resources](/workers/platform/storage-options/) such as [KV](/kv/), [R2](/r2/), [Durable Objects](/durable-objects/) and [D1](/d1/) are not tracked with versions. -::: +- [Bundled code](/workers/wrangler/bundling/) and [static assets](/workers/static-assets/) +- [Bindings](/workers/runtime-apis/bindings/) configuration +- [Compatibility date and flags](/workers/configuration/compatibility-dates/) +- Metadata (version ID, author, timestamp, optional message and tag) ## Deployments -Deployments track the version(s) of your Worker that are actively serving traffic. A deployment can consist of one or two versions of a Worker. +A deployment determines which version(s) of your Worker serve traffic. A deployment can include one or two versions. -By default, Workers supports an all-at-once deployment model where traffic is immediately shifted from one version to the newly deployed version automatically. Alternatively, you can use [gradual deployments](/workers/configuration/versions-and-deployments/gradual-deployments/) to create a rolling deployment strategy. +By default, `wrangler deploy` uploads a new version and immediately deploys it to 100% of traffic. For more control, you can: -You can also track metadata associated with a deployment, including: the user that created the deployment, deploy source, timestamp and the version(s) in the deployment. Optionally, you can configure a deployment message when you create a deployment. +1. Upload a version without deploying it using `wrangler versions upload` +2. Deploy it later using `wrangler versions deploy` or [gradual deployments](/workers/configuration/versions-and-deployments/gradual-deployments/) -## Use versions and deployments +## Gradual deployments -### Create a new version +Instead of deploying to 100% of traffic immediately, gradual deployments let you split traffic across versions. This reduces risk by limiting exposure to potential bugs, and lets you monitor error rates before full rollout. -Review the different ways you can create versions of your Worker and deploy them. +Gradual deployments support two strategies: -#### Upload a new version and deploy it immediately +| Strategy | Use when | +| --------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | +| [Percentage-based](/workers/configuration/versions-and-deployments/gradual-deployments/percentage-deployments/) | You want to split traffic randomly (for example, 10% new version, 90% old version) | +| [Cohort-based](/workers/configuration/versions-and-deployments/gradual-deployments/cohort-deployments/) | You want to deploy to specific user groups first (employees, beta testers, free tier) | -A new version that is automatically deployed to 100% of traffic when: +Refer to [Gradual deployments](/workers/configuration/versions-and-deployments/gradual-deployments/) for an overview of both strategies and shared concepts like version affinity, version overrides, and observability. -- Changes are uploaded with [`wrangler deploy`](/workers/wrangler/commands/#deploy) via the Cloudflare Dashboard -- Changes are deployed with the command [`npx wrangler deploy`](/workers/wrangler/commands/#deploy) via [Workers Builds](/workers/ci-cd/builds) -- Changes are uploaded with the [Workers Script Upload API](/api/resources/workers/subresources/scripts/methods/update/) - -#### Upload a new version to be gradually deployed or deployed at a later time - -:::note - -Wrangler versions before 3.73.0 require you to specify a `--x-versions` flag. - -::: +## Create and view versions -To create a new version of your Worker that is not deployed immediately, use the [`wrangler versions upload`](/workers/wrangler/commands/#versions-upload) command or create a new version via the Cloudflare dashboard using the **Save** button. You can find the **Save** option under the down arrow beside the "Deploy" button. +### Create a version -Versions created in this way can then be deployed all at once or gradually deployed using the [`wrangler versions deploy`](/workers/wrangler/commands/#versions-deploy) command or via the Cloudflare dashboard under the **Deployments** tab. +| Method | Deploys immediately? | +| ---------------------------------------------------------------------------------------- | -------------------- | +| `wrangler deploy` | Yes (100% traffic) | +| `wrangler versions upload` | No | +| Dashboard **Deploy** button | Yes (100% traffic) | +| Dashboard **Save** button | No | +| [Workers Script Upload API](/api/resources/workers/subresources/scripts/methods/update/) | Yes (100% traffic) | :::note -When using [Wrangler](/workers/wrangler/), changes made to a Worker's triggers [routes, domains](/workers/configuration/routing/) or [cron triggers](/workers/configuration/cron-triggers/) need to be applied with the command [`wrangler triggers deploy`](/workers/wrangler/commands/#triggers). +You must use `wrangler deploy` or the Dashboard for your first upload. `wrangler versions upload` fails on new Workers. ::: -:::note -New versions are not created when you make changes to [resources connected to your Worker](/workers/runtime-apis/bindings/). For example, if two Workers (Worker A and Worker B) are connected via a [service binding](/workers/runtime-apis/bindings/service-bindings/), changing the code of Worker B will not create a new version of Worker A. Changing the code of Worker B will only create a new version of Worker B. Changes to the service binding (such as, deleting the binding or updating the [environment](/workers/wrangler/environments/) it points to) on Worker A will also not create a new version of Worker B. -::: - -#### Directly manage Versions and Deployments - -See examples of creating a Worker, Versions, and Deployments directly with the API, library SDKs, and Terraform in [Infrastructure as Code](/workers/platform/infrastructure-as-code/). - ### View versions and deployments -#### Via Wrangler +Use `wrangler versions list` and `wrangler deployments list` to view the 100 most recent versions and deployments. In the Dashboard, go to **Workers & Pages** > select your Worker > **Deployments**. -Wrangler allows you to view the 100 most recent versions and deployments. Refer to the [`versions list`](/workers/wrangler/commands/#list-4) and [`deployments`](/workers/wrangler/commands/#list-5) documentation to view the commands. +## Next steps -#### Via the Cloudflare dashboard + -To view your deployments in the Cloudflare dashboard: + + Overview of deployment strategies, version affinity, and observability. + -1. In the Cloudflare dashboard, go to the **Workers & Pages** page. + + Split traffic randomly between versions using Wrangler, Dashboard, or API. + - + + Deploy to specific user groups before rolling out to everyone. + -2. Select your Worker > **Deployments**. + + Revert to a previous version of your Worker. + -## Limits + -### First upload - -You must use [C3](/workers/get-started/guide/#1-create-a-new-worker-project) or [`wrangler deploy`](/workers/wrangler/commands/#deploy) the first time you create a new Workers project. Using [`wrangler versions upload`](/workers/wrangler/commands/#versions-upload) the first time you upload a Worker will fail. - -### Service worker syntax - -Service worker syntax is not supported for versions that are uploaded through [`wrangler versions upload`](/workers/wrangler/commands/#versions-upload). You must use ES modules format. - -Refer to [Migrate from Service Workers to ES modules](/workers/reference/migrate-to-module-workers/#advantages-of-migrating) to learn how to migrate your Workers from the service worker format to the ES modules format. +## Limits -### Durable Object migrations +| Limit | Value | +| --------------------------------- | ----------------- | +| Versions available for deployment | Last 100 uploaded | +| Versions per deployment | 2 | -Uploading a version with [Durable Object migrations](/durable-objects/reference/durable-objects-migrations/) is not supported. Use [`wrangler deploy`](/workers/wrangler/commands/#deploy) if you are applying a [Durable Object migration](/durable-objects/reference/durable-objects-migrations/). +### Unsupported features -This will be supported in the near future. +- **Service worker syntax**: Use [ES modules format](/workers/reference/migrate-to-module-workers/) with `wrangler versions upload` +- **Durable Object migrations**: Use `wrangler deploy` for [DO migrations](/durable-objects/reference/durable-objects-migrations/) diff --git a/src/content/docs/workers/observability/query-builder.mdx b/src/content/docs/workers/observability/query-builder.mdx index 9b60c683d36576..68cc88b028cb3c 100644 --- a/src/content/docs/workers/observability/query-builder.mdx +++ b/src/content/docs/workers/observability/query-builder.mdx @@ -5,10 +5,18 @@ head: [] description: Write structured queries to investigate and visualize your telemetry data. sidebar: order: 4 - --- -import { TabItem, Tabs, Render, WranglerConfig, YouTube, Markdown, DashButton, Steps } from "~/components" +import { + TabItem, + Tabs, + Render, + WranglerConfig, + YouTube, + Markdown, + DashButton, + Steps, +} from "~/components"; The Query Builder helps you write structured queries to investigate and visualize your telemetry data. The Query Builder searches the Workers Observability dataset, which currently includes all logs stored by [Workers Logs](/workers/observability/logs/workers-logs/). @@ -27,10 +35,11 @@ The Query Builder is available to all developers and requires no enablement. Que [observability] enabled = true - [observability.logs] - invocation_logs = true - head_sampling_rate = 1 # optional. default = 1. - ``` + [observability.logs] + invocation_logs = true + head_sampling_rate = 1 # optional. default = 1. + ``` + ## Write a query in the Cloudflare dashboard @@ -52,27 +61,27 @@ The Query Builder is available to all developers and requires no enablement. Que The Query Builder supports many visualization operators, including: -| Function | Arguments | Description | -| --- | --- | --- | -| **Count** | n/a | The total number of rows matching the query conditions | -| **Count Distinct** | any field | The number of occurrences of the unique values in the dataset | -| **Min** | numeric field | The smallest value for the field in the dataset | -| **Max** | numeric field | The largest value for the field in the dataset | -| **Sum** | numeric field | The total of all of the values for the field in the dataset | -| **Average** | numeric field | The average of the field in the dataset | -| **Standard Deviation** | numeric field | The standard deviation of the field in the dataset | -| **Variance** | numeric field | The variance of the field in the dataset | -| **P001** | numeric field | The value of the field below which 0.1% of the data falls | -| **P01** | numeric field | The value of the field below with 1% of the data falls | -| **P05** | numeric field | The value of the field below with 5% of the data falls | -| **P10** | numeric field | The value of the field below with 10% of the data falls | -| **P25** | numeric field | The value of the field below with 25% of the data falls | -| **Median (P50)** | numeric field | The value of the field below with 50% of the data falls | -| **P75** | numeric field | The value of the field below with 75% of the data falls | -| **P90** | numeric field | The value of the field below with 90% of the data falls | -| **P95** | numeric field | The value of the field below with 95% of the data falls | -| **P99** | numeric field | The value of the field below with 99% of the data falls | -| **P999** | numeric field | The value of the field below with 99.9% of the data falls | +| Function | Arguments | Description | +| ---------------------- | ------------- | ------------------------------------------------------------- | +| **Count** | n/a | The total number of rows matching the query conditions | +| **Count Distinct** | any field | The number of occurrences of the unique values in the dataset | +| **Min** | numeric field | The smallest value for the field in the dataset | +| **Max** | numeric field | The largest value for the field in the dataset | +| **Sum** | numeric field | The total of all of the values for the field in the dataset | +| **Average** | numeric field | The average of the field in the dataset | +| **Standard Deviation** | numeric field | The standard deviation of the field in the dataset | +| **Variance** | numeric field | The variance of the field in the dataset | +| **P001** | numeric field | The value of the field below which 0.1% of the data falls | +| **P01** | numeric field | The value of the field below with 1% of the data falls | +| **P05** | numeric field | The value of the field below with 5% of the data falls | +| **P10** | numeric field | The value of the field below with 10% of the data falls | +| **P25** | numeric field | The value of the field below with 25% of the data falls | +| **Median (P50)** | numeric field | The value of the field below with 50% of the data falls | +| **P75** | numeric field | The value of the field below with 75% of the data falls | +| **P90** | numeric field | The value of the field below with 90% of the data falls | +| **P95** | numeric field | The value of the field below with 95% of the data falls | +| **P99** | numeric field | The value of the field below with 99% of the data falls | +| **P999** | numeric field | The value of the field below with 99.9% of the data falls | You can add multiple visualizations in a single query. Each visualization renders a graph. A single summary table is also returned, which shows the raw query results. @@ -90,23 +99,34 @@ The operator is a logical condition that evaluates to true or false. See the tab | Data Type | Valid Conditions (Operators) | | --- | --- | | Numeric | Equals, Does not equal, Greater, Greater or equals, Less, Less or equals, Exists, Does not exist | -| String | Equals, Does not equal, Includes, Does not include, Regex, Exists, Does not exist, Starts with | +| String | Equals, Does not equal, Includes, Does not include, Regex, Exists, Does not exist, Starts with | The value for a numeric field is an integer. The value for a string field is any string. To add a filter: -1. Select **+** in the **Filter** section. -2. Select **Select key...** and input a key name. For example, `$workers.cpuTimeMs`. -3. Select the operator and change it to the operator best suited. For example, `Greater than`. -4. Select **Select value...** and input a value. For example, `100`. + 1. Select **+** in the **Filter** section. 2. Select **Select key...** and + input a key name. For example, `$workers.cpuTimeMs`. 3. Select the operator + and change it to the operator best suited. For example, `Greater than`. 4. + Select **Select value...** and input a value. For example, `100`. When you run the query with the filter specified above, only log events where `$workers.cpuTimeMs > 100` will be returned. Adding multiple filters combines them with an AND operator, meaning that only events matching all the filters will be returned. +#### Filter by cohort + +When using [cohort-based deployments](/workers/configuration/versions-and-deployments/gradual-deployments/cohort-deployments/), you can filter logs by cohort using `$workers.cohort`. This lets you compare error rates, latency, or other metrics across different user segments during a rollout. + +For example, to see all logs from the `employee` cohort: + +1. Select **+** in the **Filter** section. +2. Select **Select key...** and input `$workers.cohort`. +3. Select the operator **Equals**. +4. Select **Select value...** and input `employee`. + ### Search Search is a text filter that returns only events containing the specified text. Search can be helpful as a quick filtering mechanism, or to search for unique identifiable values in your logs. diff --git a/src/content/docs/workers/observability/traces/spans-and-attributes.mdx b/src/content/docs/workers/observability/traces/spans-and-attributes.mdx index 27d50ec5cfcead..4ee98b4843e464 100644 --- a/src/content/docs/workers/observability/traces/spans-and-attributes.mdx +++ b/src/content/docs/workers/observability/traces/spans-and-attributes.mdx @@ -40,6 +40,7 @@ Cloudflare Workers provides automatic tracing instrumentation **out of the box** - `cloudflare.outcome` - The outcome of the Worker invocation (e.g., `ok`, `exception`, `exceededCpu`, `exceededMemory`) - `cloudflare.cpu_time_ms` - The CPU time used by the Worker invocation, in milliseconds - `cloudflare.wall_time_ms` - The wall time used by the Worker invocation, in milliseconds +- `cloudflare.cohort` - The cohort that served this request when using [cohort-based deployments](/workers/configuration/versions-and-deployments/gradual-deployments/cohort-deployments/), or `null` if no cohort matched or for percentage-based deployments --- diff --git a/src/content/docs/workers/runtime-apis/bindings/version-metadata.mdx b/src/content/docs/workers/runtime-apis/bindings/version-metadata.mdx index 1b0d8b4f6fdd6b..2ddffba40904b4 100644 --- a/src/content/docs/workers/runtime-apis/bindings/version-metadata.mdx +++ b/src/content/docs/workers/runtime-apis/bindings/version-metadata.mdx @@ -4,20 +4,20 @@ title: Version metadata head: - tag: title content: Version metadata binding -description: Exposes Worker version metadata (`versionID` and `versionTag`). +description: + Exposes Worker version metadata (`versionID`, `versionTag`, and `cohort`). These fields can be added to events emitted from the Worker to send to downstream observability systems. sidebar: badge: text: Beta - --- -import { TabItem, Tabs, WranglerConfig } from "~/components" +import { TabItem, Tabs, WranglerConfig } from "~/components"; The version metadata binding can be used to access metadata associated with a [version](/workers/configuration/versions-and-deployments/#versions) from inside the Workers runtime. -Worker version ID, version tag and timestamp of when the version was created are available through the version metadata binding. They can be used in events sent to [Workers Analytics Engine](/analytics/analytics-engine/) or to any third-party analytics/metrics service in order to aggregate by Worker version. +Worker version ID, version tag, timestamp of when the version was created, and cohort (when using [cohort-based deployments](/workers/configuration/versions-and-deployments/gradual-deployments/cohort-deployments/)) are available through the version metadata binding. They can be used in events sent to [Workers Analytics Engine](/analytics/analytics-engine/) or to any third-party analytics/metrics service in order to aggregate by Worker version or cohort. To use the version metadata binding, update your Worker's Wrangler file: @@ -30,23 +30,38 @@ binding = "CF_VERSION_METADATA" -### Interface +## Properties + +| Property | Type | Description | +| ----------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `id` | `string` | The version UUID | +| `tag` | `string` | The version tag (if set during upload) | +| `timestamp` | `string` | ISO 8601 timestamp of when the version was created | +| `cohort` | `string \| null` | The matched cohort name when using [cohort-based deployments](/workers/configuration/versions-and-deployments/gradual-deployments/cohort-deployments/), or `null` if no cohort matched or for percentage-based deployments | + +## Interface -An example of how to access the version ID and version tag from within a Worker to send events to [Workers Analytics Engine](/analytics/analytics-engine/): +An example of how to access the version ID, version tag, and cohort from within a Worker to send events to [Workers Analytics Engine](/analytics/analytics-engine/): ```js export default { - async fetch(request, env, ctx) { - const { id: versionId, tag: versionTag, timestamp: versionTimestamp } = env.CF_VERSION_METADATA; - env.WAE.writeDataPoint({ - indexes: [versionId], - blobs: [versionTag, versionTimestamp], - //... - }); - //... - }, + async fetch(request, env, ctx) { + const { + id: versionId, + tag: versionTag, + timestamp: versionTimestamp, + cohort, + } = env.CF_VERSION_METADATA; + + env.WAE.writeDataPoint({ + indexes: [versionId], + blobs: [versionTag, versionTimestamp, cohort], + //... + }); + //... + }, }; ``` @@ -54,21 +69,48 @@ export default { ```ts interface Environment { - CF_VERSION_METADATA: WorkerVersionMetadata; - WAE: AnalyticsEngineDataset; + CF_VERSION_METADATA: WorkerVersionMetadata; + WAE: AnalyticsEngineDataset; } export default { - async fetch(request, env, ctx) { - const { id: versionId, tag: versionTag } = env.CF_VERSION_METADATA; - env.WAE.writeDataPoint({ - indexes: [versionId], - blobs: [versionTag], - //... - }); - //... - }, + async fetch(request, env, ctx) { + const { + id: versionId, + tag: versionTag, + timestamp: versionTimestamp, + cohort, + } = env.CF_VERSION_METADATA; + + env.WAE.writeDataPoint({ + indexes: [versionId], + blobs: [versionTag, versionTimestamp, cohort], + //... + }); + //... + }, } satisfies ExportedHandler; ``` + +## Example: Log cohort for debugging + +You can use the `cohort` property to add context to your logs during [gradual deployments](/workers/configuration/versions-and-deployments/gradual-deployments/): + +```js +export default { + async fetch(request, env, ctx) { + const { id, cohort } = env.CF_VERSION_METADATA; + + console.log({ + message: "Request received", + versionId: id, + cohort: cohort ?? "default", + }); + + // Your Worker logic... + return new Response("OK"); + }, +}; +```