Skip to content

fix: atomically acquire missing option leases - #3082

Merged
chubes4 merged 1 commit into
mainfrom
fix-3078-atomic-unlocked-lease
Aug 9, 2026
Merged

fix: atomically acquire missing option leases#3082
chubes4 merged 1 commit into
mainfrom
fix-3078-atomic-unlocked-lease

Conversation

@chubes4

@chubes4 chubes4 commented Aug 9, 2026

Copy link
Copy Markdown
Member

Summary

  • Replace the unlocked OptionLeaseStore path's unsafe WordPress 7 add_option() upsert with a prepared plain INSERT owned by the lease store.
  • Treat exactly one inserted row as acquisition; duplicate-key contention returns false and re-snapshots the winning lease without overwriting its token.
  • Add deterministic two-session missing-row contention, cache/read-after-write, non-autoload, and stale-takeover regression coverage.

Atomicity and return semantics

WordPress 7.0.3 implements add_option() with INSERT ... ON DUPLICATE KEY UPDATE, so a caller that observed a missing row can overwrite a concurrent winner and still receive a truthy result. The replacement uses a plain prepared INSERT against the options table. The options table's unique option_name key serializes competing inserts: the winner receives one affected row, while the loser receives duplicate-key failure and is not reported as acquired. Expected duplicate errors are suppressed at the wpdb display layer, then the prior suppression state is restored.

The exact stale-payload UPDATE ... WHERE option_name = ? AND option_value = ? path introduced for #3072 remains unchanged. Missing-row acquisition and exact stale-row takeover therefore retain separate fencing semantics.

Cache behavior

The inserted row uses autoload = off, matching WordPress's current explicit non-autoload representation. After either insert success or contention, the store invalidates the option's object-cache entry plus notoptions and alloptions. This removes each session's cached missing-row snapshot and forces the winner and loser to read authoritative database state. Tests verify read-after-write, loser read-after-contention, notoptions removal, and absence from alloptions.

Affected callers

The owner-layer change covers all existing consumers without caller workarounds:

  • WorkerLock global and lane locks
  • RecurringScheduler schedule mutation leases
  • PipelineAIConcurrencyLimiter numbered provider/site slots

Concurrency coverage

The two-session test starts a transaction, inserts the winner lease, and issues the competing plain insert asynchronously. It proves the contender blocks on the unique key until commit, resumes with MySQL duplicate error 1062, and cannot overwrite the winner token. The persisted row is also verified as non-autoloaded. Existing two-session exact stale-takeover and generation-CAS tests remain in place.

Database assumptions

  • The WordPress options schema has a unique key on option_name.
  • MySQL/InnoDB unique-key enforcement blocks a competing insert until the first transaction resolves.
  • Duplicate-key error 1062 maps to a non-acquired result; no upsert clause is used.

Verification

  • vendor/bin/phpcs inc/Core/OptionLeaseStore.php tests/Unit/Core/OptionLeaseStoreTest.php
  • Homeboy changed-scope lint: passed, including PHPCS, ESLint, and PHPStan level 7 with zero findings
  • Homeboy architecture audit: passed with zero introduced findings
  • php tests/prefix-policy-audit.php: 11 passed, 0 failed
  • php tests/worker-lock-smoke.php: 33 assertions passed
  • php tests/recurring-scheduler-idempotency-smoke.php: all assertions passed
  • php tests/ai-step-backpressure-smoke.php: 61 assertions passed
  • PHP syntax checks: passed
  • Local Homeboy PHPUnit gate selected OptionLeaseStoreTest.php but the installed PHPUnit 9 launcher on PHP 8.4 emitted no output and reported zero executed tests; Homeboy correctly classified this as a zero-test harness failure. CI should execute the WordPress integration suite in its configured environment.

Closes #3078

@chubes4
chubes4 merged commit 1c2fba7 into main Aug 9, 2026
15 of 19 checks passed
@chubes4
chubes4 deleted the fix-3078-atomic-unlocked-lease branch August 9, 2026 17:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix: make unlocked OptionLeaseStore acquisition atomic on WordPress 7

1 participant