Mirror Service
Technical documentation for the mod file caching and mirroring service
Mirror Service
The mirror service is a dedicated caching layer for mod file downloads that improves performance, reduces load on external APIs, and provides bandwidth savings through intelligent file caching.
Service Overview
Purpose
The mirror service acts as an intermediary between clients and the GameBanana API, caching frequently accessed mod files in S3 storage. This provides:
- Faster Downloads: Cached files are served directly from S3 with lower latency
- Reduced API Load: Minimizes requests to GameBanana API
- Bandwidth Savings: Tracks and reports bandwidth saved through caching
- High Availability: Files remain accessible even if upstream sources are temporarily unavailable
Technology Stack
- Runtime: Bun (high-performance JavaScript runtime)
- Framework: Hono (lightweight web framework)
- Storage: S3-compatible object storage
- Cache: Redis (for metrics and metadata)
- Database: PostgreSQL (for file metadata tracking)
- Background Jobs: Cron-based workers for validation and cleanup
Architecture
High-Level Architecture
Component Architecture
Data Flow
Cache Hit Flow
When a file already exists in the cache:
Cache Miss Flow
When a file needs to be downloaded and cached:
Validation Worker Flow
Background worker that validates cached files:
Cleanup Worker Flow
Background worker that removes unused files:
Core Components
MirrorService
The main service orchestrator that handles file mirroring logic.
Responsibilities:
- Check if file exists in cache and is not stale
- Stream cached files from S3
- Fetch files from GameBanana on cache miss
- Upload files to S3 while streaming to client
- Track cache hits and misses
- Create mirrored file metadata entries
Key Methods:
mirrorFile(modId: string, fileId: string)- Main entry point for file downloads
// Simplified flow
async mirrorFile(modId: string, fileId: string) {
// Check cache
const cached = await this.mirroredFileRepository.findByModIdAndFileId(modId, fileId);
if (cached.isOk() && !cached.value.isStale) {
// Verify S3 file exists
if (await S3Service.instance.fileExists(cached.value.s3Key)) {
// Cache hit - stream from S3
await MetricsService.instance.incrementCacheHit();
return S3Service.instance.downloadFileStream(cached.value.s3Key);
}
}
// Cache miss - fetch from upstream
await MetricsService.instance.incrementCacheMiss();
const file = await fetch(gamebananaUrl);
// Upload to S3 and stream to client simultaneously
const { outputStream, uploadPromise } =
S3Service.instance.uploadAndStreamThrough(key, file.body, onFileUploaded);
return outputStream;
}S3Service
Handles all S3 storage operations using Bun's built-in S3Client.
Responsibilities:
- Upload files to S3
- Download files from S3
- Check file existence
- Delete files from S3
- Stream-through uploads (upload while streaming to client)
Key Methods:
uploadFileStream(key, stream)- Upload a file streamdownloadFileStream(key)- Download a file as a streamuploadAndStreamThrough(key, stream, onSuccess)- Upload and stream simultaneouslyfileExists(key)- Check if file exists in S3deleteFile(key)- Remove file from S3
Stream-Through Pattern:
The service uses stream tee-ing to upload to S3 and stream to the client simultaneously:
uploadAndStreamThrough(key: string, sourceStream: ReadableStream, onSuccess: (hash: string) => void) {
// Split stream into two
const [streamForS3, streamForResponse] = sourceStream.tee();
// Upload to S3 in background
const uploadPromise = uploadToS3(streamForS3);
// Return stream for immediate client response
return {
outputStream: streamForResponse,
uploadPromise
};
}MetricsService
Tracks cache performance and download statistics using Redis.
Responsibilities:
- Track cache hits and misses
- Track download counts per file
- Calculate cache hit rate
- Calculate bandwidth savings
- Provide top downloads statistics
Key Methods:
incrementCacheHit()- Increment cache hit counterincrementCacheMiss()- Increment cache miss counterincrementDownload(fileId)- Track download for a specific filegetCacheHitRate()- Calculate percentage of cache hitscalculateBandwidthSaved()- Estimate bandwidth saved by cachinggetTopDownloads(limit)- Get most downloaded filesgetMetrics()- Get comprehensive metrics report
Redis Keys:
mirror:metrics:cache_hits- Total cache hitsmirror:metrics:cache_misses- Total cache missesmirror:metrics:downloads:{fileId}- Downloads per file
ValidationProcessor
Background worker that validates cached files against upstream sources.
Responsibilities:
- Check if mod downloads still exist in database
- Validate file sizes match upstream
- Mark stale files for re-download
- Delete orphaned files
- Update validation timestamps
Configuration:
- Runs every N hours (configurable via
VALIDATION_WORKER_INTERVAL_HOURS) - Default: Every hour
Process:
- Fetch all mirrored files with their mod download relationships
- For each file:
- Check if mod download still exists
- Compare file sizes
- Delete files if mod download deleted
- Mark as stale if size mismatch
- Update last_validated timestamp if valid
CleanupProcessor
Background worker that removes unused files based on retention policy.
Responsibilities:
- Find files not downloaded within retention period
- Delete files from S3
- Remove database entries
- Track storage freed
Configuration:
- Runs daily at 2 AM
- Retention period: N days (configurable via
CLEANUP_RETENTION_DAYS) - Default: 14 days
Process:
- Query files where
last_downloaded_at < NOW() - N days - For each file:
- Delete from S3
- Delete from database
- Track storage freed
API Endpoints
Download Endpoint
GET /download/:modId/:fileIdDownloads a mod file with automatic caching.
Parameters:
modId(string) - The mod identifierfileId(string) - The file download identifier
Response:
200 OK- File stream with appropriate headers404 Not Found- File not found in database500 Internal Server Error- Server error
Response Headers:
Content-Type: application/octet-streamContent-Disposition: attachment; filename="..."Content-Length: {size}
Example:
curl https://mirror.deadlockmods.app/download/abc123/xyz789 -o mod.zipHealth Check Endpoints
GET /
GET /healthReturns service health status.
Response:
{
"status": "healthy",
"service": "mirror-service",
"version": "1.0.0",
"uptime": 3600,
"timestamp": "2024-01-01T12:00:00.000Z"
}Metrics Endpoint
GET /metricsReturns comprehensive service metrics.
Response:
{
"cacheHitRate": 85.5,
"totalStorageUsed": 1073741824,
"totalFiles": 150,
"bandwidthSaved": 5368709120,
"topDownloads": [
{
"fileId": "file_123",
"filename": "popular-mod.zip",
"downloads": 500
}
],
"timestamp": "2024-01-01T12:00:00.000Z"
}Integration with Main API
The mirror service is integrated with the main API service through a feature flag system.
Feature Flag
The mod-download-mirroring feature flag controls whether the API returns mirror URLs or direct GameBanana URLs.
// In API service
const isModDownloadMirroringEnabled =
await featureFlagsService.isFeatureEnabled("mod-download-mirroring", userId);
const downloads = files.map((download) => ({
...download,
url: isModDownloadMirroringEnabled.unwrapOr(false)
? `${env.MIRROR_SERVICE_URL}/download/${modId}/${download.id}`
: download.url, // Direct GameBanana URL
}));URL Generation
When mirroring is enabled:
- Format:
https://mirror.deadlockmods.app/download/{modId}/{fileId} - Example:
https://mirror.deadlockmods.app/download/abc123/xyz789
When mirroring is disabled:
- Format: Direct GameBanana URL
- Example:
https://files.gamebanana.com/mods/abc123.zip
Graceful Degradation
If the mirror service is unavailable:
- Clients receive 404 or 500 errors from mirror service
- Clients can fall back to direct GameBanana URLs
- Feature flag can be disabled to bypass mirror service
Configuration
Environment Variables
# Server Configuration
PORT=3002
NODE_ENV=production
TZ=Europe/Berlin
# Database
DATABASE_URL=postgresql://user:pass@localhost:5432/deadlock
# S3 Storage
S3_ACCESS_KEY_ID=your_access_key
S3_SECRET_ACCESS_KEY=your_secret_key
S3_BUCKET=deadlock-mod-manager-mirror
S3_ENDPOINT=https://s3.amazonaws.com
# Redis
REDIS_URL=redis://localhost:6379
# Background Workers
VALIDATION_WORKER_INTERVAL_HOURS=1 # Validate every N hours
CLEANUP_RETENTION_DAYS=14 # Keep files for N days
# CORS
CORS_ORIGIN=["https://deadlockmods.com","https://deadlockmods.app"]
# Monitoring
SENTRY_DSN=https://...
POD_NAME=mirror-service-1 # For multi-pod deploymentsWorker Configuration
Validation Worker:
- Interval: 1-24 hours (configurable)
- Default: 1 hour
- Purpose: Detect stale files and maintain cache integrity
Cleanup Worker:
- Schedule: Daily at 2 AM (CronPatterns.MAINTENANCE_DAILY)
- Retention: 1-365 days (configurable)
- Default: 14 days
- Purpose: Remove unused files to save storage costs
Database Schema
The mirror service uses the mirrored_files table:
CREATE TABLE mirrored_files (
id TEXT PRIMARY KEY,
mod_id TEXT NOT NULL,
mod_download_id TEXT NOT NULL,
remote_id TEXT NOT NULL,
filename TEXT NOT NULL,
s3_key TEXT NOT NULL,
s3_bucket TEXT NOT NULL,
file_hash TEXT NOT NULL,
file_size INTEGER NOT NULL,
mirrored_at TIMESTAMP NOT NULL,
last_downloaded_at TIMESTAMP NOT NULL,
last_validated TIMESTAMP NOT NULL,
is_stale BOOLEAN NOT NULL DEFAULT false,
FOREIGN KEY (mod_download_id) REFERENCES mod_downloads(id)
);Key Fields:
is_stale- Marks files that need re-download (size mismatch)last_downloaded_at- Used for cleanup retention policylast_validated- Tracks validation worker progressfile_hash- SHA-256 hash for integrity verification
Performance Considerations
The mirror service is designed for high throughput with: - Stream-based processing (no file buffering) - S3 multipart uploads (5 MB chunks) - Retry logic for S3 operations - Connection pooling for database queries
Architecture Overview
See how mirror service fits into the overall system
API Reference
Complete API documentation for all services
Development Setup
Set up your development environment
Production Deployment
When deploying the mirror service: - Ensure S3 credentials have appropriate permissions (read/write/delete) - Configure Redis for persistence if metrics are critical - Set up monitoring for cache hit rates and storage usage - Consider S3 lifecycle policies for additional cost optimization - Monitor background worker execution and error rates