> **Can't find what you're looking for?** Use `search_docs` on the docs MCP server at `https://docs.ampdb.io/api/mcp` to find what you need.

# Garbage Collection

When [compaction](/running-amp/storage/compaction) merges files, the files it merged are no longer part of their
table, but a query that started before the merge may still be reading them. Garbage collection deletes them safely:
it waits until such queries have had time to finish, then deletes the files from storage.

It runs on its own: every deployed dataset has a garbage collection job, next to the job that builds its tables. You
only need this page to tune how long files wait, or to clean up after a dataset stops.

## How it works

A file goes through four stages:

1. **In use**: the [metadata database](/running-amp/metadata-database) lists it in its table, and queries read it.
2. **Scheduled**: compaction merged it, or you [pruned](/running-amp/storage/garbage-collection#delete-a-tables-abandoned-files) it. Amp removes it from its
   table and lists it for deletion, with a time it expires. New queries no longer read it, but queries already running
   still can.
3. **Expired**: the time has passed.
4. **Deleted**: the dataset's garbage collection job deletes the file from storage, then from the list.

The garbage collection job runs when the dataset is deployed, then every hour, on the worker that builds the dataset.
Each run deletes every expired file of the dataset's tables. A merged file is therefore deleted between one grace period
and one grace period plus an hour after its merge.

| Where scheduled files come from                                           | How long until they expire                                                                                                                            |
| ------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| [Compaction](/running-amp/storage/compaction), for the files it merges    | [`writer.compactor.gc_manifest_grace_period`](/running-amp/configuration-reference#writercompactorgc_manifest_grace_period): 1,800 seconds by default |
| [`ampctl table prune`](/clis/ampctl/table), for a table's abandoned files | Its `--gc-delay`: 3,600 seconds by default                                                                                                            |

Nothing else deletes files through garbage collection. Jobs that write files never schedule them, and
[truncating or deleting a revision](/managing-amp/table-revisions) works differently.

## Set the grace period

The grace period is the only thing that protects a running query: a query that's still reading a file when it's
deleted fails. Set it above your longest queries:

```toml [config.toml]
[writer.compactor]
gc_manifest_grace_period = 7200.0 # 2 hours
```

Like the other `[writer.compactor]` settings, it applies to every table on a worker, after the worker restarts.

## Check the backlog

The status of a deployed dataset reports its garbage collection: the revisions with files waiting, how many have
expired, how many haven't yet, and when the next one expires. See [Dataset Status](/managing-amp/dataset-status):

```bash
ampctl dataset status acme/eth_mainnet@1.0.0 --json
```

The garbage collection job's [metrics](/running-amp/metrics#garbage-collection-metrics) count the files it finds and
deletes.

## Collect files on demand

Stopping a running deployment stops its garbage collection job too, so the files its tables had scheduled stay in
storage. To delete them, create a garbage collection job for the dataset's version, by its namespace, name, and manifest
hash, with `MANIFEST_HASH` set to the hash of the version's manifest (see [Versioning](/datasets/versioning)):

```bash
ampctl job create gc acme eth_mainnet "$MANIFEST_HASH"
```

A version has one garbage collection job, so this runs the deployment's own job again, once. To keep it running, give
it a [trigger](/managing-amp/jobs/triggers), like `--trigger interval --trigger-interval 3600` for every hour. See
[Jobs](/managing-amp/jobs) to set when and where the job runs.

While the dataset is deployed, creating the job changes the deployment's own: it runs at once, then follows the trigger
you give instead of running every hour, and without one, it doesn't run again. It also leaves the deployment, so
stopping the deployment no longer stops it. To collect now and keep collecting every hour, give it the hourly trigger:

```bash
ampctl job create gc acme eth_mainnet "$MANIFEST_HASH" --trigger interval --trigger-interval 3600
```

## Delete a table's abandoned files

When a chain reorganizes, a table can keep files of the blocks it replaced, which no query reads anymore. To schedule
them for deletion, prune the table's revision, once the job that writes it has stopped:

```bash
# Every abandoned file of revision 42, deleted after 1 hour
ampctl table prune 42

# Only those that end before block 1,000,000, deleted after 2 hours, so streaming
# clients that resume across a reorg can still read the recent ones
ampctl table prune 42 --before-block 1000000 --gc-delay 7200
```

See [Table Revisions](/managing-amp/table-revisions).

## Limitations

* **Files Amp never recorded stay in storage.** Garbage collection only deletes the files it lists. If a worker stops
  after writing a merged file, but before recording it, the file stays in storage until you delete it yourself.
* **A query that outlives the grace period can fail**, and so can a streaming client that resumes from blocks a prune
  deleted. Nothing keeps a file alive for the query reading it.
* **A revision shared with another dataset** is only collected by the garbage collection job of the dataset that
  created it.
* **Deleting a revision** with `ampctl table delete` removes it from the metadata database, but not its files from
  storage.
* **A deployment that reached its end block keeps collecting.** Its garbage collection job keeps running every hour,
  and stopping the deployment doesn't stop it. Stop the job itself, with `ampctl job stop` and the
  `garbage_collection.job_id` of the [dataset's status](/managing-amp/dataset-status).
