Skip to main content
Version: 0.8 (next)

Caching

Credo caches data that is slow to fetch: resolved DID documents, AnonCreds objects from ledgers and revocation lists of X.509 certificates. A cache is always registered, and you only replace it in two cases: when you use multi-tenancy, and when several instances of the agent run side by side.

Which should I use?​

Keep the default for a single agent without tenants, such as a mobile wallet: nothing to configure, and entries survive a restart. Use InMemoryLruCache when the agent has tenants, because the default throws as soon as a tenant uses it. Use RedisCache when several instances of the agent run behind a load balancer, so they share one cache. With the default, instances overwrite each other's cache record in the shared storage, and with InMemoryLruCache each instance fetches and caches everything on its own.

CachePackageTenantsSeveral processes share itSurvives a restart
SingleContextStorageLruCache (default)@credo-ts/coreNoNoYes
InMemoryLruCache@credo-ts/coreYesNoNo
RedisCache@credo-ts/redis-cacheYesYesYes

The default keeps up to 500 entries in memory and saves them as a record in the agent's storage. When a tenant uses it, it throws SingleContextStorageLruCache can not be used with multiple agent context instances.

Configure the cache​

Register a CacheModule under the key cache to replace the default:

agent.ts
import { Agent, CacheModule, InMemoryLruCache } from '@credo-ts/core'
import { agentDependencies } from '@credo-ts/node'

const agent = new Agent({
dependencies: agentDependencies,
modules: {
...storage, // Storage and key management, see Agent setup
cache: new CacheModule({
cache: new InMemoryLruCache({ limit: 500 }),
}),
},
})

InMemoryLruCache removes the least recently used entries when it reaches limit. The limit applies to all agent contexts together.

CacheModule optionDescription
cacheThe cache to use. Required.
defaultExpiryInSecondsExpiry for entries set without one. RedisCache uses it. Default 60.
useCachedStorageServiceAlso caches DIDComm connection, mediation and mediator routing records, to save storage reads on busy DIDComm agents such as mediators. Default false. With several instances, only turn it on with a cache they share, such as Redis.

Redis​

npm install @credo-ts/redis-cache@alpha ioredis
agent.ts
import { Agent, CacheModule } from '@credo-ts/core'
import { agentDependencies } from '@credo-ts/node'
import { RedisCache } from '@credo-ts/redis-cache'
import { Redis } from 'ioredis'

const redis = new Redis('redis://localhost:6379')

const agent = new Agent({
dependencies: agentDependencies,
modules: {
...storage, // Storage and key management, see Agent setup
cache: new CacheModule({
// Also accepts ioredis options, e.g. new RedisCache({ host: 'localhost', port: 6379 })
cache: new RedisCache(redis),
defaultExpiryInSeconds: 300,
}),
},
})

RedisCache takes an ioredis client or ioredis options. Keys are prefixed with the agent context, or with global: for shared entries, so tenants and instances can share one Redis database. When you create the client yourself, close it after the agent shuts down:

agent.ts
await agent.shutdown()
// The agent doesn't close the Redis connection for you
await redis.quit()

cache.destroy(agentContext) removes all entries of one agent context, for example after you delete a tenant.

What is cached​

DataCached forScope
Resolved DID documents, for methods whose resolver allows caching (did:web, did:webvh, did:cheqd, did:indy, did:sov, did:hedera). did:key, did:jwk and did:peer aren't cached.5 minutes, change it with cacheDurationInSeconds when resolvingGlobal for methods in DidsModule({ publicDidMethods }), otherwise per agent context
AnonCreds schemas, credential definitions, revocation registry definitions and status lists5 minutesGlobal
Summaries of X.509 certificate revocation listsUntil the list's next updateGlobal
Indy VDR: which ledger a DID is onUntil evictedGlobal
DIDComm connection, mediation and mediator routing records, with useCachedStorageService: trueUntil evictedPer agent context

Global entries are public data that all tenants share. publicDidMethods defaults to web, indy, sov, cheqd, hedera and webvh.

Custom cache​

Implement the Cache interface: get, set and remove. Each method receives the agent context and an optional scope. Keep entries of different agent contexts apart, and only share entries when scope is 'global':

map-cache.ts
import type { AgentContext, Cache, CacheOptions } from '@credo-ts/core'

export class MapCache implements Cache {
private entries = new Map<string, { value: unknown; expiresAt?: number }>()

// Isolate entries per agent context unless the caller asks for the global scope
private key(agentContext: AgentContext, key: string, options?: CacheOptions) {
return options?.scope === 'global' ? `global:${key}` : `${agentContext.contextCorrelationId}:${key}`
}

public async get<CacheValue>(agentContext: AgentContext, key: string, options?: CacheOptions) {
const namespacedKey = this.key(agentContext, key, options)
const entry = this.entries.get(namespacedKey)
if (!entry) return null

if (entry.expiresAt && entry.expiresAt < Date.now()) {
this.entries.delete(namespacedKey)
return null
}

return entry.value as CacheValue
}

public async set<CacheValue>(
agentContext: AgentContext,
key: string,
value: CacheValue,
expiresInSeconds?: number,
options?: CacheOptions
) {
this.entries.set(this.key(agentContext, key, options), {
value,
expiresAt: expiresInSeconds ? Date.now() + expiresInSeconds * 1000 : undefined,
})
}

public async remove(agentContext: AgentContext, key: string, options?: CacheOptions) {
this.entries.delete(this.key(agentContext, key, options))
}
}