Cache Decorators
UDIOT provides three decorators for method-level caching: @AICache caches a method's return value, @CacheInvalidate evicts cache entries when a method is called, and @CacheKey marks which parameters should be included when building the cache key.
All three decorators operate against the global CacheManager returned by getGlobalCacheManager().
Key Concepts
- Auto key generation. By default,
@AICachebuilds keys asudiot:<ClassName>:<methodName>:<argsHash>usingCacheKeyGenerator. Providing a customkeyGeneratoroverrides this entirely. @CacheKeyfiltering. When at least one parameter is decorated with@CacheKey, only those parameters contribute to the cache key hash. Undecorated parameters are ignored. This is useful when a method accepts options objects that should not vary the cache key.- Post-call invalidation.
@CacheInvalidateruns the original method first, then performs invalidation so the caller always receives the fresh result. - Event emission.
eventsin@CacheInvalidatecallscacheManager.handleEvent(eventName, { result, args })after method completion, triggering any matchingsetupEventInvalidationhandlers.
API Reference
@AICache(options?)
function AICache(options?: AICacheOptions): MethodDecorator
AICacheOptions:
| Field | Type | Description |
|---|---|---|
strategy |
CacheStrategy |
Provider to use: 'memory' | 'redis' | 'cdn' | 'tiered' | 'auto' |
ttl |
'auto' | number |
TTL in seconds, or 'auto' to derive from compute time |
tags |
string[] |
Tags for grouped invalidation |
invalidateOn |
string[] |
Reserved field for future event-based invalidation wiring |
keyGenerator |
(...args: any[]) => string |
Custom cache key function |
condition |
(...args: any[]) => boolean |
Return false to skip the cache entirely |
serialize |
(value: any) => any |
Transform value before storing |
deserialize |
(value: any) => any |
Transform value after retrieval |
predictive |
boolean |
Reserved for future pre-warming integration |
@CacheInvalidate(options)
function CacheInvalidate(options: CacheInvalidateOptions): MethodDecorator
CacheInvalidateOptions:
| Field | Type | Description |
|---|---|---|
keys |
string[] | ((...args) => string[]) |
Exact keys to delete. The function form receives the method's arguments. |
tags |
string[] |
Tags to bulk-invalidate |
events |
string[] |
Event names to emit via handleEvent |
@CacheKey()
function CacheKey(): ParameterDecorator
Place on any number of parameters of a method that also carries @AICache. Only decorated parameters contribute to the cache key.
Introspection helpers
getAICacheOptions(target, propertyKey): AICacheOptions | undefined
isAICached(target, propertyKey): boolean
getCacheInvalidateOptions(target, propertyKey): CacheInvalidateOptions | undefined
isCacheInvalidator(target, propertyKey): boolean
getCacheKeyParams(target, propertyKey): number[] // parameter indices
hasCacheKeyParams(target, propertyKey): boolean
AICacheAnalyzer
A standalone class that tracks access patterns and produces optimization reports. It is not wired automatically to the global manager but can be used independently.
const analyzer = new AICacheAnalyzer();
analyzer.trackHit(key, strategy);
analyzer.trackMiss(key, strategy, computeTimeMs);
analyzer.trackUpdate(key); // the underlying data changed
analyzer.trackSet(key, ttlSeconds); // an entry was stored with this TTL
const ttl = await analyzer.recommendTTL(key, computeTimeMs, value);
const strategy = await analyzer.recommendStrategy(key, factory);
const report = await analyzer.generateReport();
// report.inefficientKeys, report.oversizedTTLs, report.undersizedTTLs, report.recommendations
const predictions = await analyzer.predictAccess(); // CachePrediction[]
Basic Usage
import {
AICache,
CacheInvalidate,
CacheKey,
} from '@tagna/udiot';
interface User {
id: string;
email: string;
name: string;
}
class UserService {
@AICache({
strategy: 'memory',
ttl: 300,
tags: ['users'],
})
async getUser(@CacheKey() id: string, _includeDeleted = false): Promise<User> {
// Only 'id' forms the cache key; '_includeDeleted' is ignored.
return db.users.findById(id);
}
@CacheInvalidate({
tags: ['users'],
keys: (id: string) => [`udiot:userservice:getuser:${hashOf(id)}`],
events: ['user.updated'],
})
async updateUser(id: string, data: Partial<User>): Promise<User> {
return db.users.update(id, data);
}
@CacheInvalidate({ tags: ['users'] })
async deleteUser(id: string): Promise<void> {
await db.users.delete(id);
}
}
Common Patterns
Conditional caching
Only cache results for authenticated requests:
@AICache({
ttl: 60,
condition: (_id: string, ctx: RequestContext) => ctx.isAuthenticated,
})
async getSecretData(id: string, ctx: RequestContext): Promise<Secret> {
return db.secrets.findById(id);
}
Serialization round-trip
Cache a Date object by converting it to a timestamp:
@AICache({
ttl: 3600,
serialize: (value: { timestamp: Date }) => ({
...value,
timestamp: value.timestamp.toISOString(),
}),
deserialize: (raw: { timestamp: string }) => ({
...raw,
timestamp: new Date(raw.timestamp),
}),
})
async getEvent(id: string): Promise<{ timestamp: Date; name: string }> {
return db.events.findById(id);
}
Auto TTL for expensive computations
@AICache({ ttl: 'auto' })
async runHeavyReport(params: ReportParams): Promise<Report> {
// If this takes 500 ms, the TTL will be max(60, min(5000, 3600)) = 3600 s.
return analytics.generateReport(params);
}
Using AICacheAnalyzer for optimization reports
import { AICacheAnalyzer } from '@tagna/udiot';
const analyzer = new AICacheAnalyzer();
// After a period of operation, generate a report:
const report = await analyzer.generateReport();
console.log('Overall hit rate:', report.overallHitRate);
console.log('Recommendations:', report.recommendations);
Integration Notes
@AICacherequiresreflect-metadatato be imported before the decorator runs. Import it once at the entry point of your application.- The global
CacheManagermust be configured before any decorated method is called. IfsetGlobalCacheManagerhas not been called, a default in-memory manager is created lazily. @CacheKeydecorators are read byCacheKeyGenerator.generatethroughfilterCacheKeyArgs. If you provide a customkeyGenerator,@CacheKeyhas no effect.
Pitfalls
@CacheInvalidateruns invalidation after the method returns. If the method throws, the cache is not invalidated. Wrap the invalidation call in a try/finally block if you need guaranteed cleanup on error.- Passing a function to
keysreceives the raw method arguments in their original order. Ensure the function signature matches the method signature. - Keys generated by
CacheKeyGeneratorinclude a 12-character SHA-256 hash of the serialized arguments. Non-serializable arguments (class instances, functions) fall back toString(arg), which may collide if two distinct objects stringify identically.