encore-cache

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Encore Caching (Redis)

Encore 缓存(Redis)

Instructions

说明

Encore's cache is a typed wrapper around Redis. Declare a
CacheCluster
once, then create
Keyspace
objects for each shape of data you need.
Encore 的缓存是对 Redis 的类型化封装。只需声明一次
CacheCluster
,然后为所需的每种数据类型创建相应的
Keyspace
对象。

Cluster

集群

typescript
import { CacheCluster } from "encore.dev/storage/cache";

const cluster = new CacheCluster("my-cache", {
  evictionPolicy: "allkeys-lru",
});
Reference a cluster from another service:
const cluster = CacheCluster.named("my-cache");
Eviction policies:
"allkeys-lru"
(default),
"noeviction"
,
"allkeys-lfu"
,
"allkeys-random"
,
"volatile-lru"
,
"volatile-lfu"
,
"volatile-ttl"
,
"volatile-random"
.
typescript
import { CacheCluster } from "encore.dev/storage/cache";

const cluster = new CacheCluster("my-cache", {
  evictionPolicy: "allkeys-lru",
});
从另一个服务引用集群:
const cluster = CacheCluster.named("my-cache");
淘汰策略:
"allkeys-lru"
(默认)、
"noeviction"
、
"allkeys-lfu"
、
"allkeys-random"
、
"volatile-lru"
、
"volatile-lfu"
、
"volatile-ttl"
、
"volatile-random"
。

Keyspace types

Keyspace 类型

Each keyspace has a key shape (used to build the Redis key from
keyPattern
) and a value type.
typescript
import {
  StringKeyspace,
  IntKeyspace,
  FloatKeyspace,
  StructKeyspace,
  StringListKeyspace,
  NumberListKeyspace,
  StringSetKeyspace,
  NumberSetKeyspace,
  expireIn,
} from "encore.dev/storage/cache";

// Strings
const tokens = new StringKeyspace<{ tokenId: string }>(cluster, {
  keyPattern: "token/:tokenId",
  defaultExpiry: expireIn(3600 * 1000),
});
await tokens.set({ tokenId: "abc" }, "value");
const val = await tokens.get({ tokenId: "abc" }); // undefined on miss

// Integers (atomic counters)
const counters = new IntKeyspace<{ userId: string }>(cluster, {
  keyPattern: "requests/:userId",
  defaultExpiry: expireIn(10 * 1000),
});
const count = await counters.increment({ userId: "user123" }, 1);

// Structs (JSON)
interface UserProfile { name: string; email: string; }
const profiles = new StructKeyspace<{ userId: string }, UserProfile>(cluster, {
  keyPattern: "profile/:userId",
  defaultExpiry: expireIn(3600 * 1000),
});
await profiles.set({ userId: "123" }, { name: "Alice", email: "alice@example.com" });

// Lists
const recent = new StringListKeyspace<{ userId: string }>(cluster, {
  keyPattern: "recent/:userId",
});
await recent.pushRight({ userId: "user123" }, "item1", "item2");

// Sets
const tags = new StringSetKeyspace<{ articleId: string }>(cluster, {
  keyPattern: "tags/:articleId",
});
await tags.add({ articleId: "post1" }, "typescript", "encore");
const has = await tags.contains({ articleId: "post1" }, "typescript");
每个 keyspace 都有一种键结构(用于根据
keyPattern
构建 Redis 键)和一种值类型。
typescript
import {
  StringKeyspace,
  IntKeyspace,
  FloatKeyspace,
  StructKeyspace,
  StringListKeyspace,
  NumberListKeyspace,
  StringSetKeyspace,
  NumberSetKeyspace,
  expireIn,
} from "encore.dev/storage/cache";

// Strings
const tokens = new StringKeyspace<{ tokenId: string }>(cluster, {
  keyPattern: "token/:tokenId",
  defaultExpiry: expireIn(3600 * 1000),
});
await tokens.set({ tokenId: "abc" }, "value");
const val = await tokens.get({ tokenId: "abc" }); // undefined on miss

