Note: The developers are aware of this and are working to resolve it and other issues.

I recently installed the latest plugin from Elevated Extensions — Site Search — which added a very nice search feature. Unfortunately, I kept running into issues with the reindexing. I submitted a ticket to the developer, but wanted to get this up and running as quickly as possible, so I dropped the plugin into ChatGPT to help determine the cause, and below are its findings.

ChatGPT Findings

If your plugin’s reindex process never starts, even though there are no PHP errors or log entries, the problem may be a stale rebuild lock.

In this case, the issue was caused by the plugin failing to completely remove an expired transient lock before attempting to create a new one.

Symptoms

You may notice one or more of the following:

  • Reindexing never begins.
  • Reindexing error
  • No PHP errors are logged.
  • No detailed debug messages indicate a problem.

Where to Make the Change

Open the following file:

includes/search/class-search-engine.php

Locate the acquire_rebuild_lock() method.

Inside this method, you’ll find logic that removes an expired rebuild lock. The original code only deletes the timeout transient:

DELETE FROM {$wpdb->options}
WHERE option_name = '_transient_timeout_elevated_ss_rebuild_in_progress'

Unfortunately, this leaves behind the actual transient value:

_transient_elevated_ss_rebuild_in_progress

When the plugin later attempts to recreate the lock using INSERT IGNORE, WordPress ignores the insert because the transient already exists. As a result, the lock cannot be acquired, and the rebuild process exits without an error.

The Fix

Replace the existing expired-lock cleanup with the following code:

// Purge expired lock first.

$expired_timeout = (int) $wpdb->get_var(
    $wpdb->prepare(
        "SELECT option_value FROM {$wpdb->options} WHERE option_name = %s",
        $timeout_key
    )
);

if ($expired_timeout > 0 && $expired_timeout < time()) {
    $wpdb->query(
        $wpdb->prepare(
            "DELETE FROM {$wpdb->options} WHERE option_name IN (%s, %s)",
            $key,
            $timeout_key
        )
    );
}

Why This Works

WordPress stores a database-backed transient as two separate records:

_transient_elevated_ss_rebuild_in_progress
_transient_timeout_elevated_ss_rebuild_in_progress

The original implementation only removed the timeout record after it expired.

Because the transient itself remained, the subsequent INSERT IGNORE failed to recreate the lock. The method interpreted this as “another rebuild is already running” and exited immediately.

By deleting both records whenever an expired lock is detected, the plugin can successfully acquire a new lock and continue with the rebuild.

Recommendation

If you’re implementing your own database-backed locking mechanism, always treat the transient and timeout as a pair:

  • Check whether the timeout has expired.
  • If it has, remove both the timeout and the transient value.
  • Only then attempt to acquire a new lock.

This small change prevents stale locks from blocking future rebuilds while preserving the existing locking strategy.