TikNix Caching System Documentation

Overview

TikNix implements a sophisticated multi-tier caching architecture that delivers exceptional performance improvements with zero application code changes. The system provides 9.4x faster database queries and 99.7% faster permission checks out of the box.

Architecture

┌─────────────────────────────────────────────────┐
│              Application Layer                   │
├─────────────────────────────────────────────────┤
│         CachedDatabaseAdapter                    │
│    (Transparent Query Cache - 9.4x faster)       │
├─────────────────────────────────────────────────┤
│            PermissionCache                       │
│  (3-Tier: Memory → APCu → DB - 99.7% faster)    │
├─────────────────────────────────────────────────┤
│              APCu Shared Memory                  │
│         (Cross-request persistence)              │
├─────────────────────────────────────────────────┤
│               OPcache Preloading                 │
│          (Framework files in memory)             │
└─────────────────────────────────────────────────┘

Component Details

1. CachedDatabaseAdapter

Location: lib/CachedDatabaseAdapter.php

The CachedDatabaseAdapter is a transparent drop-in replacement for RedBeanPHP's standard database adapter that automatically caches all SELECT queries.

Features:

  • 100% Transparent: No code changes required
  • Automatic Caching: All SELECT, SHOW queries cached
  • Smart Invalidation: Auto-clears on INSERT, UPDATE, DELETE
  • JOIN Support: Tracks all tables in complex queries
  • Multi-tenant Safe: Unique cache keys per installation

How It Works:

1. Query Interception: Intercepts all database operations at the adapter level
2. Cache Key Generation: Creates MD5 hash from SQL + parameters + site identifier
3. Table Tracking: Extracts and tracks all tables from FROM and JOIN clauses
4. Version Management: Each table has a version number that changes on modification
5. Automatic Invalidation: When a table is modified, its version increments, invalidating all related cached queries

Performance Metrics:

  • Speed Improvement: 9.4x faster (16ms → 1.7ms for 1000 queries)
  • Hit Rate: 99.9% in production
  • Memory Usage: <1MB for typical applications

Configuration:

[cache]
query_cache = true              ; Enable query caching
query_cache_ttl = 60            ; Default TTL in seconds

Usage:

// No code changes needed! These are automatically cached:
$users = R::getAll('SELECT * FROM users');           // Cached
$count = R::count('users', 'active = ?', [1]);      // Cached
$bean = R::findOne('user', 'email = ?', [$email]);  // Cached

// Modifications automatically invalidate cache:
R::store($user); // Clears all cached queries involving 'users' table

2. PermissionCache

Location: lib/PermissionCache.php

Three-tier caching system for ultra-fast permission checks.

Cache Tiers:

1. Process Memory (Tier 1)
  • Fastest: 0.0001ms per check
  • Lives only during request
  • No serialization overhead
2. APCu Shared Memory (Tier 2)
  • Fast: 0.01ms per check
  • Persists across requests
  • Shared between PHP processes
3. Database (Tier 3)
  • Slowest: 1-2ms per check
  • Persistent storage
  • Source of truth

Features:

  • Hierarchical Lookup: Checks memory → APCu → database
  • Automatic Population: Fills higher tiers on cache miss
  • Batch Loading: Loads all permissions at once
  • Build Mode: Auto-creates permissions during development
  • Version-Based Invalidation: Cache keys include version timestamp from file
  • CLI-Safe Clearing: Clear cache from CLI without PHP-FPM restart

Performance Metrics:

  • Speed: 175,000 checks/second
  • Improvement: 99.7% faster than database lookups
  • Memory: <100KB for typical permission set

Cache Invalidation Strategy:

The PermissionCache uses a version-file approach to invalidate caches across all processes:

1. Cache Key Format: tiknix_[site_id]_permissions_v[timestamp]
2. Version File: cache/.permission_cache_version contains current timestamp
3. On Clear: Updates version file with new timestamp
4. Next Request: All processes read new version, look for cache with new version key
5. Auto-Reload: Cache miss triggers reload from database with new version key

