Tracker Report Fetcher
A distributed service for fetching Apple FindMy tracker reports and storing them in a PostgreSQL database.
Overview
The Tracker Report Fetcher is designed to periodically fetch location reports for Apple FindMy trackers using the anisette service. It uses Redis for distributed queue management, ensuring that:
- All trackers are requested at least once every 6 hours
- Requests are staggered throughout the day
- Multiple containers can work simultaneously without conflicts
- Trackers that don't report are handled gracefully
Architecture
- Distributed Queue: Uses Redis sorted sets and hashes to manage the processing queue
- Worker Coordination: Each container has a unique ID and claims work atomically
- Fault Tolerance: Includes heartbeat mechanism and stalled work detection
- Backoff Strategy: Implements exponential backoff for consistently failing trackers
Configuration
The service can be configured using environment variables or command-line arguments:
| Environment Variable | Description | Default |
|---|---|---|
ANISETTE_SERVER | URL of the Anisette server | (Required) |
DB_HOST | PostgreSQL database host | localhost |
DB_PORT | PostgreSQL database port | 5432 |
DB_NAME | PostgreSQL database name | postgres |
DB_USER | PostgreSQL database user | postgres |
DB_PASSWORD | PostgreSQL database password | postgres |
REDIS_HOST | Redis host | localhost |
REDIS_PORT | Redis port | 6379 |
REDIS_USERNAME | Redis username | (empty) |
REDIS_PASSWORD | Redis password | (empty) |
REDIS_SSL | Enable SSL/TLS for Redis connection | false |
REDIS_DB | Redis database number | 0 |
FETCH_INTERVAL | Seconds between fetch cycles | 300 |
MAX_KEYS_PER_BATCH | Maximum trackers to process per batch | 64 |
MAX_REPORT_AGE_DAYS | Maximum age of reports to fetch | 7 |
REQUEST_INTERVAL_HOURS | Hours between tracker report requests | 6 |
FETCHER_REPLICAS | Number of container replicas to run | 2 |
Running with Docker Compose
Quick Setup
A setup script is provided to quickly set up the environment:
cd fetcher
chmod +x setup.sh
./setup.sh
This script will:
- Create the data directory for the Docker volume
- Create a
.envfile from the example template if it doesn't exist - Make the Python scripts executable
Manual Setup
- Create a data directory for the Docker volume:
mkdir -p data - Copy
.env.exampleto.envand edit as needed - Run the service:
cd fetcher
docker-compose up -d
Scaling
To scale the number of fetcher containers:
docker-compose up -d --scale tracker-report-fetcher=3
Monitoring
The service logs detailed information about its operation. You can view the logs with:
docker-compose logs -f tracker-report-fetcher
Queue Status Tool
A utility script is provided to check the status of the Redis queue:
# From inside the container
python check_queue_status.py
# From the host machine
docker-compose exec tracker-report-fetcher python check_queue_status.py
# Output as JSON
docker-compose exec tracker-report-fetcher python check_queue_status.py --format json
# Connect to a different Redis instance
python check_queue_status.py --host redis-host --port 6379 --username myuser --password mypassword
# Connect to Redis with TLS (e.g., AWS MemoryDB)
python check_queue_status.py --host clustercfg.tracker.xxxxx.memorydb.region.amazonaws.com --port 6379 --username myuser --password mypassword --ssl
This tool shows:
- Number of pending and processing trackers
- Next trackers scheduled for processing
- Active worker containers and their heartbeats
- Currently processing trackers and which container claimed them
Redis Data Structure
The service uses the following Redis keys:
Standard Redis Keys
tracker:pending- Sorted set of tracker IDs with score = next processing timetracker:processing:{id}- Hash of processing metadata for a trackertracker:metadata:{id}- Hash of tracker metadata (success/failure counts, etc.)tracker:workers- Set of active worker container IDstracker:heartbeat:{id}- Heartbeat timestamp for each worker
Redis Cluster Keys
When using Redis Cluster (e.g., AWS MemoryDB), the service automatically uses hash tags to ensure proper slot allocation:
{tracker}:pending- Sorted set of tracker IDs with score = next processing time{tracker}:processing:{id}- Hash of processing metadata for a tracker{tracker}:metadata:{id}- Hash of tracker metadata (success/failure counts, etc.){tracker}:workers- Set of active worker container IDs{tracker}:heartbeat:{id}- Heartbeat timestamp for each worker
The {tracker} hash tag ensures that all related keys are assigned to the same slot in the Redis Cluster, allowing multi-key operations to work correctly.
For more details on Redis Cluster support, including how we solved the CROSSSLOT error and ensured backward compatibility with standalone Redis, see Redis Cluster Support.
Development
Running Locally
To run the service locally for development:
cd fetcher/build
python tracker_report_fetcher.py --anisette-server=http://localhost:6969
Testing Redis Connection
A utility script is provided to test the Redis connection:
cd fetcher/build
python test_redis_connection.py
# Connect to a different Redis instance
python test_redis_connection.py --host redis-host --port 6379 --username myuser --password mypassword
# Connect to Redis with TLS (e.g., AWS MemoryDB)
python test_redis_connection.py --host clustercfg.tracker.xxxxx.memorydb.region.amazonaws.com --port 6379 --username myuser --password mypassword --ssl
This script will:
- Connect to Redis
- Test basic operations (set, get, delete)
- Report success or failure