Documentation menu

Version 3.0.0

Tracking Tables

The registry and per-job logs GoArchive keeps on the destination, their status values, and the checks every run makes against them.

GoArchive keeps all its tracking state on the destination server, in the schema named by destination.job_schema, by default the destination database itself. The tables are the record of what left production: nothing in them is cleaned up automatically.

ObjectCardinalityHolds
archiver_jobone table, one row per jobThe registry: type, root table, binding, status, heartbeat
archiver_job_log_<id>one table per jobEach logged root key and its status
goarchive_metaone table, one rowThe tracking schema’s label, 3.0

GoArchive creates the tables but never the schema, and its account holds no DELETE or DROP there: every deletion is a DBA’s act (Tracking Table Maintenance ). The examples below use a job_schema named goarchive and job id 42; substitute yours.

SELECT id, job_name, root_table, job_type FROM goarchive.archiver_job;
-- job id 42  ->  goarchive.archiver_job_log_42

The job registry (archiver_job)

CREATE TABLE IF NOT EXISTS archiver_job (
  id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
  job_name VARCHAR(255) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL,
  root_table VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
  job_type VARCHAR(16) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
  job_status TINYINT NOT NULL DEFAULT 0,
  source_server_uuid VARCHAR(64) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
  source_database VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
  destination_database VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
  destination_server_uuid VARCHAR(64) CHARACTER SET ascii COLLATE ascii_bin NOT NULL DEFAULT '',
  config_fingerprint TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
  root_key_columns TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  last_heartbeat_at DATETIME NULL,
  UNIQUE KEY uk_job_name (job_name),
  KEY idx_status (job_status)
) ENGINE=InnoDB
ColumnMeaning
idSurrogate key; names the log table, archiver_job_log_<id>
job_nameThe job’s name in the configuration, unique without regard to letter case
root_tableThe root table; fixed for the job’s life
job_typearchive, purge or copy-only; fixed for the job’s life
job_status0 idle, 1 running, 3 failed (the last run ended in an error)
source_server_uuid, source_databaseThe source the job’s log rows came from
destination_databaseThe database the copies went to; it can differ from job_schema
destination_server_uuidReserved; not written by this release ('')
config_fingerprintThe relations (parents, tables, column pairs) and the where text, as JSON
root_key_columnsThe root key’s column names, in key order, as JSON; empty while registration is incomplete
last_heartbeat_atA UTC wall clock refreshed every 15 seconds while running; measure its age with UTC_TIMESTAMP(), never NOW()

The source and destination columns and config_fingerprint are the job’s binding: the facts that give its log rows their meaning.

The job log (archiver_job_log_<id>)

Created when the job is registered, and typed by the root key: one column k1…kN per root key column, in key order, with the column’s own type, charset and collation. A TIMESTAMP is stored as DATETIME of the same precision, so no session time zone can shift it. For a root key (order_id BIGINT UNSIGNED, line_no INT):

CREATE TABLE IF NOT EXISTS archiver_job_log_42 (
  `k1` bigint unsigned NOT NULL COMMENT 'order_id',
  `k2` int NOT NULL COMMENT 'line_no',
  ga_status TINYINT NOT NULL,
  ga_updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`k1`, `k2`),
  KEY idx_ga_status (ga_status)
) ENGINE=InnoDB

Each comment names the root column, for people; GoArchive never reads it. The table must stay InnoDB, with PRIMARY KEY (k1…kN) and the two ga_ columns as created: a log whose shape changed is refused at startup. Only roots are logged, never child rows.

Status values

ga_statusNameWritten
1copiedarchive, in the destination transaction with the batch’s copy and verification; the source rows are not yet all deleted
2completedarchive and purge after their deletes; copy-only with its copy
4deletingpurge, in its own transaction, before its deletes

Each status is written in the transaction that does the work it records. What a rerun does with them is described under Crash Recovery and Resume .

Startup checks

Before anything is recovered or run, every run checks its job against the registry. These are bookkeeping integrity checks: no flag skips them.

  1. The job is fixed. A different job_type or root_table under an existing name, or a name that differs from a stored one only in letter case, is refused: use a new name, or retire the job .

  2. The root key is fixed. root_key_columns and the log’s k1…kN must match the root table’s current primary key: names, order, types, charsets and collations. A changed key is refused, since the log cannot record it: retire the job or use a new name.

  3. The binding. While the job has pending work (archive and purge: any copied or deleting root; copy-only: any completed root, whatever its resume setting), a change to the parts that work depends on is refused, naming the parts and the number of roots:

    Job typeCompared
    archivesource server and database, relations, destination database
    purgesource server and database, relations, where
    copy-onlysource server and database, relations, destination database

    The refusal lists three ways out: restore the original configuration or source and run once; abandon the roots ; or, after a source failover, re-bind the job . With no pending work, the stored binding is replaced by the current one, so changes between clean runs are free.

Registration takes two steps, because the log is named by the job’s id: the job row is written with an empty root_key_columns, then the log is created, and only then are the key’s names written. A run that finds an empty root_key_columns completes the registration. A table that already exists under the new log’s name and holds rows, or has another shape, is never adopted: the job stays unregistered until a DBA drops that table, after confirming no job uses it, or restores archiver_job.

Lifecycle

  1. First run in the schema: goarchive_meta and archiver_job are created under the schema lock, and a fresh schema is labelled 3.0.
  2. First run of a job: the job is registered and its log created.
  3. During a run: job_status = 1, the heartbeat refreshes, and log rows are written per batch.
  4. The end of a run: job_status returns to 0, or 3 after an error. A crash or kill leaves 1 with a stale heartbeat. A job whose log still holds copied or deleting roots, in any status, holds its tables against overlapping jobs until its own rerun finishes them.

The schema label

CREATE TABLE IF NOT EXISTS goarchive_meta (
  id TINYINT NOT NULL PRIMARY KEY,
  schema_version VARCHAR(16) NOT NULL,
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB

One row, id = 1. The value is the tracking-schema label, not the program version: a release changes it when it changes the tables’ layout or a column’s meaning. GoArchive 3.0 writes and recognises exactly one label, 3.0. At startup it labels a fresh schema and refuses every other state; there is no automatic migration.

When a schema is refused, stop every GoArchive process that uses it, whatever its version, then follow the row that matches the refusal:

The refusal says the tables…Procedure
report schema_version "2.0" or "2.2", or hold a 2.x archiver_jobWritten by GoArchive 2.x or earlier: see Upgrading to 3.0
are labelled 3.0 but have no archiver_job tableRestore archiver_job from a backup, or drop every tracking table to start afresh
hold job rows but no schema_version labelAfter confirming GoArchive 3.0 wrote them: INSERT INTO goarchive.goarchive_meta (id, schema_version) VALUES (1, '3.0'); or drop every tracking table
report any other schema_versionAnother GoArchive release wrote them, earlier or later: find out which first. An earlier release’s tables: finish its jobs with its binary, then drop them. A later release’s tables: run that release, and do not drop them. Or point job_schema at another schema, which leaves them untouched