Uh oh!
There was an error while loading. Please reload this page.
fix(locks): make cron admission atomic - #522
Conversation
Persist concurrency state in the options table and bind workers to generation-aware leases so stale cleanup cannot release a replacement lock. Refs: PLTFRM-2759 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Scanned FilesNone |
There was a problem hiding this comment.
Pull request overview
This PR refactors Cron Control’s concurrency locking to use an atomic, database-backed lock state (stored in the options table) and introduces generation-aware release logic to prevent stale workers from accidentally releasing newer/recovered locks.
Changes:
- Replaced object-cache-based lock counters with an atomic
INSERT ... ON DUPLICATE KEY UPDATElock state stored inwp_options. - Added generation tracking per admitted lock acquisition and made
free_lock()generation-aware (including deleting inactive lock rows). - Added unit tests covering admission limits, stale recovery, and old-generation cleanup behavior.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| includes/class-lock.php | Implements atomic options-table lock state + generation-aware acquisition/release and parsing helpers. |
| includes/class-events.php | Routes event action lock release through Lock::free_lock() consistently. |
| tests/unit-tests/test-lock.php | Adds unit tests validating concurrency limits and generation-safe cleanup semantics. |
Suppressed comments (2)
includes/class-lock.php:152
random_int()can throw, and this code path currently doesn’t handle that. A failure here would prevent priming locks and may cascade into cron admission failures. Consider the same try/catch +wp_rand()fallback used incheck_lock().
$wpdb->query(
$wpdb->prepare(
"INSERT IGNORE INTO `$wpdb->options` (`option_name`, `option_value`, `autoload`) VALUES (%s, %s, 'no')",
self::get_key( $lock ),
self::build_lock_state( 0, time(), random_int( 1, \PHP_INT_MAX ) )
)
includes/class-lock.php:192
random_int()can throw and isn’t caught here, which could makereset_lock()fatally error (potentially leaving locks stuck). Consider catching\Throwableand falling back towp_rand()for generation selection.
$now = time();
$generation = random_int( 1, \PHP_INT_MAX );
$result = $wpdb->query(
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Use WordPress's non-throwing random wrapper for lock-row generation so an unavailable CSPRNG cannot interrupt cron admission, lock priming, or lock reset. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Resolve the namespaced wp_rand call so the test bootstrap can load the lock class. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Use the WordPress random wrapper when available and a bounded native fallback while the lock class initializes before WordPress functions load. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
Suppressed comments (1)
Previously missed (1) — in code that hasn't changed since the last review.
tests/unit-tests/test-lock.php:37
- Despite the test name, these calls are sequential, so the previous read-then-increment implementation would also pass this test. The PR's core regression is simultaneous admissions observing the same prior count; add a test using separate database connections/processes synchronized to contend on the same lock and assert that only one acquisition succeeds.
$this->assertTrue( Lock::check_lock( $lock, 1 ) );
$this->assertFalse( Lock::check_lock( $lock, 1 ) );
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Keep an acquired generation after a failed database release, and stop unnecessary lock priming writes. Refs: PLTFRM-2759 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Summary
Testing
git diff --check/tmp/wordpress-tests-liband its MariaDB-backed WordPress test database are unavailableRefs: https://linear.app/a8c/issue/PLTFRM-2759