BoxCache Provider

The BoxCache Provider is BoxLang's default, high-performance cache implementation.

The BoxCache Provider is BoxLang's default, high-performance cache implementation. It provides a comprehensive caching solution with pluggable object stores, configurable eviction policies, automatic maintenance, and extensive monitoring capabilities. The BoxCache Provider serves as the foundation for most caching needs in BoxLang applications.

Overview

The BoxCache Provider (BoxCacheProvider) is designed to be:

  • High Performance: Uses efficient concurrent data structures and async operations

  • Flexible Storage: Supports multiple object store backends (memory, disk, custom)

  • Intelligent Eviction: Configurable eviction policies (LRU, MRU, LFU, etc.)

  • Self-Maintaining: Automatic expiration, reaping, and memory management

  • Event-Driven: Comprehensive event broadcasting for cache operations

  • Thread-Safe: Fully concurrent and thread-safe for multi-threaded applications

  • Observable: Rich statistics and monitoring capabilities

Accessing the BoxCache Provider

The primary way to access BoxCache instances in BoxLang is through the cache() BIF:

// Get the default cache (usually BoxCache provider)
defaultCache = cache();

// Get a named BoxCache instance  
userCache = cache("userSessions");
productCache = cache("productCatalog");

// Check cache type
println( "Cache type: " & cache().getType() ); // Outputs: BoxLang

Core Storage Operations

Setting Data

The BoxCache Provider offers multiple ways to store data with flexible timeout options:

myCache = cache("myCache");

// Basic storage (uses default timeouts)
myCache.set("user:123", userData);

// With custom timeout (in seconds)
myCache.set("session:abc", sessionData, 1800); // 30 minutes

// With both timeout and last access timeout
myCache.set("temp:xyz", tempData, 3600, 900); // 1 hour total, 15 min idle

// With metadata
metadata = {"priority": 5, "category": "user"};
myCache.set("user:456", userData, 3600, 1800, metadata);

// Bulk storage with same timeouts
entries = {
    "product:100": product1,
    "product:101": product2,
    "product:102": product3
};
myCache.set(entries); // Uses default timeouts
myCache.set(entries, 7200, 3600); // 2 hours, 1 hour idle

Getting Data

Retrieve data with built-in null safety using the Attempt pattern. Please see the Attempt construct section.

Basic Retrieval Patterns

// Traditional approach with explicit checking
result = cache().get( "user:123" )
if ( result.isPresent() ) {
    user = result.get()
    processUser( user )
} else {
    // Handle cache miss
    user = loadUserFromDatabase( 123 )
    cache().set( "user:123", user, 1800 )
}

// Functional approach with orElse fallback
user = cache().get( "user:123" )
    .orElse( loadUserFromDatabase( 123 ) )

// Lazy fallback with orElseGet (only executed if cache miss)
user = cache().get( "user:123" )
    .orElseGet( function() {
        var freshUser = loadUserFromDatabase( 123 )
        cache().set( "user:123", freshUser, 1800 )
        return freshUser
    } )

// Get with default value
config = cache().get( "app:config" )
    .orElse( getDefaultConfiguration() )

Functional Transformations

// Transform cached data with map()
userDisplayName = cache().get( "user:123" )
    .map( ( user ) -> user.firstName & " " & user.lastName )
    .orElse( "Unknown User" )

// Chain multiple transformations
userPermissions = cache().get( "user:123" )
    .map( ( user ) -> user.roleID )
    .map( ( roleID ) -> loadRolePermissions( roleID ) )
    .orElse( [ ] )

// Conditional processing with filter()
activeUser = cache().get( "user:123" )
    .filter( ( user ) -> user.status == "active" )
    .orElse( null )

// Complex transformation with flatMap()
userPreferences = cache().get( "user:123" )
    .flatMap( function( user ) {
        return cache().get( "preferences:" & user.userID )
    } )
    .orElse( getDefaultPreferences() )

Validation and Type Safety

