Caching
Pagode includes a robust caching system to improve performance and reduce database load. The cache service is available through the application container.
Cache Service
The cache is implemented as an in-memory solution by default, but the interface is designed to be easily swappable with other implementations if needed.
Set Data
To store data in the cache:
// Cache data with a 1 hour expiration
err := c.Cache.Set(ctx, "cache-key", someData, time.Hour)
// Cache data with tags for selective invalidation
err := c.Cache.SetWithTags(ctx, "user-profile-123", profileData, time.Hour, []string{"users", "profiles"})
Get Data
To retrieve data from the cache:
var result SomeType
found, err := c.Cache.Get(ctx, "cache-key", &result)
if found {
// Use the cached data
} else {
// Cache miss - fetch the data and cache it
result = fetchExpensiveData()
c.Cache.Set(ctx, "cache-key", result, time.Hour)
}
Flush Data
To remove specific keys or flush the entire cache:
// Remove a specific key
err := c.Cache.Delete(ctx, "cache-key")
// Flush the entire cache
err := c.Cache.Flush(ctx)
Flush Tags
One of the most powerful features of the caching system is tag-based invalidation. This allows you to invalidate groups of related cache entries at once:
// Invalidate all cache entries with the "users" tag
err := c.Cache.FlushTags(ctx, []string{"users"})
This is particularly useful when updating entities - you can invalidate all cache entries related to that entity type.
Practical Example
Here's a complete example of using the cache for an expensive database query:
func (h *Handler) GetPopularProducts(ctx echo.Context) error {
var products []Product
cacheKey := "popular-products"
// Try to get from cache
found, err := h.container.Cache.Get(ctx.Request().Context(), cacheKey, &products)
if err != nil {
return err
}
if !found {
// Cache miss - fetch from database
products, err = h.container.ORM.Product.
Query().
Where(product.Featured(true)).
Order(ent.Desc(product.FieldViewCount)).
Limit(10).
All(ctx.Request().Context())
if err != nil {
return err
}
// Store in cache for 1 hour
err = h.container.Cache.SetWithTags(
ctx.Request().Context(),
cacheKey,
products,
time.Hour,
[]string{"products", "featured"},
)
if err != nil {
// Log cache error but continue (non-fatal)
log.Printf("Cache error: %v", err)
}
}
return ctx.JSON(http.StatusOK, products)
}
Node Caching for UI Components
For UI components, Pagode also includes a node caching system to avoid redundant rendering of unchanged components. This is particularly useful for complex components that don't change frequently.
Configuration
Cache settings can be configured in config/config.yaml
:
cache:
# Default expiration for items without explicit TTL
expiration: 10m
# Controls whether caching is enabled
enabled: true
Cache Headers for Static Files
In addition to application-level caching, Pagode configures appropriate cache-control headers for static files to optimize browser caching:
// Set cache headers for 7 days
func setCacheHeaders(next echo.HandlerFunc) echo.HandlerFunc {
return func(ctx echo.Context) error {
ctx.Response().Header().Set("Cache-Control", "public, max-age=604800")
return next(ctx)
}
}
This ensures that static resources like CSS, JavaScript, and images are efficiently cached by browsers.