Key Benefits:
  • Works from CLI without PHP-FPM restart
  • No race conditions between processes
  • Automatic across all workers
  • File system ensures atomic updates

API:

// Check permission (automatically cached)
$allowed = PermissionCache::check('Admin', 'users', $userLevel);

// Clear cache after permission changes (works from CLI)
PermissionCache::clear();

// Warm up cache
PermissionCache::warmup();

// Get statistics
$stats = PermissionCache::getStats();

3. OPcache Preloading

Location: conf/opcache_preload.php

Loads framework files into memory at PHP startup, eliminating file I/O.

Configuration:

For PHP-FPM:

; In php-fpm pool config
php_admin_value[opcache.preload] = /path/to/conf/opcache_preload.php
php_admin_value[opcache.preload_user] = www-data

For Apache mod_php:

php_admin_value opcache.preload /path/to/conf/opcache_preload.php
php_admin_value opcache.preload_user www-data

Benefits:

  • Eliminates file system checks
  • Reduces autoloader overhead
  • Faster class instantiation
  • Lower memory usage per request

Cache Management

Admin Interface

Access the cache management interface at /admin/cache (requires admin privileges).

Features:

  • Real-time Statistics: Hit rates, memory usage, cached items
  • Cache Operations:
  • Clear All Caches
  • Clear Query Cache Only
  • Reload Permission Cache
  • Warm Up Cache
  • Visual Monitoring:
  • Progress bars for memory usage
  • Color-coded hit rate indicators
  • Detailed permission listing

CLI Commands

The scripts/resetcache.php script provides a fast, reliable way to clear the permission cache from CLI without requiring PHP-FPM restart:

# Clear permission cache from CLI
php scripts/resetcache.php
How it works:
  • Updates a version file at cache/.permission_cache_version with a new timestamp
  • All PHP-FPM workers automatically invalidate their APCu caches on next request
  • No service restart required - works across all processes (CLI and web)
  • Shows before/after statistics and confirmation
When to use:
  • After bulk permission changes
  • After database schema updates
  • During deployment scripts
  • When troubleshooting permission issues

Legacy Cache Commands

# Clear all caches via admin controller
php index.php --control=cache --method=clear

# Show cache statistics
php index.php --control=cache --method=stats

# Warm up caches
php index.php --control=cache --method=warmup

Installation

1. Install APCu

# Ubuntu/Debian
sudo apt-get install php8.1-apcu

# Enable for CLI (optional, for testing)
echo "apc.enable_cli=1" | sudo tee -a /etc/php/8.1/cli/conf.d/20-apcu.ini

# Restart PHP-FPM
sudo systemctl restart php8.1-fpm

2. Configure Memory Limits

Edit /etc/php/8.1/mods-available/apcu.ini:

apc.shm_size=64M        ; Shared memory size
apc.ttl=3600            ; Time-to-live for cache entries
apc.user_ttl=3600       ; User cache TTL
apc.gc_ttl=3600         ; Garbage collection TTL
apc.enable_cli=1        ; Enable for CLI (testing)

3. Enable in TikNix

Edit conf/config.ini:

[cache]
enabled = true
query_cache = true
query_cache_ttl = 60

4. Verify Installation

# Test caching system
php test_cached_adapter.php

# Expected output:
# ✓ Cache working! 84.5% faster
# ✓ 9.4x faster with cache!
# ✓ Hit rate: 99.9%

Multi-Tenant Considerations

The caching system is designed for multi-tenant environments:

Namespace Isolation

Each installation gets unique cache keys based on:
  • Installation directory path
  • Domain name (HTTP_HOST)
  • MD5 hash combination
$siteId = md5(__DIR__ . '_' . ($_SERVER['HTTP_HOST'] ?? 'cli'));
$cacheKey = "tiknix_{$siteId}_" . md5($sql . serialize($params));

Benefits:

  • No cache collisions between sites
  • Independent cache management
  • Secure data isolation
  • Shared APCu memory pool efficiency

