System Architecture Overview
The tif1 architecture follows three core principles:- Performance First: Every component is optimized for speed, from HTTP/2 multiplexing to orjson parsing to categorical data types.
- Resilience: Multi-tier caching, circuit breakers, and retry logic ensure reliability even under adverse network conditions.
- Transparency: Logging and monitoring show what happens at each stage.
High-Level Architecture Diagram
Component Responsibilities
Session Object (core.py)
- Entry point for all data access
- Manages lazy loading of laps, telemetry, weather, and race control data
- Coordinates between cache layers and CDN fetching
- Handles backend selection (pandas vs polars)
cache.py)
- In-memory cache using Python’s
functools.lru_cache - Stores fully constructed Python objects (DataFrames, model instances)
- Default capacity: 1024 items (configurable via
TIF1_CACHE_SIZEenv var) - Eviction policy: Least Recently Used (LRU)
- Lifetime: Process duration only
cache.py)
- Disk-based cache using SQLite database
- Location: OS-dependent tif1 cache directory (configurable via
TIF1_CACHE_DIR); see the platform defaults in the configuration reference - Stores compressed JSON representations
- Schema:
(key TEXT PRIMARY KEY, value BLOB, timestamp REAL) - Supports TTL-based expiration (default: 7 days)
- Thread-safe with connection pooling
cdn.py)
- Manages multiple CDN sources with automatic fallback
- Primary: jsDelivr CDN (
cdn.jsdelivr.net/gh/TracingInsights/{year}@main) - Fallback: Hugging Face buckets (
huggingface.co/buckets/tracinginsights/{year}/resolve) - Backup: StaticDelivr CDN (
cdn.staticdelivr.com/gh/TracingInsights/{year}/main) - Tracks failure counts per CDN source
- Automatically disables failing sources after 3 consecutive failures
- A 404 from one CDN falls through to the next, because mirrors can be stale
- tif1 raises
DataNotFoundErrorwhen every CDN returns a 4xx client error (404 included) - Requests for files missing everywhere never burn the retry/backoff budget
- 5xx and transport errors keep the retryable
NetworkError - Handles URL encoding and path construction
- Never uses
raw.githubusercontent.com(strict rate limits)
async_fetch.py)
- Parallel HTTP requests using
niquests(HTTP/2 support) - Connection pooling and keep-alive
- Automatic retry with exponential backoff
- Timeout management (default: 30s per request)
- Progress tracking for batch operations
retry.py)
- Prevents cascading failures during network issues
- States: CLOSED (normal), OPEN (failing), HALF_OPEN (testing recovery)
- Failure threshold: 5 consecutive failures
- Recovery timeout: 60 seconds
- Automatic state transitions
orjson)
- High-performance JSON parsing (2-3x faster than stdlib
json) - Direct bytes-to-Python object conversion
- Handles large payloads efficiently (100MB+ telemetry files)
- Strict validation mode enabled
validation.py)
- Pydantic-based validation of JSON structure
- Ensures data integrity before DataFrame construction
- Type coercion and default value handling
- Detailed error messages for debugging
core.py / core_utils)
- Converts validated JSON to pandas/polars DataFrames
- Column renaming (snake_case → PascalCase)
- Type inference and optimization
- Index management
core.py)
- Adds computed columns (LapTimeSeconds, IsPersonalBest, etc.)
- Merges weather data with lap data
- Calculates stint information
- Adds driver metadata
core_utils/helpers.py)
- Converts string columns to categoricals (50-90% memory reduction)
- Downcasts numeric types where safe (float64 → float32)
- Optimizes datetime representations
- Handles missing data efficiently
Complete Data Loading Pipeline
The data loading pipeline consists of eight distinct stages. Each stage has specific responsibilities and performance characteristics. These details help with code optimization and troubleshooting.Stage 1: Request Initiation
When code accesses data through a Session object, tif1 initiates the loading pipeline. This stage involves property access, lazy evaluation, and request routing.- Property access triggers
__getattribute__or explicit getter method - Session checks if data is already loaded (
self._laps is not None) - If not loaded, calls internal
_load_laps()method _load_laps()constructs cache key:f"laps_{year}_{gp}_{session_type}"- Passes control to cache layer
- Property access overhead: < 0.1ms
- Cache key construction: < 0.01ms
- No network I/O at this stage
Stage 2: Multi-Tier Cache Lookup
tif1 implements a two-tier caching system that reduces load times for frequently accessed data. This section explains the cache behavior needed for performance optimization.Tier 1: Memory LRU Cache (L1 Cache)
The memory cache is the fastest tier, storing fully constructed Python objects in RAM. Technical Specifications:- Implementation: Python
functools.lru_cachewith custom wrapper - Storage: In-process memory (heap)
- Data format: Native Python objects (DataFrames, model instances)
- Capacity: 1024 items (default), configurable via
TIF1_CACHE_SIZE - Eviction: Least Recently Used (LRU) algorithm
- Access time: < 1ms (typically 0.1-0.5ms)
- Thread safety: GIL-protected (safe for multi-threaded access)
- Persistence: None (cleared on process exit)
Tier 2: SQLite Persistent Cache (L2 Cache)
The SQLite cache provides persistent storage that survives process restarts. Technical Specifications:- Implementation: SQLite3 with custom connection pooling
- Storage: Disk-based database file
- Location: OS-dependent tif1 cache directory (configurable via
TIF1_CACHE_DIR); see the platform defaults in the configuration reference - Data format: Compressed JSON (zstd level 1; legacy zlib rows stay readable)
- Capacity: Unlimited (constrained by disk space)
- Access time: 10-50ms (depends on disk I/O)
- Thread safety: Connection pooling with thread-local storage
- Persistence: Permanent (until manually cleared or TTL expires)
Cache Lookup Flow
Cache Warming Strategies:
Stage 3: CDN Fetching with Fallback Strategy
When both cache tiers miss, tif1 fetches data from the CDN. The fetch uses a multi-source strategy with automatic fallback.CDN Architecture
Primary Source: jsDelivr CDN- URL Pattern:
https://cdn.jsdelivr.net/gh/TracingInsights/{year}@main/{path} - Global CDN with edge locations worldwide
- Automatic caching and compression
- No rate limits for reasonable usage
- HTTPS with HTTP/2 support
- Average latency: 50-200ms (depending on location)
- Uptime: 99.9%+
- URL Pattern:
https://huggingface.co/buckets/tracinginsights/{year}/resolve/{path} - Mirrors the TracingInsights GitHub data repos with the same layout (no branch segment)
- Pre-warmed CDN edge locations in the US and EU
- Used when jsDelivr fails or is unavailable
- URL Pattern:
https://cdn.staticdelivr.com/gh/TracingInsights/{year}/main/{path} - Global CDN with edge locations worldwide
- Automatic caching and compression
- No rate limits for reasonable usage
- HTTPS with HTTP/2 support
- Average latency: 50-200ms (depending on location)
- Used when both jsDelivr and Hugging Face fail or are unavailable
- Never used due to strict rate limits (10 requests/hour)
- Causes
NetworkErrorwhen all other sources fail
URL Construction
Fallback Logic
Circuit Breaker Pattern
tif1 implements a circuit breaker to prevent cascading failures during network issues. Circuit Breaker States:-
CLOSED (Normal Operation)
- All requests pass through
- Failures are counted
- Threshold: 5 consecutive failures
-
OPEN (Failing)
- Requests fail immediately, without a network call
- Prevents overload of the failing service
- Duration: 60 seconds
-
HALF_OPEN (Testing Recovery)
- Limited requests allowed through
- Success → transition to CLOSED
- Failure → transition back to OPEN
Retry Strategy
Request Timeout Management
Stage 4: Async Parallel Fetching
tif1 fetches data for multiple drivers in parallel with asyncio and HTTP/2. This parallel fetching is one of the most important tif1 performance optimizations.Sequential vs Parallel Fetching
Sequential Fetching (Traditional Approach):HTTP/2 Multiplexing
tif1 uses theniquests library. niquests supports HTTP/2 and multiplexes requests over a single TCP connection.
HTTP/1.1 Limitations:
- One request per TCP connection
- Multiple connections required for parallelism (typically 6-8 max)
- High overhead: TCP handshake + TLS handshake per connection
- Head-of-line blocking
- Multiple requests over single TCP connection
- Binary framing for efficiency
- Header compression (HPACK)
- Server push capability (not used by tif1)
- Stream prioritization
Connection Pooling
Progress Tracking
Error Handling in Parallel Fetching
Batch Size Optimization
Real-World Performance Example
Stage 5: High-Performance JSON Parsing
After the CDN fetch, tif1 parses JSON withorjson. This library parses 2-3x faster than the standard Python json module.
Why orjson?
Performance Comparison:
Key Features:
- Written in Rust for maximum performance
- Direct bytes-to-Python object conversion (no intermediate string)
- Efficient handling of large payloads (100MB+ telemetry files)
- Strict validation mode
- Native support for datetime, UUID, and other types
Parsing Pipeline
Data Structure Examples
Lap Data JSON Structure:Parsing Performance Optimization
Lazy Parsing for Large Files:Error Recovery
Validation After Parsing
Stage 6: DataFrame Construction and Transformation
After parsing and validating JSON, tif1 constructs DataFrames with optimized column names, types, and ordering.DataFrame Construction Pipeline
Column Naming Convention
tif1 uses PascalCase for all column names to maintain consistency with F1 terminology and improve readability. Rename Mapping:Type Optimization
Pandas Type Optimization:Column Ordering
tif1 orders columns logically for better readability:Index Management
Pandas Index Strategy:Missing Data Handling
DataFrame Validation
Performance Benchmarks
DataFrame Construction Performance:
Memory Usage:
Stage 7: Data Enrichment and Augmentation
After constructing the base DataFrame, tif1 automatically enriches data with computed columns, merged weather information, and derived metrics.Lap Data Enrichment
Computed Time Columns:Weather Data Integration
Telemetry Enrichment
Acceleration Calculation:Enrichment Performance
Enrichment Timing:
Memory Impact:
The memory increase is acceptable because enrichment adds analytical value.
Stage 8: Cache Storage and Finalization
The final stage saves processed data to both cache tiers and returns the DataFrame to the user.Cache Storage Strategy
Dual-Tier Write:Cache Metadata Tracking
Cache Eviction Policies
LRU Eviction (Memory Cache):Cache Statistics and Monitoring
Final Data Return
Complete Pipeline Timing
End-to-End Performance (Cold Start):
End-to-End Performance (Warm Start - SQLite):
End-to-End Performance (Hot Start - Memory):
Speedup Summary:
- Warm vs Cold: 93x faster (31ms vs 2868ms)
- Hot vs Cold: 2868x faster (< 1ms vs 2868ms)
- Hot vs Warm: 31x faster (< 1ms vs 31ms)
Data Transformation Through the Pipeline
This section follows data through each transformation stage. The details help with debugging and performance optimization.Stage-by-Stage Data Evolution
Stage 1: Raw JSON (from CDN)
- Format: UTF-8 encoded JSON
- Size: ~2-5 KB per driver (compressed), ~10-20 KB (uncompressed)
- Naming: snake_case
- Types: Mixed (strings, numbers, booleans, nulls)
Stage 2: Python Dictionary (after orjson parsing)
- Format: Native Python dict
- Size: ~3x JSON size in memory (~30-60 KB)
- Types: Python native (int, float, str, bool, None)
- Access: O(1) dictionary lookups
Stage 3: Initial DataFrame (after construction)
- Format: pandas DataFrame
- Size: ~15 MB for 1500 laps (before optimization)
- Column names: snake_case
- Types: Default pandas types (int64, float64, object)
Stage 4: Renamed DataFrame (after column renaming)
- Format: pandas DataFrame
- Size: Same as Stage 3
- Column names: PascalCase (tif1 convention)
- Types: Still default types
Stage 5: Optimized DataFrame (after type optimization)
- Before: 15 MB
- After: 4 MB
- Reduction: 73%
Stage 6: Enriched DataFrame (after enrichment)
- Format: pandas DataFrame
- Size: ~7 MB (75% increase from Stage 5)
- Columns: Original + ~20 computed columns
- Ready for analysis
Stage 7: Final DataFrame (cached and returned)
- Format: pandas/polars DataFrame
- Size: ~7 MB (in memory)
- Cached: Yes (both memory and SQLite)
- Ready: For immediate analysis
Data Type Comparison: Pandas vs Polars
Pandas Types:- Polars uses more efficient internal representation
- Polars strings are always UTF-8 validated
- Polars categoricals use dictionary encoding by default
- Polars has better null handling (no NaN vs None confusion)
Memory Usage Comparison
Full Pipeline Memory Usage (1500 laps):
Polars consistently uses ~50% less memory than pandas for the same data.
Advanced Performance Optimizations
tif1 implements many performance optimizations throughout the data pipeline. This section explains them for faster code and better architectural decisions.1. HTTP/2 Multiplexing and Connection Reuse
HTTP Protocol Evolution
HTTP/1.0 (Legacy):- One request per TCP connection
- Connection closed after each request
- High overhead: TCP handshake (3-way) + TLS handshake (2-3 round trips)
- Total overhead: ~200-300ms per request
- Connection keep-alive (reuse connection)
- Pipelining (limited browser support)
- Head-of-line blocking (requests must complete in order)
- Typical browser limit: 6-8 concurrent connections per domain
- Binary framing protocol (vs text-based HTTP/1.1)
- Multiplexing: Multiple requests over single connection
- Header compression (HPACK algorithm)
- Server push (not used by tif1)
- Stream prioritization
- No head-of-line blocking at HTTP layer
Performance Impact
Implementation in tif1
2. Lazy Loading and On-Demand Data Fetching
Lazy loading fetches data only when the code needs it. This reduces unnecessary network I/O and memory usage.Implementation
Performance Benefits
3. Categorical Data Type Optimization
Converting string columns to categoricals gives large memory savings and faster operations.Memory Comparison
Performance Comparison
Automatic Categorization in tif1
4. Backend Selection: Pandas vs Polars
Select the backend that matches the workload to maximize performance.Performance Benchmarks
Operation Speed (1500 laps):
Memory Usage (1500 laps):
When to Use Each Backend
Use Pandas When:- Compatibility with existing pandas code is required
- A library requires pandas (matplotlib, seaborn, and others)
- Mutable DataFrames are required (in-place operations)
- Dataset is small (<10k rows)
- The complete pandas ecosystem is required
- Performance is critical
- The datasets are large (>100k rows)
- Memory is constrained
- Lazy evaluation is required
- Type safety and better error messages are preferred
- The project is new
Switching Backends
5. Async Parallel Fetching
Async fetching is one of the most important tif1 performance optimizations.Sequential vs Parallel Comparison
Concurrency Control
6. JSON Parsing Optimization
orjson provides 2-3x faster JSON parsing than stdlib json.Benchmark Comparison
7. Cache Optimization Strategies
Pre-warming Cache
Cache Size Tuning
8. Batch Operations
Process multiple items together for better performance.Performance Summary
Key Optimizations and Their Impact:
Combined Impact:
- Cold start: ~3s
- Warm start: ~30ms (100x faster)
- Hot start: <1ms (3000x faster)
- Memory usage: 50-70% reduction vs naive implementation
Comprehensive Error Handling
tif1 implements an error handling system with a hierarchical exception structure, detailed error context, and recovery strategies.Exception Hierarchy
Network Errors
Network errors occur during CDN fetching and HTTP operations.NetworkError (Base)
ConnectionError
TimeoutError
CDNError
Data Not Found Errors
These errors occur when the requested data does not exist.DataNotFoundError (Base)
DriverNotFoundError
LapNotFoundError
Invalid Data Errors
These errors occur during data parsing and validation.InvalidDataError (Base)
JSONParseError
ValidationError
Cache Errors
These errors occur during cache operations.CacheError (Base)
CacheCorruptionError
Error Recovery Strategies
Automatic Retry with Exponential Backoff
Fallback to Alternative Data Source
Graceful Degradation
Error Context and Debugging
All tif1 exceptions include detailed context for debugging.Monitoring and Observability
tif1 provides monitoring capabilities for understanding system behavior, diagnosing issues, and optimizing performance.Logging System
Log Levels and Configuration
What Gets Logged
DEBUG Level:Performance Monitoring
Timing Decorators
Performance Metrics Collection
Cache Monitoring
Cache Statistics
Cache Performance Testing
Circuit Breaker Monitoring
Network Monitoring
Request Tracking
Memory Monitoring
Comprehensive Monitoring Dashboard
Data Flow Patterns and Scenarios
Common data flow patterns help with code optimization for different use cases.Pattern 1: Cold Start (First Load)
Scenario: First time loading data, no cache available.- Pre-warm cache during application startup
- Use async loading for non-blocking operation
- Consider loading only required data (laps vs telemetry)
Pattern 2: Warm Start (SQLite Cache Hit)
Scenario: Data exists in SQLite cache, but not in memory.- Keep SQLite cache on SSD for faster access
- Increase cache TTL to reduce re-fetching
- Monitor cache hit rate
Pattern 3: Hot Start (Memory Cache Hit)
Scenario: Data exists in memory cache.- Reuse session objects to maximize memory cache hits
- Increase the memory cache size when RAM is available
- Keep frequently accessed data in memory
Pattern 4: Partial Cache Hit
Scenario: Some drivers cached, others need fetching.- 15 drivers from memory: < 1ms
- 3 drivers from SQLite: ~90ms
- 2 drivers from CDN: ~700ms
- Batch fetch missing drivers
- Pre-warm cache for commonly accessed drivers
- Use selective loading (only load needed drivers)
Pattern 5: Network Failure with Retry
Scenario: Network request fails, automatic retry with backoff.- Try 1: 30s (timeout)
- Backoff 1: 1s
- Try 2: 500ms (fast fail)
- Backoff 2: 2s
- Try 3: 500ms (success)
- Processing: 370ms
- Reduce timeout for faster failure detection
- Implement circuit breaker to fail fast
- Use fallback data sources
Pattern 6: Batch Loading Multiple Sessions
Scenario: Load data for multiple sessions efficiently.Pattern 7: Incremental Data Loading
Scenario: Load data incrementally as needed.Pattern 8: Cache Warming Strategy
Scenario: Pre-warm cache for better user experience.Advanced Optimization Strategies
These strategies achieve maximum performance in production environments.Strategy 1: Intelligent Cache Pre-warming
Pre-warm cache strategically based on usage patterns.Strategy 2: Adaptive Timeout Management
Adjust timeouts based on network conditions.Strategy 3: Selective Data Loading
Load only the required data.Strategy 4: Batch Operations for Multiple Analyses
Batch operations to minimize overhead.Strategy 5: Memory-Efficient Iteration
Process large datasets without loading everything into memory.Strategy 6: Polars for Large-Scale Analysis
Use Polars for better performance on large datasets.Strategy 7: Connection Pooling Optimization
Optimize HTTP connection pooling for the workload.Strategy 8: Lazy Evaluation with Polars
Use Polars lazy evaluation for complex pipelines.Strategy 9: Compression for Cache Storage
Optimize cache storage with compression.Strategy 10: Monitoring-Driven Optimization
Use monitoring data to identify bottlenecks.Comprehensive Summary
The tif1 data flow architecture targets maximum performance, reliability, and developer experience. This section summarizes the key concepts and gives actionable recommendations.Architecture Principles
1. Performance First- Every component optimized for speed
- Multi-tier caching reduces latency by 2800x
- HTTP/2 multiplexing enables parallel fetching
- orjson provides 3x faster JSON parsing
- Categorical types reduce memory by 90%
- Polars backend offers 4x faster operations
- Circuit breaker prevents cascading failures
- Automatic retry with exponential backoff
- Multi-source CDN fallback (jsDelivr → GitHub)
- Comprehensive error handling with detailed context
- Graceful degradation for non-critical data
- Lazy loading minimizes unnecessary work
- Intuitive API with sensible defaults
- Detailed logging and monitoring capabilities
- Detailed error messages with recovery suggestions
- Flexible backend selection (pandas/polars)
Performance Characteristics
Load Time Comparison:
Memory Usage:
Backend Comparison:
Key Optimizations
1. Multi-Tier Caching- L1 (Memory): < 1ms access, 1024 items
- L2 (SQLite): 30ms access, unlimited capacity
- Automatic promotion from L2 to L1
- TTL-based expiration (7 days default)
- HTTP/2 multiplexing over single connection
- 20 drivers fetched in 500ms (vs 10s sequential)
- 17x speedup for multi-driver operations
- Automatic error handling and retry
- Categorical encoding for string columns
- Downcast numeric types (float64 → float32)
- Boolean optimization (int64 → bool)
- 73% memory reduction on average
- Automatic computed columns (20+ fields)
- Weather data integration
- Performance flags and metrics
- Minimal overhead (130ms for 1500 laps)
Best Practices
For Maximum Performance:Common Pitfalls and Solutions
Pitfall 1: Creating New Sessions RepeatedlyPerformance Tuning Checklist
Before Deployment:- Enable appropriate logging level (INFO for production)
- Configure cache size based on available memory
- Set cache TTL based on data freshness requirements
- Pre-warm cache for frequently accessed data
- Choose appropriate backend (pandas vs polars)
- Configure timeouts based on network conditions
- Set up monitoring and alerting
- Test error handling and recovery
- Monitor cache hit rate (target: > 80%)
- Monitor network success rate (target: > 95%)
- Monitor memory usage (should be stable)
- Monitor circuit breaker state (should be CLOSED)
- Check for slow queries (> 5s)
- Review error logs regularly
- Clear expired cache entries periodically
- Update cache warming strategy based on usage
- Profile code to identify bottlenecks
- Increase cache size if hit rate is low
- Use Polars for large datasets
- Batch operations where possible
- Use async loading for non-blocking operations
- Optimize network timeouts
- Consider CDN proximity
- Review and optimize data loading patterns
Future Enhancements
The tif1 data flow architecture is designed to evolve. Planned enhancements include: Short Term:- Streaming data support for live sessions
- GraphQL API for selective field loading
- Redis cache tier for distributed systems
- Compression algorithm selection (zstd, lz4)
- Automatic cache warming based on ML predictions
- Edge caching with CloudFlare Workers
- WebSocket support for real-time updates
- Distributed cache with automatic sharding
- Query result caching with automatic invalidation
- Advanced prefetching based on access patterns
Conclusion
The tif1 data flow architecture takes a complete approach to high-performance data loading and processing. Combine the eight-stage pipeline, multi-tier caching, async parallel fetching, and these best practices to get:- 93-2800x faster data access through caching
- 17x faster multi-driver operations through parallelization
- 50-73% less memory usage through type optimization
- 4x faster operations through Polars backend
- 99%+ reliability through error handling and circuit breakers
Related Documentation
Caching Strategy
The multi-tier caching system.
Backends
Pandas vs Polars comparison and selection guide.
Sessions
Session management and data loading.
HTTP API
HTTP client and network operations.
Utilities
Helper functions and utilities.
CLI
Command-line interface for data management.
Additional Resources
- GitHub Repository: TracingInsights/tif1
- Issue Tracker: Report bugs and request features
- Discussions: Community discussions and Q&A
- Examples: Code examples and tutorials