How the Parquet copy works
Where your rows live, what an update writes, what it costs, and what happens if something dies.

Three places
- The index. DBTrail's own MySQL. Every row change lands here within seconds, before and after. It holds the last hours and the point capture resumes from, so it is the one thing you back up.
- Hourly archives. Each hour older than that becomes one Parquet file in S3.
- Snapshots. Every table as one Parquet file, one folder per moment. The first comes from a full read of your database; every later one is the previous snapshot plus the changes since, without reading your database.
When an update reads your database instead
You pick how often a snapshot is made. DBTrail picks how, on every run.

-
No snapshot yet. The first run reads everything. There is nothing to update.
-
You asked for one. The Read database now button on the Snapshots page reads the whole database once, right away. A schedule saved by an older version can also carry a full read on a timetable; its card then says so (for example "with a full read every 7d").
-
An update would cost more than a full read. DBTrail times its recent updates and its last full read, and counts the changes waiting. When the update is estimated to take longer, it reads instead. On a big database a full read takes hours, so this only happens after a long stop has piled up more changes than that. Before anything is measured, age decides: a previous snapshot older than 2 hours, or 6 times the interval if that is longer, is read again rather than caught up.
-
The recorded changes can't rebuild a table. DBTrail starts the update, and stops it when the changes it recorded can't rebuild a table exactly. It publishes nothing rather than a copy that looks right and is wrong. Three things cause this:
- A column was added, dropped or retyped. The snapshot and the new rows no longer have the same columns. Expected after a schema change; nothing to do.
- A table got
TRUNCATE,DROPorRENAME. MySQL logs these as one statement, not row by row, so there are no rows to apply. Nothing to do. - Some changes are missing. DBTrail was stopped longer than MySQL kept
its binary logs, so applying the rest would silently miss rows. To avoid
it, keep binary logs longer than DBTrail could be down
(
binlog_expire_logs_seconds, orbinlog retention hourson RDS).
If full reads are allowed (the setting that shows the Read database now button), that run reads the whole database instead, in the same slot. If not, the slot is skipped, the Snapshots page says why, and the copy waits for the next full read.
Anything else is an update. A run with nothing new is skipped, not repeated.
Snapshots only in S3, no Local folder: updates work the same way (since 0.101.0). Each one is built in DBTrail's staging folder, uploaded, and the folder is emptied. Tables that changed are downloaded, rewritten and uploaded; tables that did not change are copied inside S3, without being downloaded (except on the first update after a full read, which rewrites every table). Before 0.101.0, every run on such a server was a full read.
Snapshots shows, for every run, which kind it was and why, and what the next run will be. A full read every few minutes on a database that hardly changes means one of these keeps firing: the reason on that page names which. An update publishes all tables at once or nothing.
What an update writes
Every table stops at the same point in the binlog. Per table:
- Nothing changed: the previous file is linked. Free.
- Something changed: a small pair of files beside the table holds the changes. The table file is not touched.
- Rewritten in full once the pairs are a day old or grow past a quarter of the file, and in a few rarer cases. The chain starts over. This rebuilds the file from the previous snapshot. It does not read your database.

DuckDB joins the file and its pairs on every query, through views.sql.
Another tool reading the files directly, or a copy that takes only the
*.parquet files, sees the table as it was when the chain started, with no
error.
What it costs
Measured, on synthetic data unless it says otherwise.

- Size. 1.4 GB of InnoDB became 180 MB of Parquet, 7 to 12 times smaller. Real text compresses less.
- Memory. About 4 KB per changed row for narrow rows, 19 KB for wide ones. A long catch-up is the risk; updating more often keeps each window small.
- Freshness. Your interval, plus the update's own time, plus capture lag. Measured once on RDS MySQL 8.4 at about 340 transactions per second: 2.7 to 13.2 minutes from commit to DuckDB, median 5.9.
Sizing a machine: Capacity Planning.
Good to know
- S3. Hourly archives go up as they are written; snapshots, when the server's schedule has an S3 location. Local snapshots are removed only after S3 confirms.
- The newest snapshot. A
currentpointer beside the snapshot folders moves in one step per update.views.sqlreads through it, so it never changes.
If something dies
- The DBTrail process: restart it. Capture resumes where it stopped, as long as MySQL still has the binlog.
- The local snapshot disk: the next update reads from S3 and rewrites every table once.
- The index MySQL: the last hours are gone unless it had backups.
restore-indexrebuilds the rest from S3. - A power cut: a file cut short fails its check instead of being read.