// Integers (atomic counters)
const counters = new IntKeyspace<{ userId: string }>(cluster, {
  keyPattern: "requests/:userId",
  defaultExpiry: expireIn(10 * 1000),
});
const count = await counters.increment({ userId: "user123" }, 1);

// Structs (JSON)
interface UserProfile { name: string; email: string; }
const profiles = new StructKeyspace<{ userId: string }, UserProfile>(cluster, {
  keyPattern: "profile/:userId",
  defaultExpiry: expireIn(3600 * 1000),
});
await profiles.set({ userId: "123" }, { name: "Alice", email: "alice@example.com" });

// Lists
const recent = new StringListKeyspace<{ userId: string }>(cluster, {
  keyPattern: "recent/:userId",
});
await recent.pushRight({ userId: "user123" }, "item1", "item2");

// Sets
const tags = new StringSetKeyspace<{ articleId: string }>(cluster, {
  keyPattern: "tags/:articleId",
});
await tags.add({ articleId: "post1" }, "typescript", "encore");
const has = await tags.contains({ articleId: "post1" }, "typescript");

Multi-field key patterns

多字段键模式

typescript
interface Key { userId: string; resourcePath: string; }

const requests = new IntKeyspace<Key>(cluster, {
  keyPattern: "requests/:userId/:resourcePath",
  defaultExpiry: expireIn(10 * 1000),
});
typescript
interface Key { userId: string; resourcePath: string; }

const requests = new IntKeyspace<Key>(cluster, {
  keyPattern: "requests/:userId/:resourcePath",
  defaultExpiry: expireIn(10 * 1000),
});

Expiry helpers

过期时间辅助函数

typescript
import {
  expireIn,          // milliseconds
  expireInSeconds,
  expireInMinutes,
  expireInHours,
  expireDailyAt,     // a specific UTC time each day
  neverExpire,
  keepTTL,           // keep existing TTL when updating
} from "encore.dev/storage/cache";
typescript
import {
  expireIn,          // milliseconds
  expireInSeconds,
  expireInMinutes,
  expireInHours,
  expireDailyAt,     // a specific UTC time each day
  neverExpire,
  keepTTL,           // keep existing TTL when updating
} from "encore.dev/storage/cache";

Write options

写入选项

typescript
await keyspace.set(key, value, { expiry: expireInMinutes(30) });
await keyspace.set(key, value, { expiry: keepTTL });
await keyspace.setIfNotExists(key, value);  // throws CacheKeyExists if present
await keyspace.replace(key, value);          // throws CacheMiss if absent
typescript
await keyspace.set(key, value, { expiry: expireInMinutes(30) });
await keyspace.set(key, value, { expiry: keepTTL });
await keyspace.setIfNotExists(key, value);  // throws CacheKeyExists if present
await keyspace.replace(key, value);          // throws CacheMiss if absent

Errors

错误

typescript
import { CacheMiss, CacheKeyExists } from "encore.dev/storage/cache";

const value = await keyspace.get(key);  // undefined on miss (does not throw)
typescript
import { CacheMiss, CacheKeyExists } from "encore.dev/storage/cache";

const value = await keyspace.get(key);  // undefined on miss (does not throw)

Guidelines

指南

  • Declare
    CacheCluster
    and keyspaces at package level.
  • Pick the most specific keyspace type —
    IntKeyspace
    for counters gives you atomic
    increment
    /
    decrement
    for free.
  • get()
    returns
    undefined
    on miss;
    replace()
    and
    setIfNotExists()
    throw on conflict.
  • Local development uses an in-memory Redis with a ~100-key cap — don't load-test it.
  • For durable storage, use
    encore-database
    (Postgres) or
    encore-bucket
    (object storage) instead.
  • 在包级别声明
    CacheCluster
    和 keyspace。
  • 选择最具体的 keyspace 类型——对于计数器,
    IntKeyspace
    可直接提供原子的
    increment
    /
    decrement
    。
  • get()
    在未命中时返回
    undefined
    ;
    replace()
    和
    setIfNotExists()
    在发生冲突时抛出异常。
  • 本地开发使用带约 100 个键上限的内存版 Redis——不要对其进行负载测试。
  • 对于持久化存储,请改用
    encore-database
    (Postgres)或
    encore-bucket
    (对象存储)。