// Validate cached data before use
validatedUser = cache().get( "user:123" )
    .toBeType( "struct" )
    .toSatisfy( ( user ) -> structKeyExists( user, "userID" ) && structKeyExists( user, "email" ) )

if ( validatedUser.isValid() ) {
    user = validatedUser.get()
    // Safe to use user data
} else {
    // Invalid or corrupted cache data
    cache().clear( "user:123" )
    user = loadUserFromDatabase( 123 )
}

// Validate numeric ranges
score = cache().get( "game:score:123" )
    .toBeBetween( 0, 1000 )
    .orElse( 0 )

// Validate with regex patterns
email = cache().get( "user:email:123" )
    .toMatchRegex( "^[A-Za-z0-9+_.-]+@(.+)$" )
    .orElse( "[email protected]" )

Side Effects and Actions

// Execute side effects with ifPresent()
cache().get( "user:123" )
    .ifPresent( function( user ) {
        updateLastAccessed( user.userID )
        logUserActivity( user.userID, "cache_hit" )
    } )
    .ifEmpty( function() {
        logCacheMiss( "user:123" )
    } )

// Conditional actions based on validation
cache().get( "session:abc123" )
    .toSatisfy( ( session ) -> session.expiresAt > now() )
    .ifValid( function( session ) {
        extendSessionTimeout( session.sessionID )
    } )
    .ifInvalid( function( session ) {
        cache().clear( "session:abc123" )
        invalidateSession( session.sessionID )
    } )

// Chain multiple conditional actions
cache().get( "user:123" )
    .ifPresent( function( user ) {
        writeLog( "User " & user.userID & " loaded from cache", "info" )
    } )
    .filter( ( user ) -> user.lastLogin > dateAdd( "d", -30, now() ) )
    .ifPresent( function( user ) {
        writeLog( "Recent user activity detected", "info" )
    } )
    .ifEmpty( function() {
        writeLog( "User not recently active or not cached", "warn" )
    } )

Error Handling and Recovery

// Graceful error handling with orThrow()
function getRequiredUser( userID ) {
    return cache().get( "user:" & userID )
        .orThrow( "UserNotFound", "User " & userID & " not found in cache" )
}

// Try cache with multiple fallbacks
userData = cache().get( "user:primary:123" )
    .or( function() {
        return cache().get( "user:backup:123" )
    } )
    .orElseGet( function() {
        return loadUserFromDatabase( 123 )
    } )

// Attempt with validation and recovery
safeUserData = cache().get( "user:123" )
    .toBeType( "struct" )
    .filter( ( user ) -> !structIsEmpty( user ) )
    .or( function() {
        // Try backup cache
        return cache( "backup" ).get( "user:123" )
    } )
    .orElseGet( function() {
        // Last resort - load from database
        var user = loadUserFromDatabase( 123 )
        // Cache for next time
        cache().set( "user:123", user, 3600 )
        return user
    } )

Stream Operations

// Process cached data as streams
cache().get( "user:123" )
    .stream()
    .map( ( user ) -> user.departmentID )
    .forEach( function( deptID ) {
        processDepartment( deptID )
    } )

// Combine multiple cache results
userIDs = [ "123", "456", "789" ]
activeUsers = userIDs
    .map( function( id ) {
        return cache().get( "user:" & id )
    } )
    .filter( ( attempt ) -> attempt.isPresent() )
    .map( ( attempt ) -> attempt.get() )
    .filter( ( user ) -> user.status == "active" )

Advanced Functional Patterns

// Memoization pattern with cache
function getMemoizedData( key, expensiveFunction, timeout = 3600 ) {
    return cache().get( key )
        .orElseGet( function() {
            var result = expensiveFunction()
            cache().set( key, result, timeout )
            return result
        } )
}

// Usage
reportData = getMemoizedData( "report:monthly:2024", function() {
    return generateComplexReport( "2024" )
}, 86400 )

