Overview
tif1 implements a sophisticated two-tier caching architecture designed to maximize performance while minimizing redundant network requests. The caching system consists of:
- In-Memory LRU Cache: Fast, volatile cache for frequently accessed data with configurable size limits
- SQLite Persistent Cache: Durable disk-based storage that survives across sessions
Cache Architecture
The cache stores two primary types of data:- JSON Data: Session metadata, lap data, driver information, weather data, and race control messages stored in a key-value table
- Telemetry Data: High-frequency sensor data (speed, throttle, brake, gear, RPM, DRS) stored in a dedicated optimized table with composite indexing on (year, gp, session, driver, lap)
Session.load() and can be manually managed through the Cache API.
Getting the Cache Instance
get_cache()
Cache instance used throughout the library. This is the primary entry point for all cache operations.
The cache is initialized lazily on first access and configured based on environment variables and .tif1rc settings. The same instance is shared across all sessions and operations within your application.
Returns: Cache - The global cache singleton
Thread Safety: The cache instance is thread-safe and can be safely accessed from multiple threads.
Example:
TIF1_CACHE_DIR: Custom cache directory (default:~/.tif1/cache)TIF1_CACHE_ENABLED: Set to"false"to disable caching entirelyTIF1_CACHE_READ_ONLY: Set to"true"to prevent writing new cache entries
Cache Management Operations
clear()
- Clearing corrupted cache data
- Freeing disk space
- Forcing a complete data refresh
- Troubleshooting data inconsistencies
has_session_data()
year(int): Season year (e.g.,2024,2023)gp(str): Grand Prix identifier in snake_case format (e.g.,"Monaco_Grand_Prix","Belgian_Grand_Prix")session(str): Session type - one of"Race","Qualifying","Sprint","Practice_1","Practice_2","Practice_3","Sprint_Qualifying"
bool - True if any JSON or telemetry data exists for the session, False otherwise
Use Cases:
- Checking cache availability before loading
- Building cache status dashboards
- Implementing cache warming strategies
- Conditional data loading logic
close()
atexit handler, but can be invoked manually for fine-grained resource management.
When to Use:
- Long-running applications that need to release resources
- Testing scenarios requiring clean cache state
- Before forking processes (to avoid connection sharing issues)
- Explicit resource cleanup in context managers
close(), the cache can still be used - it will automatically reconnect to the database on the next operation.
Example:
General Cache Access (JSON Data)
These methods provide low-level access to the cache’s key-value store for JSON-serializable data. Most users won’t need these methods directly, asSession.load() handles caching automatically. However, they’re useful for advanced use cases like custom data pipelines or cache inspection.
get()
key(str): Cache key in the format"{year}/{gp}/{session}/{file}.json"wherefileis one of:drivers.json- Driver list and metadatalaps.json- Lap timing dataweather.json- Weather conditionsmessages.json- Race control messagessession_info.json- Session metadata
Any | None - The cached data (typically a dict or list), or None if not found
Example:
set()
key(str): Cache key in the format"{year}/{gp}/{session}/{file}.json"data(Any): JSON-serializable data to cache (dict, list, str, int, float, bool, None)
orjson for performance. Ensure your data contains only JSON-compatible types.
Example:
Telemetry Cache Access
Telemetry data (high-frequency sensor readings) is stored in a dedicated SQLite table optimized for lap-by-lap queries. This separation improves performance for telemetry-heavy workloads and enables efficient batch operations.Telemetry Data Structure
Each telemetry entry contains time-series data for a single lap:- Time - Elapsed time in seconds from session start
- Speed - Vehicle speed in km/h
- RPM - Engine revolutions per minute
- Gear - Current gear (0-8, where 0 is neutral)
- Throttle - Throttle position (0-100%)
- Brake - Brake pressure (0-100%)
- DRS - DRS status (0=closed, 1=open, 2-14=various states)
- Distance - Distance traveled in meters
- X, Y, Z - 3D position coordinates
get_telemetry()
None if the telemetry is not cached.
Parameters:
year(int): Season year (e.g.,2024)gp(str): Grand Prix identifier (e.g.,"Monaco_Grand_Prix")session(str): Session type (e.g.,"Race","Qualifying")driver(str): Three-letter driver code (e.g.,"VER","HAM","LEC")lap(int): Lap number (1-indexed)
Any | None - Telemetry data structure (typically a dict with arrays), or None if not found
Performance: Single telemetry lookup is optimized with a composite index on (year, gp, session, driver, lap).
Example:
set_telemetry()
year(int): Season yeargp(str): Grand Prix identifiersession(str): Session typedriver(str): Three-letter driver codelap(int): Lap numberdata(Any): Telemetry data structure to cache
orjson before storage.
Example:
get_telemetry_batch()
get_telemetry() multiple times, as it uses a single SQL query with an IN clause.
Parameters:
year(int): Season yeargp(str): Grand Prix identifiersession(str): Session typedriver_laps(list[tuple[str, int]]): List of (driver_code, lap_number) tuples to fetch
dict[tuple[str, int], Any] - Dictionary mapping (driver, lap) tuples to telemetry data. Missing entries are not included in the result.
Performance Benefits:
- Single database query instead of N queries
- Reduced Python-SQLite round trips
- Optimized for bulk telemetry analysis
- Ideal for comparing multiple drivers/laps
Async Methods
All cache read/write operations have async variants for use in async contexts. These methods are essential for building high-performance async applications that need to interact with the cache without blocking the event loop.Async Method Overview
| Sync Method | Async Method | Description |
|---|---|---|
get(key) | get_async(key) | Retrieve JSON data asynchronously |
set(key, data) | set_async(key, data) | Store JSON data asynchronously |
get_telemetry(...) | get_telemetry_async(...) | Retrieve single telemetry entry asynchronously |
set_telemetry(...) | set_telemetry_async(...) | Store single telemetry entry asynchronously |
get_telemetry_batch(...) | get_telemetry_batch_async(...) | Retrieve multiple telemetry entries asynchronously |
Implementation Details
Async methods useasyncio.to_thread() to execute synchronous SQLite operations in a thread pool, preventing event loop blocking. This approach maintains thread safety while providing async compatibility.
get_async()
set_async()
get_telemetry_async()
set_telemetry_async()
get_telemetry_batch_async()
Cache Instance Attributes
TheCache instance exposes several read-only attributes for inspecting cache configuration and state.
cache_dir
~/.tif1/cache but can be customized via the TIF1_CACHE_DIR environment variable or .tif1rc configuration.
Example:
db_path
cache.sqlite). This file contains all persistent cache data.
Use Cases:
- Backing up the cache database
- Checking database file size
- Manually inspecting cache with SQLite tools
read_only
True, the cache will serve existing data but will not write new entries to disk.
Use Cases:
- Running in environments with read-only filesystems
- Preventing cache pollution during testing
- Analyzing existing cache without modifications
TIF1_CACHE_READ_ONLY=true environment variable or in .tif1rc.
Example:
Cache Statistics and Monitoring
While theCache class doesn’t expose built-in statistics methods, you can query the SQLite database directly for monitoring purposes.
Example - Cache Size Analysis
Example - Session Coverage Report
Performance Considerations
Cache Hit Rates
The cache is most effective when:- Loading the same session multiple times
- Analyzing historical data that doesn’t change
- Working with telemetry-heavy workloads
- Running batch analyses across multiple sessions
- Memory cache hit: ~1-10 microseconds
- SQLite cache hit: ~1-10 milliseconds
- CDN fetch (cache miss): ~500-2000 milliseconds
Memory Management
The in-memory LRU cache has a default size limit to prevent excessive memory usage. When the limit is reached, least-recently-used entries are evicted (but remain in SQLite). Memory Usage Estimates:- Session metadata: ~10-50 KB per session
- Lap data: ~50-200 KB per session
- Telemetry data: ~500 KB - 5 MB per session (depending on lap count)
Disk Space
The SQLite database grows as more data is cached. Typical sizes:- Single session (no telemetry): ~100-500 KB
- Single session (with telemetry): ~5-50 MB
- Full season (all sessions, all telemetry): ~5-20 GB
Optimization Tips
- Use Batch Operations:
get_telemetry_batch()is significantly faster than multipleget_telemetry()calls - Selective Loading: Only load the data you need (e.g.,
session.load(laps=True, telemetry=False)) - Async for Concurrency: Use async methods when fetching data for multiple sessions in parallel
- Cache Warming: Pre-load frequently accessed sessions during off-peak hours
- Read-Only Mode: Use read-only mode in production environments to prevent cache pollution
Common Use Cases
Use Case 1: Cache Status Dashboard
Use Case 2: Selective Cache Clearing
Use Case 3: Cache Export/Import
Use Case 4: Cache Validation
Caching Strategy
Cache architecture
Config
Cache configuration
Performance
Optimization
Troubleshooting
Cache Not Working
Symptoms: Data is fetched from CDN every time, even for previously loaded sessions. Solutions:-
Check if caching is enabled:
-
Verify cache directory is writable:
-
Check environment variables:
Database Locked Errors
Symptoms:sqlite3.OperationalError: database is locked
Causes:
- Multiple processes accessing the cache simultaneously
- Long-running transactions
- Improper connection handling
-
Ensure proper connection cleanup:
- Avoid concurrent writes from multiple processes
- Use read-only mode for read-heavy workloads
Cache Corruption
Symptoms: Errors when reading cached data, invalid JSON, or database integrity errors. Solutions:-
Clear and rebuild the cache:
-
Delete the database file manually:
-
Verify database integrity:
High Memory Usage
Symptoms: Python process consuming excessive RAM. Causes:- Large in-memory LRU cache
- Loading many sessions with telemetry
- Reduce memory cache size (requires code modification)
-
Load data selectively:
-
Process data in batches:
Slow Cache Performance
Symptoms: Cache reads are slower than expected. Solutions:-
Use batch operations for telemetry:
-
Optimize database (vacuum):
-
Check disk I/O performance:
Related Pages
Caching Strategy
Understand the two-tier cache architecture
Config API
Configure cache behavior and location
Performance Guide
Optimization techniques and best practices
Core API
Learn how sessions interact with the cache