Dependency tracking is an advanced feature in RunCache that allows you to establish relationships between cache entries. When a cache entry changes or is invalidated, all dependent entries are automatically invalidated as well. This guide explains how to use dependency tracking effectively in your applications.
Understanding Dependency Tracking
Dependency tracking allows you to:
Define relationships between cache entries
Automatically invalidate dependent entries when a parent entry changes
Create complex dependency chains with multi-level relationships
Maintain data consistency across related cache entries
This is particularly useful for:
Derived data that depends on base data
Composite views that depend on multiple data sources
Hierarchical data structures
Ensuring consistency in complex caching scenarios
Setting Up Dependencies
To establish a dependency relationship, use the dependencies parameter when setting a cache entry:
// Primary dataawaitRunCache.set({ key:'user:1:profile', value:JSON.stringify({ name:'John Doe', email:'john@example.com' })});// Dependent dataawaitRunCache.set({ key:'user:1:dashboard', value:JSON.stringify({ widgets: [...] }), dependencies: ['user:1:profile'] // This entry depends on the profile});
In this example:
user:1:dashboard depends on user:1:profile
If user:1:profile is updated or invalidated, user:1:dashboard will be automatically invalidated
user:1:product-recommendations depends on three different cache entries
If any of those entries change, the recommendations will be invalidated
Checking Dependencies
You can check if one cache entry depends on another (directly or indirectly) using the isDependencyOf method:
// Check if one entry depends on another
const isDependency = await RunCache.isDependencyOf('home:feed', 'user:1:profile');
console.log(isDependency); // true (because of the multi-level dependency)
// Check a non-dependent relationship
const isNotDependency = await RunCache.isDependencyOf('products', 'categories');
console.log(isNotDependency); // false (no dependency relationship)
Manual Dependency Invalidation
You can manually invalidate a cache entry and all its dependents using the invalidateByDependency method:
// Invalidate an entry and all its dependents
RunCache.invalidateByDependency('user:1:profile');
This will:
Invalidate the specified entry (user:1:profile)
Invalidate all entries that directly depend on it (user:1:recommendations)
Invalidate all entries that depend on those entries (home:feed)
And so on, following the dependency chain
Monitoring Dependency Invalidations
RunCache provides events to monitor dependency invalidations:
// Global dependency invalidation event
RunCache.onDependencyInvalidation((event) => {
console.log(`Cache entry ${event.key} was invalidated due to dependency on: ${event.dependencyKey}`);
});
// Specific key dependency invalidation
RunCache.onKeyDependencyInvalidation("user:1:dashboard", (event) => {
console.log(`Dashboard cache was invalidated due to dependency on: ${event.dependencyKey}`);
});
// Pattern-based dependency invalidation events
RunCache.onKeyDependencyInvalidation("home:*", (event) => {
console.log(`Home page component ${event.key} was invalidated due to dependency on: ${event.dependencyKey}`);
});
These events can be useful for:
Logging and monitoring
Triggering additional actions when dependencies are invalidated
Debugging complex dependency chains
Combining Dependencies with Source Functions
Dependencies work particularly well with source functions and automatic refetching:
// Primary data with source function
await RunCache.set({
key: 'user:1:profile',
sourceFn: async () => {
const response = await fetch('https://api.example.com/users/1');
const data = await response.json();
return JSON.stringify(data);
},
ttl: 300000, // 5 minutes
autoRefetch: true
});
// Dependent data with source function
await RunCache.set({
key: 'user:1:recommendations',
sourceFn: async () => {
// Get the user profile from cache
const profileJson = await RunCache.get('user:1:profile');
const profile = JSON.parse(profileJson);
// Use profile data to fetch recommendations
const response = await fetch(`https://api.example.com/recommendations?preferences=${profile.preferences.join(',')}`);
const recommendations = await response.json();
return JSON.stringify(recommendations);
},
dependencies: ['user:1:profile'],
ttl: 600000, // 10 minutes
autoRefetch: true
});
In this example:
When user:1:profile is refreshed (automatically or manually), its dependents are invalidated
When user:1:recommendations is next accessed, its source function will be called
The source function will use the fresh profile data to generate new recommendations
This creates a powerful pattern for maintaining consistency while still benefiting from caching.
Dependency Best Practices
1. Design Dependency Hierarchies Carefully
Plan your dependency relationships to reflect logical data dependencies:
Deep dependency chains can lead to widespread invalidations:
// Potentially problematic - deep dependency chain
A → B → C → D → E → F → G
// Better - flatter dependency structure
A → B, C, D
E → F, G
Consider using tags for broader grouping when appropriate.
3. Be Specific with Dependencies
Define dependencies as specifically as possible:
// Too broad - invalidates too much
await RunCache.set({
key: 'user:1:dashboard',
value: '...',
dependencies: ['global:data'] // Very broad dependency
});
// Better - more specific dependencies
await RunCache.set({
key: 'user:1:dashboard',
value: '...',
dependencies: ['user:1:profile', 'user:1:preferences'] // Only what's needed
});
4. Combine with Tags for Complex Scenarios
For complex invalidation needs, combine dependencies with tags:
// Set up with both dependencies and tags
await RunCache.set({
key: 'user:1:feed',
value: '...',
dependencies: ['user:1:profile', 'user:1:friends'],
tags: ['user:1', 'feed']
});
// Different invalidation options:
// 1. Invalidate when profile changes (automatic via dependency)
// 2. Invalidate all user:1 data (via tag)
RunCache.invalidateByTag('user:1');
// 3. Invalidate all feeds (via tag)
RunCache.invalidateByTag('feed');
5. Monitor Invalidation Patterns
Use events to monitor and understand how your dependencies behave:
// Track dependency invalidations
RunCache.onDependencyInvalidation((event) => {
console.log(`${event.key} invalidated due to ${event.dependencyKey}`);
// Track metrics about invalidation frequency
trackMetric('cache.dependency.invalidation', {
key: event.key,
dependency: event.dependencyKey
});
});
Advanced Dependency Patterns
1. Dynamic Dependencies
Create dependencies dynamically based on content:
async function cacheArticleWithRelatedContent(articleId) {
// Fetch and cache the article
const article = await fetchArticle(articleId);
await RunCache.set({
key: `article:${articleId}`,
value: JSON.stringify(article)
});
// Determine related content IDs from the article
const relatedIds = article.relatedArticles;
// Cache related content with dependencies
for (const relatedId of relatedIds) {
const relatedArticle = await fetchArticle(relatedId);
await RunCache.set({
key: `article:${relatedId}`,
value: JSON.stringify(relatedArticle),
dependencies: [`article:${articleId}`] // Dynamic dependency
});
}
}
2. Dependency-Aware Refresh
Implement smart refreshing that considers dependencies:
async function smartRefresh(key) {
// First, check if this key has dependencies
const allKeys = await RunCache.get('*'); // Get all cache keys
const dependencyKeys = [];
for (const cacheKey of allKeys) {
if (await RunCache.isDependencyOf(key, cacheKey)) {
dependencyKeys.push(cacheKey);
}
}
// Refresh dependencies first (from furthest to closest)
for (const depKey of dependencyKeys.reverse()) {
await RunCache.refetch(depKey);
}
// Then refresh the target key
await RunCache.refetch(key);
}
3. Conditional Dependencies
Implement conditional dependency relationships:
// Check if we should establish a dependency
const shouldDependOn = await checkCondition();
await RunCache.set({
key: 'conditional-data',
value: '...',
dependencies: shouldDependOn ? ['base-data'] : [] // Conditional dependency
});
Next Steps
Now that you understand dependency tracking, explore these related topics: