Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .changeset/seven-squids-dream.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
"wrangler": minor
"@cloudflare/workers-utils": minor
---

Add `cache` configuration option for enabling worker cache (experimental)

You can now enable cache before worker execution using the new `cache` configuration:

```jsonc
{
"cache": {
"enabled": true,
},
}
```

This setting is environment-inheritable and opt-in. When enabled, cache behavior is applied before your worker runs.

Note: This feature is experimental. The runtime API is not yet generally available.
1 change: 1 addition & 0 deletions packages/workers-utils/src/config/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,7 @@ export const defaultWranglerConfig: Config = {
upload_source_maps: undefined,
assets: undefined,
observability: { enabled: true },
cache: undefined,
/** The default here is undefined so that we can delegate to the CLOUDFLARE_COMPLIANCE_REGION environment variable. */
compliance_region: undefined,
python_modules: { exclude: ["**/*.pyc"] },
Expand Down
13 changes: 13 additions & 0 deletions packages/workers-utils/src/config/environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,14 @@ interface EnvironmentInheritable {
*/
observability: Observability | undefined;

/**
* Specify the cache behavior of the Worker.
*
* @inheritable
* @hidden
*/
cache: CacheOptions | undefined;

/**
* Specify the compliance region mode of the Worker.
*
Expand Down Expand Up @@ -1428,6 +1436,11 @@ export interface Observability {
};
}

export interface CacheOptions {
/** If cache is enabled for this Worker */
enabled: boolean;
}

export type DockerConfiguration = {
/** Socket used by miniflare to communicate with Docker */
socketPath: string;
Expand Down
1 change: 1 addition & 0 deletions packages/workers-utils/src/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export type {
RawDevConfig,
} from "./config";
export type {
CacheOptions,
ConfigModuleRuleType,
Environment,
RawEnvironment,
Expand Down
41 changes: 41 additions & 0 deletions packages/workers-utils/src/config/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import type { Binding } from "../types";
import type { Config, DevConfig, RawConfig, RawDevConfig } from "./config";
import type {
Assets,
CacheOptions,
DispatchNamespaceOutbound,
Environment,
Observability,
Expand Down Expand Up @@ -1936,6 +1937,14 @@ function normalizeAndValidateEnvironment(
validateObservability,
undefined
),
cache: inheritable(
diagnostics,
topLevelEnv,
rawEnv,
"cache",
validateCache,
undefined
),
compliance_region: inheritable(
diagnostics,
topLevelEnv,
Expand Down Expand Up @@ -4915,6 +4924,38 @@ const validateObservability: ValidatorFn = (diagnostics, field, value) => {
return isValid;
};

const validateCache: ValidatorFn = (diagnostics, field, value) => {
if (value === undefined) {
return true;
}

if (typeof value !== "object" || value === null) {
diagnostics.errors.push(
`"${field}" should be an object but got ${JSON.stringify(value)}.`
);
return false;
}

const val = value as CacheOptions;
let isValid = true;

isValid =
validateRequiredProperty(
diagnostics,
field,
"enabled",
val.enabled,
"boolean"
) && isValid;

isValid =
validateAdditionalProperties(diagnostics, field, Object.keys(val), [
"enabled",
]) && isValid;

return isValid;
};

function warnIfDurableObjectsHaveNoMigrations(
diagnostics: Diagnostics,
durableObjects: Config["durable_objects"],
Expand Down
3 changes: 2 additions & 1 deletion packages/workers-utils/src/worker.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Observability, Route } from "./config/environment";
import type { CacheOptions, Observability, Route } from "./config/environment";
import type { INHERIT_SYMBOL } from "./constants";
import type { Json, WorkerMetadata } from "./types";
import type { AssetConfig, RouterConfig } from "@cloudflare/workers-shared";
Expand Down Expand Up @@ -428,6 +428,7 @@ export interface CfWorkerInit {
}
| undefined;
observability: Observability | undefined;
cache: CacheOptions | undefined;
}

export interface CfWorkerContext {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ describe("normalizeAndValidateConfig()", () => {
keep_names: undefined,
assets: undefined,
observability: undefined,
cache: undefined,
compliance_region: undefined,
images: undefined,
media: undefined,
Expand Down Expand Up @@ -7484,6 +7485,189 @@ describe("normalizeAndValidateConfig()", () => {
});
});

describe("[cache]", () => {
it("should error when cache is not an object", ({ expect }) => {
const { diagnostics } = normalizeAndValidateConfig(
{
cache: "enabled",
} as unknown as RawConfig,
undefined,
undefined,
{ env: undefined }
);

expect(diagnostics.hasWarnings()).toBe(false);
expect(diagnostics.hasErrors()).toBe(true);
expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
"Processing wrangler configuration:
- "cache" should be an object but got "enabled"."
`);
});

it("should error when cache is null", ({ expect }) => {
const { diagnostics } = normalizeAndValidateConfig(
{
cache: null,
} as unknown as RawConfig,
undefined,
undefined,
{ env: undefined }
);

expect(diagnostics.hasWarnings()).toBe(false);
expect(diagnostics.hasErrors()).toBe(true);
expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
"Processing wrangler configuration:
- "cache" should be an object but got null."
`);
});

it("should error when cache.enabled is missing", ({ expect }) => {
const { diagnostics } = normalizeAndValidateConfig(
{
cache: {},
} as unknown as RawConfig,
undefined,
undefined,
{ env: undefined }
);

expect(diagnostics.hasWarnings()).toBe(false);
expect(diagnostics.hasErrors()).toBe(true);
expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
"Processing wrangler configuration:
- "cache.enabled" is a required field."
`);
});

it("should error when cache.enabled is not a boolean", ({ expect }) => {
const { diagnostics } = normalizeAndValidateConfig(
{
cache: {
enabled: "true",
},
} as unknown as RawConfig,
undefined,
undefined,
{ env: undefined }
);

expect(diagnostics.hasWarnings()).toBe(false);
expect(diagnostics.hasErrors()).toBe(true);
expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
"Processing wrangler configuration:
- Expected "cache.enabled" to be of type boolean but got "true"."
`);
});

it("should not error on valid cache config with enabled true", ({
expect,
}) => {
const { config, diagnostics } = normalizeAndValidateConfig(
{
cache: {
enabled: true,
},
} as unknown as RawConfig,
undefined,
undefined,
{ env: undefined }
);

expect(diagnostics.hasWarnings()).toBe(false);
expect(diagnostics.hasErrors()).toBe(false);
expect(config.cache).toEqual({ enabled: true });
});

it("should not error on valid cache config with enabled false", ({
expect,
}) => {
const { config, diagnostics } = normalizeAndValidateConfig(
{
cache: {
enabled: false,
},
} as unknown as RawConfig,
undefined,
undefined,
{ env: undefined }
);

expect(diagnostics.hasWarnings()).toBe(false);
expect(diagnostics.hasErrors()).toBe(false);
expect(config.cache).toEqual({ enabled: false });
});

it("should warn on unexpected fields in cache config", ({ expect }) => {
const { diagnostics } = normalizeAndValidateConfig(
{
cache: {
enabled: true,
invalid_key: "hello",
},
} as unknown as RawConfig,
undefined,
undefined,
{ env: undefined }
);

expect(diagnostics.hasWarnings()).toBe(true);
expect(diagnostics.hasErrors()).toBe(false);
expect(diagnostics.renderWarnings()).toMatchInlineSnapshot(`
"Processing wrangler configuration:
- Unexpected fields found in cache field: "invalid_key""
`);
});

it("should inherit cache from top-level config to environment", ({
expect,
}) => {
const { config, diagnostics } = normalizeAndValidateConfig(
{
cache: {
enabled: true,
},
env: {
production: {},
},
} as unknown as RawConfig,
undefined,
undefined,
{ env: "production" }
);

expect(diagnostics.hasWarnings()).toBe(false);
expect(diagnostics.hasErrors()).toBe(false);
expect(config.cache).toEqual({ enabled: true });
});

it("should allow environment to override top-level cache", ({
expect,
}) => {
const { config, diagnostics } = normalizeAndValidateConfig(
{
cache: {
enabled: true,
},
env: {
staging: {
cache: {
enabled: false,
},
},
},
} as unknown as RawConfig,
undefined,
undefined,
{ env: "staging" }
);

expect(diagnostics.hasWarnings()).toBe(false);
expect(diagnostics.hasErrors()).toBe(false);
expect(config.cache).toEqual({ enabled: false });
});
});

describe("route & routes fields", () => {
it("should error if both route and routes are specified in the same environment", ({
expect,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ describe("RemoteRuntimeController", () => {
limits: undefined,
observability: undefined,
containers: undefined,
cache: undefined,
});

vi.mocked(createWorkerPreview).mockResolvedValue({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export function createEsmWorker(
tail_consumers: undefined,
limits: undefined,
observability: undefined,
cache: undefined,
...overrides,
};
}
Expand Down Expand Up @@ -77,6 +78,7 @@ export function createCjsWorker(
tail_consumers: undefined,
limits: undefined,
observability: undefined,
cache: undefined,
...overrides,
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,12 @@ describe("createWorkerUploadForm — optional metadata fields", () => {
key: "observability",
expected: { enabled: true },
},
{
label: "cache",
overrides: { cache: { enabled: true } },
key: "cache_options",
expected: { enabled: true },
},
{
label: "annotations",
overrides: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ function createWorkerBundleFormData(
assets: undefined,
containers: undefined, // containers are not supported in Pages
observability: undefined,
cache: undefined, // cache is not supported in Pages
},
getBindings(config, { pages: true })
);
Expand Down
1 change: 1 addition & 0 deletions packages/wrangler/src/deploy/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -879,6 +879,7 @@ See https://developers.cloudflare.com/workers/platform/compatibility-dates for m
}
: undefined,
observability: config.observability,
cache: config.cache,
};

sourceMapSize = worker.sourceMaps?.reduce(
Expand Down
Loading
Loading