# How the TeslaMate Repair Module Fixes Data Inconsistencies: A Deep Dive

> Discover how the TeslaMate Repair module automatically fixes data inconsistencies by reverse-geocoding missing drive and charging location references directly in your database.

- Repository: [TeslaMate/teslamate](https://github.com/teslamate-org/teslamate)
- Tags: deep-dive
- Published: 2026-06-16

---

**The `TeslaMate.Repair` module is a background GenServer that periodically scans the database for missing address references in drives and charging processes, then reverse-geocodes the coordinates to populate the missing data.**

The `teslamate-org/teslamate` repository includes a sophisticated data reconciliation system to handle incomplete records caused by earlier imports or transient geocoding failures. Understanding how the Repair module fixes data inconsistencies helps administrators ensure their vehicle logs remain geographically accurate and fully linked to the `Address` table.

## How the Repair Module Detects Missing Addresses

When the GenServer initializes, it schedules a recurring `:repair` message that executes once per hour by default, plus an immediate run via `trigger_run/0` to catch existing issues right away. The detection logic queries two specific tables for orphaned records.

### Scanning Drive Records

The module queries [`lib/teslamate/repair.ex`](https://github.com/teslamate-org/teslamate/blob/main/lib/teslamate/repair.ex) to identify `Drive` entries where either `start_address_id` or `end_address_id` is `nil`, provided both start and end positions exist. This condition indicates that geocoding failed during the original drive recording.

### Scanning Charging Process Records

Similarly, the system locates `ChargingProcess` rows where `address_id` is `nil` but a valid position reference exists. These records appear in [`lib/teslamate/log/charging_process.ex`](https://github.com/teslamate-org/teslamate/blob/main/lib/teslamate/log/charging_process.ex) and represent charging sessions that lack geographic context.

## The Repair Loop and Database Updates

The private `repair/1` function processes problematic rows recursively. For each entity, it performs three critical operations:

1. **Logging** – Records the start of the repair attempt for observability
2. **Address resolution** – Invokes `get_address_id/1` with the associated `Position` to obtain the correct geographic identifier
3. **Changeset application** – Constructs an update using `Drive.changeset/2` or `ChargingProcess.changeset/2` to fill the missing `*_address_id` field(s), then persists the change via `Repo.update/1`

Database errors are caught and reported without halting the loop, ensuring that one corrupt record does not block the entire reconciliation batch.

```elixir

# Inside the Repair module – how a missing address is fixed for a Drive

drive
|> Drive.changeset(%{
     start_address_id: get_address_id(drive.start_position),
     end_address_id:   get_address_id(drive.end_position)
   })
|> Repo.update()

```

## Geocoding with Circuit Breaker Protection

The `get_address_id/1` function implements defensive programming to prevent overwhelming external APIs. It first checks a Fuse circuit-breaker (`:addr_fuse`) to verify the geocoding service is healthy.

If the fuse reports `:ok`, the function sleeps for 1.5 seconds to enforce rate limiting, then calls `Locations.find_address/1` in [`lib/teslamate/locations.ex`](https://github.com/teslamate-org/teslamate/blob/main/lib/teslamate/locations.ex). This function performs a reverse lookup via the configured geocoder and either returns an existing `Address` record or creates a new one.

On repeated failures, the module melts the fuse using `:fuse.melt/1`, which automatically reinstalls after a cooldown period. This pattern prevents endless API calls when the geocoding service is unavailable.

```elixir

# The recursive repair function (simplified)

defp repair([]), do: :ok
defp repair([entity | rest]) do
  # …process entity…

  repair(rest)
end

```

## Manual Triggering and Configuration

Administrators can manually initiate a repair cycle from an IEx console without waiting for the hourly schedule. This is useful when importing historical data or recovering from extended geocoding outages.

```elixir

# Trigger a repair run manually (e.g. from an IEx console)

TeslaMate.Repair.trigger_run()

```

## Summary

- **Periodic detection**: The Repair module runs hourly by default via GenServer messages, scanning `Drive` and `ChargingProcess` tables for nil address references.
- **Safe updates**: Each fix uses Ecto changesets (`Drive.changeset/2`, `ChargingProcess.changeset/2`) and captures errors gracefully to prevent cascading failures.
- **Protected geocoding**: A Fuse circuit-breaker and 1.5-second rate limiting protect the external geocoder from excessive requests during bulk repairs.
- **Manual control**: The `trigger_run/0` function allows immediate reconciliation outside the normal schedule.

## Frequently Asked Questions

### What causes address data inconsistencies in TeslaMate?

Missing address data typically stems from earlier data imports that predated geocoding features, transient network failures during drive recording, or temporary unavailability of the geocoding service when the vehicle was at a specific location.

### How often does the Repair module run automatically?

According to the source code in [`lib/teslamate/repair.ex`](https://github.com/teslamate-org/teslamate/blob/main/lib/teslamate/repair.ex), the GenServer schedules repairs to run once per hour by default, plus an immediate execution upon server startup to catch any backlog.

### What happens if the geocoding service fails repeatedly?

The module uses the `:addr_fuse` circuit-breaker pattern. After multiple failures, the fuse melts and blocks further geocoding attempts until it automatically reinstalls, preventing the system from hammering a failing API endpoint.

### Can I manually trigger a repair run?

Yes. Call `TeslaMate.Repair.trigger_run/0` from an IEx console or another Elixir process to force an immediate scan and repair cycle without waiting for the next scheduled hourly execution.