krowinski/php-mysql-replication
A PHP library for reading MySQL binary logs via the replication protocol. Stream binlog events such as inserts, updates, deletes, and schema changes to build CDC pipelines, audit trails, or cache invalidation without polling. Includes typed event classes and a simple consumer API.
What it is: krowinski/php-mysql-replication is a pure-PHP MySQL replication-protocol client. It connects to MySQL as a pseudo-replica, reads the binary log, and emits decoded row events (insert/update/delete) for application consumption. It is framework-agnostic — a Laravel application can host it, but it is not an official Laravel package and ships no service provider, config publishing, or queue integration.
Architecture fit: Strong fit for change-data-capture workloads where polling and DB triggers are undesirable: cache invalidation, search-index sync, audit trails, read-model projections, and outbound event publishing. It slots into a Laravel stack as a supervised long-running Artisan command that decodes events and dispatches jobs onto the queue. It is a poor fit for request-path use or for teams wanting a managed connector — it is infrastructure-grade plumbing that the team must operate.
v11.1.0 specifics that matter:
TableMapBuilder + useTableMapMetadata — extracts TABLE_MAP_EVENT column metadata parsing into a dedicated builder and makes metadata usage configurable. Architecturally this is the right refactor, but it changes how row payloads are constructed. The default value of useTableMapMetadata and its effect on emitted column keys/types must be confirmed before rollout.databasesIgnore / tablesIgnore — the highest-value operational feature in this release. Filtering at the stream layer avoids paying PHP decode cost for tables we do not care about, which directly reduces CPU and queue volume in multi-tenant or wide-schema databases.user, password, host). Must be gated to non-production and reviewed before enabling.RowEvent refactor — released under a minor version, so semver implies non-breaking; internal restructuring plus expanded integration tests is reassuring but not proof. The historical failure surface for this class of library is MySQL/MariaDB type coverage (temporal, JSON, unsigned BIGINT, ENUM/SET), DDL mid-stream, and GTID handling — not the API.Technical risk: Medium. The library is niche, effectively single-maintainer, and sits on a critical path where a silent decoding error corrupts downstream data rather than throwing. The new metadata layer concentrates that risk in one place, which is good for debugging but means one mis-parse affects all row events. Schema-change handling and checkpoint semantics remain consumer responsibilities.
Key questions:
useTableMapMetadata, and does enabling it change the shape, keying, or typing of emitted row events?tablesIgnore/databasesIgnore work on TABLE_MAP_EVENT (pre-decode) or after row decode — i.e. is the saving real?ALTER TABLE mid-stream, and on GTID-based positioning?Stack fit: Requires binlog_format=ROW, binlog_row_image=FULL, a unique server_id per consumer, and a dedicated MySQL user with REPLICATION SLAVE / REPLICATION CLIENT. These are database-platform changes with security and DBA sign-off, not Composer-level changes — they are the real integration cost, and the long pole in the plan. The Laravel side is comparatively cheap: config file, checkpoint repository (Eloquent or Redis), a supervisor-managed worker, and health/lag endpoints.
Migration path: There is no prior assessment baseline, so treat v11.1.0 as the initial adoption target rather than an upgrade. Pin the exact version in composer.json (do not float ^11), stage against a read replica or snapshot of production, and capture the emitted event stream for a representative window. Run the same window with useTableMapMetadata on and off and diff the output — that single comparison de-risks most of this release. Only then promote to production consumers.
Compatibility: Confirm Laravel and PHP constraints explicitly; the package predates and is independent of Laravel's release cadence, so version alignment is on us. Verify MariaDB support rather than assuming parity with MySQL — protocol/type divergences are a known source of subtle breakage. Check that our MySQL version still supports the binlog retention window our consumer needs.
Sequencing:
server_id allocation, retention window.binlog file + position / GTID set transactionally with downstream writes).databasesIgnore / tablesIgnore to scope the stream and measure the volume/CPU reduction.useTableMapMetadata behind config, diff against shadow baseline.Maintenance: Low code churn, high operational surface. Budget for a pinned-version policy and an internal patch branch; the project is small enough that an upstream stall or maintainer departure is a realistic scenario, and forking a binlog decoder is not trivial. The new config options and debug dump add settings that must be governed in config management rather than ad hoc.
Support: Community-only, no SLA. Escalation is "we fix it." That argues for keeping the integration thin — wrap the library behind our own ChangeStream interface so it can be swapped, and so upstream API churn (as in the RowEvent refactor here) is absorbed in one adapter class rather than across consumers.
Scaling: One connection and one PHP process per stream; throughput is single-threaded per consumer. Scale horizontally by sharding tables or databases across workers, which tablesIgnore/databasesIgnore makes cleaner by letting each worker subscribe to a narrow slice. The performance work in v11.1.0 raises the per-worker ceiling but does not change the shape of the scaling model. Persisting checkpoints and handling backpressure remain ours.
Failure modes to instrument:
Ramp-up: Expect 2–4 weeks before an engineer is genuinely productive here; the domain knowledge (binlog semantics, GTID vs file/position, ROW image modes, replica privileges) transfers poorly from typical Laravel work. Fund that ramp explicitly: a runbook covering lag triage, checkpoint recovery, replay, and schema-change procedure, plus a named on-call owner. Without it, the first binlog-expiry incident becomes an outage.
How can I help you explore Laravel packages today?