// Circuit breaker pattern
function getWithCircuitBreaker( key, fallbackFunction ) {
    var circuitKey = "circuit:" & key
    var circuitState = cache().get( circuitKey ).orElse( "closed" )
    
    if ( circuitState == "open" ) {
        // Circuit is open, use fallback
        return fallbackFunction()
    }
    
    return cache().get( key )
        .ifEmpty( function() {
            // Track failures
            var failures = cache().get( "failures:" & key ).orElse( 0 ) + 1
            cache().set( "failures:" & key, failures, 300 )
            
            if ( failures >= 5 ) {
                // Open circuit
                cache().set( circuitKey, "open", 60 )
            }
        } )
        .orElseGet( fallbackFunction )
}

// Cached computation with dependency invalidation
function getCachedWithDependencies( key, dependencies, computeFunction ) {
    var cacheKey = key & ":" & hash( serialize( dependencies ) )
    
    return cache().get( cacheKey )
        .filter( function( cached ) {
            // Validate dependencies haven't changed
            return dependencies.every( function( dep ) {
                var depValue = cache().get( "dep:" & dep )
                return depValue.isPresent() && 
                       depValue.get().timestamp == cached.depTimestamp
            } )
        } )
        .orElseGet( function() {
            var result = computeFunction()
            result.depTimestamp = now()
            cache().set( cacheKey, result, 3600 )
            return result
        } )
}

Bulk Retrieval with Functional Processing

// Process bulk results functionally
keys = [ "user:123", "user:456", "user:789" ]
results = cache().get( keys )

// Extract successful results and handle misses
users = [ ]
missingKeys = [ ]

for ( var key in results ) {
    var attempt = results[ key ]
    attempt
        .ifPresent( function( user ) {
            arrayAppend( users, user )
        } )
        .ifEmpty( function() {
            arrayAppend( missingKeys, key )
        } )
}

// Load missing users and cache them
if ( !arrayIsEmpty( missingKeys ) ) {
    var missingUserIDs = missingKeys.map( ( key ) -> listLast( key, ":" ) )
    
    var loadedUsers = loadUsersFromDatabase( missingUserIDs )
    var cacheEntries = { }
    
    for ( var user in loadedUsers ) {
        arrayAppend( users, user )
        cacheEntries[ "user:" & user.userID ] = user
    }
    
    cache().set( cacheEntries, 3600 )
}

Silent Retrieval and Internal Operations

// Silent retrieval (no stats tracking)
internalConfig = cache().getQuiet( "internal:config" )
    .orElseGet( function() {
        return loadInternalConfiguration()
    } )

// System operations without affecting cache statistics
systemHealth = cache().getQuiet( "system:health" )
    .filter( ( health ) -> health.timestamp > dateAdd( "m", -5, now() ) )
    .orElseGet( function() {
        return performHealthCheck()
    } )

Filtered Retrieval with Complex Processing

// Get all user sessions and process them
userFilter = cacheFilter( "user:session:*" )
userSessions = cache().get( userFilter )

// Process each session attempt
activeSessions = [ ]
expiredSessions = [ ]

for ( var key in userSessions ) {
    userSessions[ key ]
        .filter( ( session ) -> session.expiresAt > now() )
        .ifPresent( function( session ) {
            arrayAppend( activeSessions, session )
        } )
        .ifEmpty( function() {
            arrayAppend( expiredSessions, key )
        } )
}

// Clean up expired sessions
if ( !arrayIsEmpty( expiredSessions ) ) {
    cache().clear( expiredSessions )
}

Get-or-Set Pattern

The BoxCache Provider includes the powerful getOrSet() method to eliminate double-lookup patterns:

// Basic get-or-set with default timeouts
userData = cache().getOrSet("user:123", () => {
    return loadUserFromDatabase(123)
})

// With custom timeouts
productData = cache().getOrSet("product:456", () => {
    return loadProductFromAPI(456)
}, 3600, 1800) // 1 hour, 30 min idle

// With metadata
reportData = cache().getOrSet("report:monthly", () => {
    return generateMonthlyReport()
}, 86400, 43200, {"priority": 10}) // 24 hours, 12 hour idle

Clearing Data

Remove data from the cache with various options:

// Clear single key
cleared = cache().clear("user:123") // Returns true/false