Performance Tuning

Query Cache TTL

Adjust TTL based on data volatility:

// Static data - long TTL
query_cache_ttl = 3600  ; 1 hour

// Semi-dynamic data - medium TTL
query_cache_ttl = 300 ; 5 minutes

// Dynamic data - short TTL
query_cache_ttl = 30 ; 30 seconds

Memory Allocation

Monitor and adjust APCu memory:

# Check memory usage
php -r "print_r(apcu_cache_info());"

# Increase if needed (in apcu.ini)
apc.shm_size=128M

Cache Warming

Pre-populate caches for better cold-start performance:

// In a startup script or cron job
PermissionCache::warmup();

// Pre-cache common queries
R::getAll('SELECT * FROM settings');
R::getAll('SELECT * FROM categories');

Troubleshooting

Cache Not Working

1. Verify APCu is enabled:

php -i | grep "apc.enabled"
# Should show: apc.enabled => On

2. Check permissions:

ls -la /var/run/php/
# PHP-FPM should have write access

3. Review logs:

tail -f log/app-*.log
# Look for cache-related errors

Low Hit Rate

  • Increase TTL for stable data
  • Verify queries are identical (whitespace matters)
  • Check if tables are being modified frequently
  • Monitor with /admin/cache interface

Memory Issues

  • Monitor APCu memory usage in admin panel
  • Increase apc.shm_size if needed
  • Implement cache key expiration strategy
  • Use shorter TTLs for large result sets

Best Practices

1. Don't Cache Everything

Avoid caching:
  • User-specific data that changes frequently
  • Real-time statistics
  • Session-dependent content
  • Queries with NOW() or RAND()

2. Use Appropriate TTLs

// Configuration data: 1 hour
$config = R::getAll('SELECT * FROM settings');  // TTL: 3600

// User list: 5 minutes
$users = R::getAll('SELECT * FROM users'); // TTL: 300

// Activity feed: 30 seconds
$feed = R::getAll('SELECT * FROM activities'); // TTL: 30

3. Manual Cache Management

When needed, manually control cache:

// Clear specific table cache
$adapter = R::getDatabaseAdapter();
if ($adapter instanceof CachedDatabaseAdapter) {
    $adapter->invalidateTable('users');
}

// Disable cache temporarily
$adapter->disableCache();
// ... operations ...
$adapter->enableCache();

4. Monitor Performance

Regularly check cache statistics:
  • Hit rate should be >90%
  • Memory usage should be <70%
  • No excessive cache evictions

Security Considerations

Cache Key Security

  • Cache keys use MD5 hashing (not for security, for key generation)
  • No sensitive data in cache keys
  • SQL and parameters are hashed together

Data Isolation

  • Multi-tenant safe with namespace isolation
  • No cross-site cache access possible
  • Each site has unique cache prefix

Cache Poisoning Prevention

  • Automatic invalidation on data changes
  • TTL limits cache lifetime
  • Admin-only cache management interface

API Reference

CachedDatabaseAdapter

class CachedDatabaseAdapter extends DBAdapter {
    public function enableCache();
    public function disableCache();
    public function clearAllCache();
    public function invalidateTable($table);
    public function getCacheStats();
}

PermissionCache

class PermissionCache {
    public static function check($control, $function, $level);
    public static function clear();
    public static function reload();
    public static function warmup();
    public static function getStats();
    public static function getAll();
}

Conclusion

TikNix's caching system provides enterprise-level performance with zero configuration complexity. The transparent nature means existing applications get immediate benefits without any code changes, while the comprehensive admin interface provides full visibility and control when needed.

Key takeaways:
  • 9.4x faster queries with CachedDatabaseAdapter
  • 175,000 permission checks/second with PermissionCache
  • Zero code changes required
  • Multi-tenant safe by design
  • Full admin control via web interface

For questions or issues, check the logs at log/app-*.log or use the admin interface at /admin/cache.