Overview
tif1 implements a multi-layer caching system designed to minimize network requests, reduce latency, and maximize data access performance. The caching architecture follows the principle of locality of reference. Multiple storage tiers balance speed, capacity, and persistence.
The caching system is critical to the performance of tif1. Without caching, every data access requires a network round-trip to the CDN, which introduces latency of 500ms-3s per request. The multi-layer cache serves subsequent accesses from memory in microseconds or from disk in milliseconds. This is a 1000-10000x performance improvement for cached data.
Reasons for Multi-Layer Caching
The multi-layer approach provides several key advantages:- Speed Hierarchy: Different storage tiers offer different speed/capacity tradeoffs. Memory is fastest but limited; disk is slower but abundant.
- Persistence: In-memory caches are lost on process restart, while disk caches survive across sessions.
- Sharing: Process-local memory caches cannot be shared, while disk caches enable multi-process coordination.
- Graceful Degradation: If one cache layer fails, the system falls back to the next layer automatically.
- Optimal Resource Usage: Hot data stays in fast memory. Warm data stays on disk.
tif1fetches cold data on demand.
Performance Impact
Real-world performance improvements with caching enabled:- First access (cold cache): 2-3 seconds (network fetch + processing)
- Second access (warm cache): 20-100ms (disk read + decompression)
- Third access (hot cache): <1ms (memory read)
- Overall speedup: 100-3000x for cached data
Cache Architecture
Thetif1 caching system consists of two primary layers that work together, with an optional third layer for distributed deployments. Each layer serves a specific purpose in the performance hierarchy.
Cache Flow in Detail
Whentif1 receives a data request (for example, session.laps), the system follows this flow:
-
Memory Cache Lookup (Layer 1)
- Check if data exists in the in-process LRU cache
- If found: Return immediately (~1ms latency)
- If not found: Proceed to Layer 2
-
SQLite Cache Lookup (Layer 2)
- Query SQLite database for cached entry
- If found: Decompress Parquet blob, deserialize to DataFrame (~20-100ms)
- Store result in Memory Cache for future access
- Return data
- If not found: Proceed to CDN fetch
-
CDN Fetch (Network Layer)
- Construct CDN URL from request parameters
- Fetch JSON data via HTTP (with retry logic)
- Parse JSON and construct DataFrame (~500ms-3s)
- Compress and store in SQLite cache
- Store in Memory Cache
- Return data
-
Cache Population
- Every successful fetch populates both cache layers
- Subsequent requests benefit from cached data
- Cache entries include metadata (timestamps, size, access count)
Cache Hierarchy Benefits
Layer 1: Memory Cache (LRU)
The first and fastest cache layer is an in-memory LRU (Least Recently Used) cache. It extends the Pythonfunctools.lru_cache decorator with custom enhancements. This cache stores recently accessed DataFrames and metadata objects directly in process memory.
Architecture & Implementation
The memory cache uses a doubly-linked list combined with a hash map for O(1) access and O(1) eviction:- Hash Map: Provides constant-time lookups by cache key
- Doubly-Linked List: Maintains access order for LRU eviction
- Thread-Safe: Uses locks to ensure thread-safe access in multi-threaded environments
- Automatic Eviction: When the cache reaches capacity, it evicts the least recently used items automatically
Characteristics
- Access Speed: Sub-millisecond (typically 0.1-1ms)
- Default Capacity: 100 items (configurable up to 1000+)
- Scope: Process-specific (not shared across processes or threads)
- Lifetime: Cleared when process exits or cache is manually cleared
- Memory Overhead: ~50-100 bytes per entry plus data size
- Eviction Policy: Least Recently Used (LRU)
- Thread Safety: Yes (with internal locking)
Configuration Options
Cached Data Types
The memory cache stores the following data types:-
Session Metadata
- Event information (name, location, date)
- Session type and timing
- Circuit information
- Size: ~1-5 KB per session
-
Lap DataFrames
- Complete lap timing data for all drivers
- Sector times, compound information
- Size: ~2-5 MB per session (20 drivers × 50-70 laps)
-
Telemetry DataFrames
- High-frequency sensor data (speed, throttle, brake, gear, RPM, DRS)
- Sampled at ~10-50 Hz
- Size: ~10-20 MB per session (all drivers)
-
Weather Data
- Track temperature, air temperature, humidity, pressure
- Rainfall status and intensity
- Size: ~100-500 KB per session
-
Race Control Messages
- Flags, penalties, safety car periods
- Driver messages and notifications
- Size: ~50-200 KB per session
Cache Key Generation
tif1 generates cache keys deterministically from request parameters to ensure consistency:
Memory Cache Behavior
Cache Hit Scenario:Memory Management
The memory cache automatically manages memory usage:Performance Characteristics
Benchmark results for memory cache operations:- Metadata: ~50-100 bytes (key, timestamps, access count)
- Data: Actual DataFrame size (2-20 MB typical)
- Total: Data size + ~100 bytes
Best Practices for Memory Cache
Size Configuration
Size Configuration
- Interactive analysis: 100-200 items (default)
- Batch processing: 50-100 items (lower memory footprint)
- Real-time dashboards: 200-500 items (maximize hit rate)
- Memory-constrained: 20-50 items or disable entirely
Multi-Process Considerations
Multi-Process Considerations
- Separate caches: Processes do not share the memory cache
- SQLite coordination: Use SQLite cache for cross-process sharing
- Warm-up: Each process should warm its own cache
- Memory multiplication: Total memory = cache_size × num_processes
Memory Pressure
Memory Pressure
- Monitor system memory usage
- Reduce cache size if memory is constrained
- Disable memory cache in low-memory environments
- Rely on SQLite cache for persistence
Layer 2: SQLite Persistent Cache
The second cache layer is a SQLite database that provides persistent, disk-based storage for cached data. This layer is positioned between the fast but volatile memory cache and the slow but reliable network fetches.Architecture & Implementation
The SQLite cache is a single-file database with an optimized schema and indexes:- Storage Format: Single SQLite database file with BLOB storage
- Compression: Zstandard (zstd) compression for 60-80% size reduction
- Serialization: Apache Parquet format for efficient DataFrame storage
- Indexing: B-tree indexes on key and access time for fast lookups
- Transactions: ACID-compliant transactions for data integrity
- Concurrency: WAL (Write-Ahead Logging) mode for concurrent reads/writes
- Vacuum: Automatic space reclamation on cleanup operations
Characteristics
- Access Speed: 20-100ms (disk I/O + decompression)
- Capacity: Unlimited (disk-limited, typically 100MB-10GB)
- Scope: Shared across all processes accessing the same cache directory
- Lifetime: Survives process restarts, system reboots
- Persistence: Permanent until manually cleared or expired
- Concurrency: Multiple readers, single writer (SQLite WAL mode)
- Compression Ratio: 60-80% size reduction with zstd
- Thread Safety: Yes (SQLite handles locking)
Cache Location & Configuration
The default cache location follows the FastF1 platform layout, usingtif1 as the application directory:
- Windows:
%LOCALAPPDATA%/Temp/tif1 - macOS:
~/Library/Caches/tif1 - Linux/other POSIX:
~/.cache/tif1when~/.cacheexists; otherwise~/.tif1
Database Schema
The cache database uses an optimized schema designed for fast lookups and efficient storage:Data Storage Pipeline
Data is stored using a multi-step pipeline optimized for space and speed:Storage (Write Path)
-
DataFrame → Parquet Bytes
- Convert pandas/polars DataFrame to Apache Parquet format
- Parquet provides columnar storage with built-in compression
- Preserves data types, indexes, and metadata
- Time: ~10-50ms for typical DataFrame
-
Compress with Zstandard
- Apply zstd compression (level 3 default)
- Achieves 60-80% size reduction
- Fast compression (~500 MB/s)
- Time: ~5-20ms for typical data
-
Store in SQLite BLOB
- Insert compressed bytes into SQLite BLOB column
- An atomic transaction ensures data integrity
- Update metadata (timestamps, size, access count)
- Time: ~5-30ms depending on disk speed
Retrieval (Read Path)
-
Query SQLite by Key
- B-tree index lookup (O(log n))
- Retrieve compressed BLOB
- Update access metadata
- Time: ~1-5ms
-
Decompress with Zstandard
- Decompress zstd bytes to Parquet
- Fast decompression (~2 GB/s)
- Time: ~2-10ms
-
Parquet → DataFrame
- Parse Parquet bytes to DataFrame
- Restore data types and indexes
- Time: ~10-50ms
Storage Efficiency
Compression ratios for different data types:- Uncompressed: ~12 GB
- Compressed: ~2.5 GB
- Savings: ~9.5 GB (79% reduction)
Cache Operations
Reading from Cache
Cache Statistics
Clearing Cache
Advanced Cache Queries
Concurrency & Thread Safety
The SQLite cache handles concurrent access safely:- Multiple readers: Unlimited concurrent reads (no blocking)
- Single writer: Writes are serialized (SQLite limitation)
- Read-write: Readers do not block writers in WAL mode
- Deadlock prevention: Automatic retry with exponential backoff
- Lock timeout: 30 seconds default (configurable)
Performance Tuning
Optimize SQLite cache performance:Best Practices for SQLite Cache
Cache Location
Cache Location
- Local development: Use the tif1 OS-dependent default cache directory
- Shared server: Use a shared directory (for example,
/shared/cache/tif1/) - Docker: Mount a volume for persistence
- Cloud: Use fast SSD storage (not network drives)
- CI/CD: Use a temporary directory and clear it after tests
Disk Space Management
Disk Space Management
- Set maximum cache size limit
- Enable automatic cleanup
- Clear old entries periodically
- Monitor disk usage with alerts
- Use compression level 3-9 for space savings
Multi-Process Coordination
Multi-Process Coordination
- Use shared cache directory
- Enable WAL mode (default)
- Set appropriate lock timeout
- Handle lock timeout errors
- Consider process-specific memory caches
Backup & Recovery
Backup & Recovery
- Back up the cache database periodically
- Test cache restoration
- Rely on automatic rebuild after corruption
- Use checksums for integrity verification
- Keep cache separate from application data
Cache Operations & Workflows
Correct use of the cache system is necessary for optimal performance. This section covers common operations, workflows, and patterns.Reading from Cache
The cache system operates transparently. Applications do not check or manage cache hits and cache misses explicitly. The system handles the cache hierarchy automatically:Cache Statistics & Monitoring
Monitor cache performance and health:Clearing Cache
Multiple strategies for cache cleanup:Cache Inspection
Inspect cache contents and metadata:Cache Invalidation Strategies
Cache invalidation is a difficult problem in computer science.tif1 provides multiple strategies to ensure cache freshness while maintaining performance.
Manual Invalidation
Explicitly bypass cache for specific requests:Automatic Invalidation
The cache system automatically invalidates entries in several scenarios:1. Schema Version Changes
When the data structure changes (for example, new columns), the cache system invalidates old entries automatically:2. CDN Freshness Checks
The cache system can check the CDN for data updates with HTTP headers:3. Corruption Detection
The cache system detects and removes corrupted entries automatically:Time-Based Invalidation (TTL)
Set time-to-live for cache entries:Event-Based Invalidation
Invalidate cache when specific events occur:Selective Invalidation
Invalidate specific subsets of cache:Cache Versioning
Handle cache versioning across library updates:Best Practices for Cache Invalidation
When to Invalidate
When to Invalidate
- After library update: Schema might have changed
- When data is updated: New race results available
- On corruption: Detected errors in cached data
- For debugging: Testing data pipeline changes
- Periodic cleanup: Remove old or unused entries
- Before critical operations: Ensure fresh data
Invalidation Strategies
Invalidation Strategies
- Manual: For debugging and testing
- TTL: For data that changes predictably
- Event-based: For real-time data updates
- Freshness checks: For critical data accuracy
- Selective: For targeted invalidation
- Automatic: For schema/version changes
Performance Considerations
Performance Considerations
- Frequent invalidation: Fresh data, slower performance
- Rare invalidation: Fast performance, stale data risk
- Selective invalidation: Balances freshness and performance
- Freshness checks: Add latency but ensure accuracy
- TTL: Good balance for most use cases
Monitoring Invalidation
Monitoring Invalidation
- Monitor invalidation frequency
- Track cache miss rate after invalidation
- Measure performance impact
- Log invalidation events
- Alert on excessive invalidation
Cache Warming Strategies
Cache warming pre-populates the cache with data before the data is needed. Warming eliminates cold-start latency and ensures optimal performance from the first request.Benefits of Cache Warming
Cache warming provides several benefits:- Eliminate Cold Start: First requests are as fast as subsequent ones
- Predictable Performance: No sudden latency spikes from cache misses
- Better User Experience: Dashboards and applications load without cold-start delay
- Reduced CDN Load: Batch fetching is more efficient than on-demand
- Offline Capability: Pre-cached data works without network access
Warm Entire Season
Pre-cache all races for a complete season:Warm Specific Events
Pre-cache specific events or races:Warm Specific Data Types
Pre-cache only specific data types:Warm by Driver
Pre-cache data for specific drivers:Scheduled Cache Warming
Automatically warm cache on a schedule:Parallel Cache Warming
Maximize warming speed with parallel execution:Smart Cache Warming
Warm the cache based on usage patterns:Cache Warming Best Practices
When to Warm
When to Warm
- Before race weekend: Warm upcoming event data
- Off-peak hours: Minimize the CDN load (for example, 2-4 AM)
- After data updates: When new race results are available
- Application startup: For dashboards and services
- Before analysis: Pre-warm the data needed for the analysis
- Periodic refresh: Weekly or monthly for historical data
What to Warm
What to Warm
- Hot data: Current season, recent races
- Frequently accessed: Popular events (Monaco, Silverstone)
- Critical data: Race results, qualifying times
- User-specific: Data for favorite drivers or teams
- Predictable access: Upcoming race weekends
- Complete sessions: All data types for consistency
Warming Strategies
Warming Strategies
- Full warming: All data for all sessions (slow, complete)
- Selective warming: Specific events or data types (fast, targeted)
- Incremental warming: Warm the data when it is needed (balanced)
- Parallel warming: Multiple sessions at once (fastest)
- Scheduled warming: Periodic warming without manual work
- Smart warming: Based on access patterns (efficient)
Performance Considerations
Performance Considerations
- Parallel execution: Use async/await or threading
- Rate limiting: Do not overwhelm the CDN (max 10-20 concurrent)
- Error handling: Continue on failures, log errors
- Progress tracking: Monitor warming progress
- Resource limits: Consider memory and disk space
- Network bandwidth: Warming uses significant bandwidth
Monitoring Warming
Monitoring Warming
- Warming time: Measure the total warming time
- Success rate: Count the sessions that succeed
- Cache hit rate: Check whether warming improves the hit rate
- Storage usage: Measure the disk space used
- CDN requests: Count the requests made to the CDN
- Error patterns: Find the sessions that fail consistently
Cache Performance Analysis
Cache performance analysis guides optimization. This section provides detailed performance metrics, benchmarks, and analysis techniques.Benchmark Results
Comprehensive performance measurements across different scenarios:Access Latency by Cache State
Throughput Measurements
Cache Hit Rates by Usage Pattern
Memory Usage Patterns
Typical memory footprint per cached session:By Data Type
By Session Type
Memory Cache Capacity Planning
Disk Usage Patterns
SQLite cache storage requirements:By Season
Growth Over Time
Performance Profiling
Profile cache performance in the application:Performance Optimization Tips
Memory Cache Optimization
Memory Cache Optimization
- Increase cache size: More items = higher hit rate
- Warm frequently accessed data: Pre-load hot data
- Monitor hit rate: Aim for >80% for interactive use
- Clear unused entries: Free memory for hot data
- Use appropriate data types: Avoid caching large objects
SQLite Cache Optimization
SQLite Cache Optimization
- Use SSD storage: 10-100x faster than HDD
- Increase SQLite cache size: More memory = faster queries
- Enable WAL mode: Better concurrency (default)
- Vacuum periodically: Reclaim space, improve performance
- Use appropriate compression: Balance speed vs size
Network Optimization
Network Optimization
- Warm cache proactively: Avoid cold starts
- Use parallel fetching: Fetch multiple sessions at once
- Enable retry logic: Handle transient failures
- Monitor CDN performance: Track fetch times
- Use a nearby CDN: Reduces latency
Application-Level Optimization
Application-Level Optimization
- Batch requests: Fetch multiple sessions together
- Reuse session objects: Avoid redundant fetches
- Profile cache access: Identify bottlenecks
- Monitor cache metrics: Track hit rates and latency
- Implement cache warming: Pre-load predictable data
Performance Monitoring
Set up comprehensive cache monitoring:Cache Maintenance & Operations
Proper cache maintenance ensures optimal performance, prevents disk space issues, and maintains data integrity.Monitoring Cache Size
Track cache growth and disk usage:Automatic Cleanup Configuration
Configure automatic cache cleanup to prevent unbounded growth:Manual Cleanup Strategies
Implement custom cleanup logic:Cache Integrity Verification
Verify cache integrity and detect corruption:Cache Backup & Restore
Backup and restore cache data:Scheduled Maintenance
Automate cache maintenance tasks:Cache in Production Environments
Deployingtif1 with caching in production requires careful consideration of architecture, scalability, and reliability.
Shared Cache Architecture
For multi-process applications, configure a shared cache:Docker Deployment
Configure caching for Docker containers:Kubernetes Deployment
Deploy with persistent cache in Kubernetes:Read-Only Cache
For read-only deployments (for example, serverless and immutable infrastructure):Cache Replication
Replicate cache across servers or regions:High-Availability Setup
Configure cache for high availability:Load Balancing
Distribute cache load across multiple instances:Monitoring & Alerting
Set up production monitoring:Performance Tuning for Production
Optimize cache for production workloads:Best Practices for Production
Deployment Architecture
Deployment Architecture
- Shared cache: Use shared storage for multi-process apps
- Persistent volumes: Mount cache on persistent storage
- Replication: Replicate cache across regions/zones
- Failover: Configure fallback cache locations
- Sharding: Distribute cache load across shards
- Read replicas: Use read-only caches for scaling reads
Resource Planning
Resource Planning
- Disk space: 5-10GB per season of data
- Memory: 2-4GB for memory cache + application
- CPU: Minimal (compression/decompression)
- Network: Bandwidth for initial cache warming
- IOPS: SSD recommended for SQLite cache
Monitoring & Alerting
Monitoring & Alerting
- Cache size: Alert when approaching limits
- Hit rate: Alert when below threshold (70%)
- Disk space: Alert when low (<10% free)
- Integrity: Alert on corruption
- Performance: Track access latency
- Errors: Monitor cache operation failures
Maintenance
Maintenance
- Daily: Verify integrity, clear old entries
- Weekly: Vacuum database, rebuild indexes
- Monthly: Full backup, cleanup old seasons
- Quarterly: Review and optimize configuration
- Yearly: Archive old data, plan capacity
Security
Security
- Permissions: Restrict cache directory access
- Encryption: Encrypt cache at rest (if needed)
- Network: Secure cache replication channels
- Audit: Log cache access for compliance
- Backup: Encrypt backups, secure storage
Troubleshooting Cache Issues
Common cache problems and their solutions.Cache Corruption
If cache errors or corrupted data occur:Cache Not Working
If the cache does not work as expected:Performance Issues
If the cache is slow:Memory Issues
If memory problems occur:Disk Space Issues
If the disk runs out of space:Connection Issues
If SQLite connection problems occur:Common Error Messages
'database is locked'
'database is locked'
- Increase lock timeout:
config.cache_lock_timeout = 60 - Enable WAL mode (should be default):
config.cache_wal_mode = True - Close other processes accessing cache
- Use separate cache directories for different processes
'Failed to decompress'
'Failed to decompress'
- Clear corrupted entry:
cache.clear_corrupted() - Verify cache integrity:
cache.verify_integrity() - Clear all cache:
cache.clear() - Restore from backup
'Invalid Parquet format'
'Invalid Parquet format'
- Clear corrupted entry
- Check schema version compatibility
- Update tif1 library
- Clear cache after library update
'Permission denied'
'Permission denied'
- Fix permissions on the platform-specific cache directory (for example,
chmod 700 ~/.cache/tif1on Linux) - Change cache directory:
config.cache_dir = "/writable/path" - Run with appropriate user permissions
'No space left on device'
'No space left on device'
- Clear old entries:
cache.clear_old(days=30) - Clear large entries:
cache.clear_largest(count=50) - Enable auto cleanup:
config.cache_auto_cleanup = True - Move cache to larger disk
Debug Mode
Enable debug logging for troubleshooting:Best Practices Summary
Follow these best practices for optimal cache performance and reliability.Configuration Best Practices
Keep Cache Enabled
Use Appropriate Cache Size
Enable Auto Cleanup
Use SSD Storage
Operational Best Practices
Warm Critical Data
Monitor Cache Health
Regular Maintenance
Handle Errors
Development Best Practices
Profile Cache Access
Batch Requests
Reuse Session Objects
Test Cache Behavior
Production Best Practices
Use Shared Cache
Persistent Storage
Backup Regularly
Monitor & Alert
Performance Optimization Checklist
Enable Memory Cache
Use SSD Storage
Tune SQLite Settings
Optimize Compression
Warm Cache Proactively
Monitor Hit Rate
Regular Maintenance
Profile & Optimize
Security Best Practices
File Permissions
File Permissions
- Owner: read/write/execute (rwx)
- Group: read/execute (r-x)
- Others: none (---)
- Command:
chmod 750 <cache-dir>
Encryption at Rest
Encryption at Rest
- Use an encrypted filesystem (LUKS, BitLocker)
- Or encrypt the cache database with SQLCipher
- Or use application-level encryption
Access Control
Access Control
- Use separate cache directories per user/application
- Set file ownership appropriately
- Use SELinux/AppArmor for additional isolation
Audit Logging
Audit Logging
- Enable cache debug logging
- Log cache operations to audit trail
- Monitor for suspicious access patterns
Conclusion
Thetif1 caching system is a production-grade solution. It maximizes performance and maintains data integrity and reliability. Correct configuration of the multi-layer cache architecture provides:
- 100-3000x performance improvement for cached data access
- Minimal network usage through the local cache tiers
- Predictable performance with cache warming
- Scalability through shared cache and replication
- Reliability through automatic corruption detection and cleanup
Key Takeaways
- Two-Layer Architecture: Memory cache (fast, volatile) + SQLite cache (persistent, shared)
- Automatic Management: Transparent cache operations, automatic cleanup, corruption detection
- High Performance: Sub-millisecond memory access, 20-100ms disk access, 60-80% compression
- Production-Ready: Shared cache, replication, monitoring, high availability
- Easy to Use: Works transparently, minimal configuration required
Getting Started
For most users, the default configuration works well:Next Steps
- Learn More: Read the Cache API Reference for detailed API documentation
- Optimize Performance: Follow the Best Practices Guide for optimization tips
- Deploy to Production: See Caching Strategy for production best practices
- Monitor Cache: Set up monitoring to track cache health
- Get Help: Join the Discord or the GitHub Discussions
Additional Resources
Cache API
Best Practices
Architecture
Configuration
Error Handling
Troubleshooting
tif1 cache system stores an entire F1 season in ~720 MB of compressed storage. The season covers 24 races, all sessions, and all data types. The uncompressed data occupies over 12 GB. Cached access to this data is immediate.