// Clear multiple keys
results = cache().clear(["user:123", "user:456", "user:789"])
// Returns: {"user:123": true, "user:456": false, ...}

// Clear with pattern filter
userFilter = cacheFilter("user:session:*"
cache().clear(userFilter);

// Clear all data
cache().clearAll()

// Silent clear (no events or stats)
cache().clearQuiet("internal:temp")

Key Management and Filtering

Getting Cache Keys

// Get all keys
allKeys = cache().getKeys()

// Get keys with filter
sessionKeys = cache().getKeys(cacheFilter("session:*"))
userKeys = cache().getKeys(cacheFilter("user:*"))

// Stream processing for large caches
cache().getKeysStream()
    .filter( (key) => {
        return key.startsWith("temp:") && 
               cache().getQuiet(key).isPresent()
    })
    .forEach( (key) => {
        cache().clear(key)
    })

Using Cache Filters

Cache filters provide powerful pattern matching for bulk operations:

// Wildcard filters
tempFilter = cacheFilter("temp:*")           // All temp keys
userFilter = cacheFilter("user:session:*")   // User sessions
apiFilter = cacheFilter("api:v?:*")          // API versions (v1, v2, etc.)

// Regex filters  
emailFilter = cacheFilter(".*@domain\.com$", true )
idFilter = cacheFilter("^id_\d{4,6}$", true)

// Custom filter functions
expiredFilter = (key) => {
    var entry = cache().getQuiet(key)
    if (!entry.isPresent()) return false;
    
    var data = entry.get();
    return structKeyExists(data, "expiry") && 
           data.expiry < now();
};

// Use filters in operations
cache().clear(tempFilter)
cache().get(userFilter)
cache().lookup(expiredFilter)

Lookup Operations

Check for key existence without retrieving data:

// Basic lookup (with stats tracking)
exists = cache().lookup("user:123")

// Silent lookup (no stats)
exists = cache().lookupQuiet("internal:config")

// Bulk lookup
results = cache().lookup(["user:123", "user:456"])
// Returns: {"user:123": true, "user:456": false}

// Filtered lookup
userFilter = cacheFilter("user:*")
userExistence = cache().lookup(userFilter)

Statistics and Monitoring

The BoxCache Provider includes comprehensive statistics tracking:

// Get cache statistics
stats = cache().getStats()
writeOutput("Hit Rate: " & numberFormat(stats.getHitRate() * 100, "0.00") & "%")
writeOutput("Hits: " & stats.getHits())
writeOutput("Misses: " & stats.getMisses())
writeOutput("Cache Size: " & cache().getSize())

// Cache metadata
writeOutput("Cache Name: " & cache().getName())
writeOutput("Provider Type: " & cache().getType())
writeOutput("Is Enabled: " & cache().isEnabled())
writeOutput("Reporting Enabled: " & cache().isReportingEnabled()

// Clear statistics
cache().clearStats()

// Object-specific metadata
metadata = cache().getCachedObjectMetadata("user:123")
if (!structIsEmpty(metadata)) {
    writeOutput("Last Accessed: " & metadata.lastAccessed)
    writeOutput("Hit Count: " & metadata.hits)
    writeOutput("Created: " & metadata.created)
}

// Bulk metadata for reporting
metadataReport = cache().getStoreMetadataReport(100) // Limit to 100 entries

Configuration and Properties

Access and understand cache configuration:

// Get cache configuration
config = cache().getConfig(
properties = config.properties;

writeOutput("Max Objects: " & properties.maxObjects)
writeOutput("Default Timeout: " & properties.defaultTimeout)
writeOutput("Eviction Policy: " & properties.evictionPolicy)
writeOutput("Object Store: " & properties.objectStore)

// Key configuration properties:
// - maxObjects: Maximum number of cached objects
// - defaultTimeout: Default expiration time in seconds  
// - defaultLastAccessTimeout: Default idle timeout
// - evictionPolicy: LRU, MRU, LFU, MFU, FIFO, LIFO, Random
// - objectStore: ConcurrentHashMap, ConcurrentSoftReference, Disk, Custom
// - reapFrequency: How often to run maintenance (seconds)
// - freeMemoryPercentageThreshold: Memory threshold for eviction
// - useLastAccessTimeouts: Enable/disable idle timeouts

Async Operations

BoxCache supports asynchronous operations for high-performance scenarios:

// Async get operation
future = cache().getAsync("user:123")

// Process the result when ready
future.thenAccept( (result) => {
    if (result.isPresent()) {
        user = result.get()
        processUser(user)
    }
});

// Or wait for completion
result = future.get() // Blocks until complete

Cache Maintenance

Manual Maintenance Operations

// Trigger reaping (cleanup expired entries)
cache().reap()

// Get cache size
size = cache().getSize()

// Check if cache is operational
if ( cache().isEnabled() ) {
    // Cache is ready for operations
}

Automatic Maintenance

BoxCache automatically handles:

  • Expiration Checking: Removes expired entries based on timeouts

  • Idle Timeout Management: Removes entries that haven't been accessed

  • Memory Threshold Eviction: Triggers eviction when memory usage is high

  • Scheduled Reaping: Periodic cleanup based on reapFrequency setting

Event Integration

BoxCache broadcasts events for all major operations:

// Cache events are automatically announced:
// - BEFORE_CACHE_ELEMENT_REMOVED / AFTER_CACHE_ELEMENT_REMOVED
// - AFTER_CACHE_ELEMENT_INSERT
// - AFTER_CACHE_ELEMENT_UPDATED  
// - AFTER_CACHE_CLEAR_ALL

// Listen to events (if you have interceptor components)
class {
    
    function afterCacheElementInsert(event, data) {
        writeLog("Cache item added: " & data.key, "info")
    }
    
    function afterCacheElementRemoved(event, data) {
        writeLog("Cache item removed: " & data.key & ", cleared: " & data.cleared, "info")
    }
}

Practical Usage Patterns

User Session Management

class {
    
    function storeUserSession(sessionID, userData) {
        var sessionKey = "session:" & sessionI
        cache("sessions").set(sessionKey, userData, 3600, 180 ) // 1 hour, 30 min idle
    }
    
    function getUserSession(sessionID) {
        var sessionKey = "session:" & sessionID
        return cache("sessions").get(sessionKey)
    }
    
    function updateSessionActivity(sessionID) {
        var sessionKey = "session:" & sessionID
        var session = cache("sessions").get(sessionKey)
        
        if (session.isPresent()) {
            var sessionData = session.get()
            sessionData.lastActivity = now()
            cache("sessions").set(sessionKey, sessionData, 3600, 1800)
        }
    }
    
    function clearUserSessions(userID) {
        var filter = cacheFilter("session:*:user:" & userID)
        cache("sessions").clear(filter)
    }
}

API Response Caching

class {
    
    function getCachedAPIResponse(endpoint, params) {
        var cacheKey = "api:" & endpoint & ":" & hash(serialize(params));
        
        return cache("api").getOrSet(cacheKey, function() {
            return makeAPICall(endpoint, params);
        }, 600); // 10 minutes
    }
    
    function invalidateAPICache(endpoint) {
        if (isNull(endpoint)) {
            // Clear all API cache
            cache("api").clear(cacheFilter("api:*"));
        } else {
            // Clear specific endpoint
            cache("api").clear(cacheFilter("api:" & endpoint & ":*"));
        }
    }
    
    function warmAPICache() {
        var popularEndpoints = getPopularEndpoints();
        
        for (var endpoint in popularEndpoints) {
            getCachedAPIResponse(endpoint.name, endpoint.defaultParams);
        }
    }
}

Database Query Caching

class {
    
    function getCachedQuery(sql, params, timeout = 1800) {
        var cacheKey = "query:" & hash(sql & params.tostring() );
        
        return cache("queries").getOrSet(cacheKey, function() {
            return queryExecute(sql, params);
        }, timeout);
    }
    
    function invalidateQueryCache(tables) {
        for (var table in tables) {
            var filter = cacheFilter("query:*" & table & "*");
            cache("queries").clear(filter);
        }
    }
    
    function getQueryCacheStats() {
        var stats = cache("queries").getStats();
        return {
            "hitRate": stats.getHitRate(),
            "size": cache("queries").getSize(),
            "hits": stats.getHits(),
            "misses": stats.getMisses()
        };
    }
}

Multi-Level Caching

class {
    
    function getData(key) {
        // Try L1 cache (fast, small)
        var l1Result = cache("l1").get(key);
        if (l1Result.isPresent()) {
            return l1Result.get();
        }
        
        // Try L2 cache (slower, larger)  
        var l2Result = cache("l2").get(key);
        if (l2Result.isPresent()) {
            var data = l2Result.get();
            // Promote to L1
            cache("l1").set(key, data, 300); // 5 minutes in L1
            return data;
        }
        
        // Load from source
        var data = loadFromDatabase(key);
        
        // Store in both levels
        cache("l1").set(key, data, 300);     // 5 minutes
        cache("l2").set(key, data, 3600);    // 1 hour
        
        return data;
    }
    
    function invalidateData(key) {
        cache("l1").clear(key);
        cache("l2").clear(key);
    }
}

Performance Optimization

Efficient Key Design

// Good key patterns - hierarchical and descriptive
"user:profile:" & userID
"product:inventory:" & productID & ":" & warehouseID
"api:response:v1:" & endpoint & ":" & hash(params)

// Avoid overly complex keys
"user:" & userID & ":profile:settings:theme:color" // Too deep

// Use consistent naming conventions
"session:user:" & userID & ":" & sessionID
"session:admin:" & adminID & ":" & sessionID

Batch Operations

// Efficient bulk operations
function loadMultipleUsers(userIDs) {
    var cacheKeys = {};
    var missingIDs = [];
    
    // Build cache keys
    for (var userID in userIDs) {
        cacheKeys["user:" & userID] = userID;
    }
    
    // Bulk lookup
    var results = cache("users").get(arrayToList(structKeyArray(cacheKeys)));
    var users = {};
    
    // Process results and identify misses
    for (var key in results) {
        var result = results[key];
        var userID = cacheKeys[key];
        
        if (result.isPresent()) {
            users[userID] = result.get();
        } else {
            arrayAppend(missingIDs, userID);
        }
    }
    
    // Load missing users
    if (!arrayIsEmpty(missingIDs)) {
        var missingUsers = loadUsersFromDatabase(missingIDs);
        var entriesToCache = {};
        
        for (var userID in missingUsers) {
            users[userID] = missingUsers[userID];
            entriesToCache["user:" & userID] = missingUsers[userID];
        }
        
        // Bulk cache the missing users
        cache("users").set(entriesToCache, 3600);
    }
    
    return users;
}

Memory Management

// Monitor cache memory usage
function monitorCacheMemory() {
    var caches = cacheNames();
    var report = {};
    
    for (var cacheName in caches) {
        var cacheInstance = cache(cacheName);
        var config = cacheInstance.getConfig();
        var size = cacheInstance.getSize();
        var maxSize = config.properties.maxObjects;
        
        report[cacheName] = {
            "size": size,
            "maxSize": maxSize,
            "utilizationPercent": (size / maxSize) * 100,
            "stats": cacheInstance.getStats()
        };
        
        // Alert if approaching capacity
        if ((size / maxSize) > 0.8) {
            writeLog("Cache " & cacheName & " is " & 
                    numberFormat((size/maxSize)*100, "0.0") & "% full", "warn");
        }
    }
    
    return report;
}

Error Handling and Resilience

Graceful Degradation

function resilientCacheOperation(key, fallbackFunction) {
    try {
        var result = cache().get(key);
        if (result.isPresent()) {
            return result.get();
        }
    } catch (any e) {
        writeLog("Cache get error for key " & key & ": " & e.message, "error");
    }
    
    // Fallback to direct operation
    var data = fallbackFunction();
    
    // Try to cache the result
    try {
        cache().set(key, data, 1800);
    } catch (any e) {
        writeLog("Cache set error for key " & key & ": " & e.message, "warn");
    }
    
    return data;
}

Health Checks

function checkCacheHealth() {
    var health = {
        "status": "healthy",
        "issues": []
    };
    
    try {
        var testKey = "healthcheck:" & createUUID();
        var testValue = "test";
        
        // Test write
        cache().set(testKey, testValue, 60);
        
        // Test read
        var result = cache().get(testKey);
        if (!result.isPresent() || result.get() != testValue) {
            arrayAppend(health.issues, "Cache read/write test failed");
            health.status = "unhealthy";
        }
        
        // Test delete
        cache().clear(testKey);
        
        // Check stats
        var stats = cache().getStats();
        if (stats.getHitRate() < 0.5 && stats.getHits() > 100) {
            arrayAppend(health.issues, "Low hit rate: " & numberFormat(stats.getHitRate() * 100, "0.00") & "%");
            health.status = "degraded";
        }
        
        // Check size
        var size = cache().getSize();
        var config = cache().getConfig();
        var maxSize = config.properties.maxObjects;
        
        if (size > (maxSize * 0.9)) {
            arrayAppend(health.issues, "Cache nearly full: " & size & "/" & maxSize);
            health.status = "degraded";
        }
        
    } catch (any e) {
        arrayAppend(health.issues, "Cache operation failed: " & e.message);
        health.status = "unhealthy";
    }
    
    return health;
}

Best Practices

1. Key Naming Conventions

// Use consistent, hierarchical naming
"user:profile:" & userID
"session:data:" & sessionID  
"product:details:" & productID
"api:response:" & version & ":" & endpoint

// Include versioning for data structures
"user:profile:v2:" & userID
"config:app:v1:" & environment

2. Appropriate Timeouts

// Short-lived dynamic data
cache().set("stock:price:" & symbol, price, 30); // 30 seconds

// User sessions
cache().set("session:" & sessionID, sessionData, 3600, 1800 // 1 hour, 30 min idle

// Reference data
cache().set("config:app", appConfig, 86400) // 24 hours

// Computed reports
cache().set("report:daily", reportData, 43200) // 12 hours

3. Memory Efficiency

// Store minimal necessary data
cache().set("user:summary:" & userID, {
    "name": user.name,
    "email": user.email,
    "role": user.role
}, 3600)

// Instead of storing the entire user object
// cache().set("user:full:" & userID, entireUserObject, 3600);

4. Strategic Cache Warming

function warmCriticalCaches() {
    // Warm high-traffic reference data
    var appConfig = loadApplicationConfig()
    cache().set("config:app", appConfig, 86400)
    
    // Warm popular user sessions
    var activeUsers = getActiveUserIDs()
    for (var userID in activeUsers) {
        var userData = loadUserProfile(userID)
        cache().set("user:profile:" & userID, userData, 3600)
    }
    
    // Warm frequently accessed products
    var popularProducts = getPopularProductIDs()
    for (var productID in popularProducts) {
        var productData = loadProductDetails(productID)
        cache().set("product:details:" & productID, productData, 7200)
    }
}

Conclusion

The BoxCache Provider offers a comprehensive, high-performance caching solution for BoxLang applications. By leveraging its rich feature set including flexible storage options, intelligent eviction policies, comprehensive monitoring, and powerful filtering capabilities, developers can build scalable, efficient applications.

Key advantages of BoxCache Provider:

  • Developer Friendly: Simple BIF-based access with powerful functionality

  • High Performance: Concurrent operations, async support, and efficient data structures

  • Flexible Configuration: Pluggable object stores and eviction policies

  • Production Ready: Comprehensive monitoring, event integration, and automatic maintenance

  • Scalable: Supports everything from simple key-value caching to complex multi-level architectures

Whether you're caching user sessions, API responses, database queries, or computed data, the BoxCache Provider provides the tools and flexibility needed to optimize your application's performance and scalability.

Last updated

Was this helpful?