Skip to main content

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:

  1. All trackers are requested at least once every 6 hours
  2. Requests are staggered throughout the day
  3. Multiple containers can work simultaneously without conflicts
  4. 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 VariableDescriptionDefault
ANISETTE_SERVERURL of the Anisette server(Required)
DB_HOSTPostgreSQL database hostlocalhost
DB_PORTPostgreSQL database port5432
DB_NAMEPostgreSQL database namepostgres
DB_USERPostgreSQL database userpostgres
DB_PASSWORDPostgreSQL database passwordpostgres
REDIS_HOSTRedis hostlocalhost
REDIS_PORTRedis port6379
REDIS_USERNAMERedis username(empty)
REDIS_PASSWORDRedis password(empty)
REDIS_SSLEnable SSL/TLS for Redis connectionfalse
REDIS_DBRedis database number0
FETCH_INTERVALSeconds between fetch cycles300
MAX_KEYS_PER_BATCHMaximum trackers to process per batch64
MAX_REPORT_AGE_DAYSMaximum age of reports to fetch7
REQUEST_INTERVAL_HOURSHours between tracker report requests6
FETCHER_REPLICASNumber of container replicas to run2

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:

  1. Create the data directory for the Docker volume
  2. Create a .env file from the example template if it doesn't exist
  3. Make the Python scripts executable

Manual Setup

  1. Create a data directory for the Docker volume: mkdir -p data
  2. Copy .env.example to .env and edit as needed
  3. 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 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

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:

  1. Connect to Redis
  2. Test basic operations (set, get, delete)
  3. Report success or failure