Skip to content
John James Jacoby edited this page Sep 20, 2026 · 4 revisions

Replication lag

LudicrousDB can avoid a replica whose lag exceeds its configured threshold. If no threshold is set, lag is ignored.

A replica is considered lagged when its measured lag is greater than its per-database lag_threshold, or the default in $wpdb->database_defaults['lag_threshold']. A dataset callback may also return a server override containing lag_threshold.

LudicrousDB does not impose a replication monitor. Register two callbacks to supply lag data:

$wpdb->add_callback( 'ludicrousdb_get_lag_cache', 'get_lag_cache' );
$wpdb->add_callback( 'ludicrousdb_get_lag', 'get_lag' );

The cache callback runs before a connection is made and should return lag in seconds, or false when unknown. $wpdb->lag_cache_key identifies the replica endpoint. The live callback runs after connection and can use $wpdb->dbhs[ $wpdb->dbhname ].

Heartbeat example

Percona Toolkit's pt-heartbeat can maintain a timestamp that replicates from the primary. Change heartbeat.heartbeat below to your qualified database and table. The LudicrousDB user needs SELECT access to it.

The qualified table name is deliberate: it avoids changing the selected database before WordPress's query runs. A persistent WordPress object-cache drop-in can share the cached value across requests; without one, the live check remains authoritative.

if ( ! defined( 'LUDICROUSDB_LAG_CACHE_TTL' ) ) {
	define( 'LUDICROUSDB_LAG_CACHE_TTL', 30 );
}

$wpdb->add_callback( 'ludicrousdb_get_lag_cache', 'get_lag_cache' );
$wpdb->add_callback( 'ludicrousdb_get_lag', 'get_lag' );

function ludicrousdb_get_lag_cache( $wpdb ) {
	if ( ! function_exists( 'wp_cache_get' ) || empty( $GLOBALS['wp_object_cache'] ) ) {
		return false;
	}

	$lag = wp_cache_get( $wpdb->lag_cache_key, 'ludicrousdb-lag' );

	return is_numeric( $lag ) ? (float) $lag : false;
}

function ludicrousdb_get_lag( $wpdb ) {
	if ( empty( $wpdb->dbhs[ $wpdb->dbhname ] ) ) {
		return false;
	}

	$dbh    = $wpdb->dbhs[ $wpdb->dbhname ];
	$result = mysqli_query(
		$dbh,
		'SELECT GREATEST( 0, UNIX_TIMESTAMP() - UNIX_TIMESTAMP( ts ) ) AS lag
		FROM heartbeat.heartbeat
		ORDER BY ts DESC
		LIMIT 1'
	);

	if ( false === $result ) {
		return false;
	}

	$row = mysqli_fetch_assoc( $result );
	mysqli_free_result( $result );

	if ( ! is_array( $row ) || ! isset( $row['lag'] ) || ! is_numeric( $row['lag'] ) ) {
		return false;
	}

	$lag = (float) $row['lag'];

	if ( function_exists( 'wp_cache_set' ) && ! empty( $GLOBALS['wp_object_cache'] ) ) {
		wp_cache_set( $wpdb->lag_cache_key, $lag, 'ludicrousdb-lag', LUDICROUSDB_LAG_CACHE_TTL );
	}

	return $lag;
}

Treat an unknown measurement deliberately. Returning false means LudicrousDB has no lag value; it does not prove that a replica is current. Your monitoring and failover design still need to cover stale, stopped, and divergent replicas.

Clone this wiki locally