Changelog
All notable changes to this project will be documented in this file.
[1.4.0 - unreleased]
Added
- Introduce
BinlogCoordinatefor MySQL file/position and GTID-set coordinates, with zero-value detection and typed JSON serialization. Decoding also accepts the legacy baremysql.PositionJSON format. - Add coordinate-based APIs to
BinlogStreamer,StateTracker, andSerializableState, retaining the existing file/position APIs and resume behavior. - Experimental MySQL GTID binlog streaming with
BinlogCoordinateMode: "gtid"for source and target-verification streams. File/position streaming remains the default. - Add helpers to read
@@GLOBAL.GTID_EXECUTEDas a GTID coordinate and check that the server’sgtid_modeisON. - GTID cutover uses set containment. GTID mode requires
gtid_mode=ONon the source and, when target verification is enabled, on the target. Resuming from serialized state is not yet supported in GTID mode and is explicitly rejected.
Changed
- Extend
DMLEventwithBinlogCoordinate()andResumableBinlogCoordinate().DMLEventBaseimplements both; custom implementations must provide these methods. - Replace
BinlogCoordinate.ComparewithHasReached, using file/position ordering or GTID-set containment. Comparing different coordinate types now returns an error instead of panicking. - Compare parsed GTID sets directly during cutover, eliminating per-event GTID serialization and parsing.
ProgressReportFrequencyandStateReportFrequencyuse a 60000 ms default when omitted or set to 0. For configured callbacks, nonzero values below 100 ms are rejected by bothConfig.ValidateConfigandFerry.Initialize; an empty callback URI disables reporting. This prevents accidental callback floods (#214).- Updated golang to 1.27.2
- Updated go packages:
github.com/sirupsen/logrusto v1.10.2github.com/rs/zerologto v1.35.1github.com/gorilla/muxto v1.8.1github.com/golang/snappyto v1.0.0github.com/DataDog/datadog-goto v4.8.3github.com/stretchr/testifyto v1.12.1 (test dependency)- indirect:
golang.org/x/sys,golang.org/x/text,filippo.io/edwards25519,go.uber.org/zap,go.uber.org/atomic
- Updated ruby to 4.0.7 and bundler to 4.0.22 (test dependency)
- Updated ruby gems, including
minitestto v6 andminitest-fail-fastto v0.2 (test and docs dependencies)
Fixed
- Release paused interrupt/resume test callbacks when the Ghostferry subprocess exits, so HTTP server shutdown does not hang indefinitely.
- Ignore binlog row events with NULL sharding keys when they do not belong to the migrating shard, rather than aborting the move. Updates entering or leaving the migrating shard still fail, including transitions to or from NULL.
- Reject malformed typed binlog coordinates instead of silently decoding them as an unset position.
- Clear cached GTID sets and inactive fields when decoding into an existing
BinlogCoordinate, preventing stale reachability results. - Keep GTID cutover aligned with transaction boundaries, including savepoints, DDL, empty transaction commits, and XA prepare/one-phase commit markers, without bypassing coordinate tracking when custom event handlers are registered.
- Synchronize GTID coordinate snapshots and updates across goroutines.
- Finish replayed GTID transactions before stopping at cutover, even when their GTIDs were already streamed before a reconnect.
- Exclude the current GTID from serialized resume checkpoints during reconnect replay, preserving other committed GTIDs. Stop streaming on invalid GTID identities before delivering further rows.
- Publish binlog stop requests atomically after recording the stop coordinate, including reads from the control server.
- Decode unsigned
MEDIUMINTbinlog values of 2^23 and above correctly. Previously they were sign-extended, so inserts were clamped to 16777215 on the target and updates of already-copied rows failed with an out-of-range error (#299). - Resume from the binlog writer coordinate when the InlineVerifier is disabled. Previously a coordinate left in the state by an earlier verifier-enabled run still held back the resume point, so resuming failed once the source purged those binlogs (#184).
Progress.TimeTaken, and the control server’s “Time Taken”, stop counting when the data copy completes instead of also including the wait for cutover and the cutover itself (#314).- ghostferry-copydb keeps top-level
DatabaseRewritesandTableRewritesinstead of overwriting them with the (often empty)Databases.RewritesandTables.Rewrites. When both forms contain rewrites, the maps must be identical (#216). Migration safety: Finish active migrations before upgrading: top-level rewrites ignored by older binaries now take effect. Resume interrupted migrations with the original binary and unchanged effective database/table mappings.
[1.3.1 - 2026-04-15]
Changed
- Align default zerolog log level with logrus (info vs trace)
- Introduce slog handler wrapper around our Logger interface and use it with BinlogStreamer
- Updated golang to 1.26.2
- Updated ruby to 3.4.8 (test dependency)
- Updated github actions (CI dependency)
Removed
- Vendored packages are removed in favour of normal
go mod
[1.3.0 - 2026-04-08]
Changed
- Replace direct
logrususage withLoggerinterface, which useslogrusas a default backend.
Added
- New configuration options for
LogBackendandLogLevel, set with either environment variables or configuration passed in via stdin - New logger backend:
zerolog, which should be backwards compatible withlogrus.
[1.2.1 - 2026-02-12]
Added
PaginationKeynow includes column name
Changed
- Use
PaginationKeyinstead of rawuint64for progress report. This means that table progress report will include not raw value, but a wholePaginationKeyobject, i.e.{ "type": "uint64", "column": "id", "value": 999 }which will be in line with the format of state dump. @driv3r #426
[1.2.0 - 2026-02-06]
Added
- Changelog.
- UUID as ID: validate collation by @grodowski in #422
- NewPaginationKeyFromRow refactor by @grodowski in #424
- Pagination beyond uint64 by @milanatshopify in #417
[1.1.0]
Past releases.