EN · ÖZGÜN DEPO BELGESİ
Güncel adaya geçiş
Özgün İngilizce metin. Komutlar ve kanıtlar, belgede belirtilen revizyona ve ortama aittir.
MIGRATION-v2.md
Bu sayfada
v2 migration and rollback
Version 1.26.0-rc.1 is a source review candidate, not a production-approved release. After the owner’s later PHP-test authorization, the local PHP 8.3.0 suite passed 37 regression cases and 148 assertions. A subsequent authorized Docker run passed six real WordPress/WooCommerce/standalone Redis integration scripts and the lifecycle suite, including the previously skipped WordPress contract. Topology/TLS/Relay, checkout isolation, scale and performance gates remain pending. Historical reports describe earlier code only. Track remaining gates in IMPLEMENTATION-STATUS.md.
Requirements and behavior
The separate optional ocp_remember() API requires coordinated configuration of
stampede_groups on all workers. The default empty list keeps protection disabled.
Drain old workers and in-flight callbacks before enabling/changing/disabling it;
mixed source/configuration and direct Redis writes bypass the mutation protocol.
Protected scopes incur extra transactions and lease scans. Read the complete
remember contract, including the optional Redis 6.0.9+ capability
requirement, no-replay/partial-EXEC limits and final test gates. The existing
remember/sear helpers do not acquire leases or wait; cached false is now a hit.
Use PHP 8.2+ and PhpRedis 6.0+. Relay remains supported in source with runtime capability checks; licensed real-Relay verification remains open. Install the matching plugin and bundled object-cache.php drop-in from the same candidate. The public wp_cache_* entry points and WP_REDIS_CONFIG name remain unchanged.
Before enabling or upgrading, inspect the existing wp-content/object-cache.php.
Automatic and admin file replacement/removal require its nonempty Plugin Name
and Plugin URI headers to match this project’s bundled drop-in. Legacy upstream
headers remain recognizable for compatibility but do not grant file ownership.
Unidentified or unreadable files are preserved, and CLI disable also refuses to
remove them. Keep a backup and disable the provider that manages that drop-in
before installing this project’s stub. The existing wp redis enable --force
command remains an explicit overwrite option; it warns on unowned files and does
not create a backup automatically. The new migration wizard
adds explicit preview, signed backup download and guarded install/restore actions;
its runtime validation is deferred. Header matching is an accidental-conflict
safeguard, not file integrity verification or atomic exclusion of another writer.
Identifiers use v2:<encoded prefix>:<encoded scope>:<encoded group>:{tag}:<encoded key>.
Prefix, scope, group and string form of the key retain case, separators, whitespace
and Unicode bytes. Integers and their equivalent string keys retain WordPress’s
existing identity semantics. Site keys use site:N; global groups remain shared.
Global/all-sites scope covers the sites using this cache prefix, including other
networks sharing the same Redis database and prefix. Use separate prefixes for
independent installations; a prefix is an application isolation boundary.
No old namespace is read on the first v2 request. Old data is not automatically deleted. Expect a cold-cache database load increase; the existence of old Redis data does not warm v2. Automatic integrity/lifecycle cleanup is limited to v2. Explicit full-database flush commands retain their existing broad behavior.
Split alloptions changes use WATCH/MULTI/EXEC on one captured primary connection. Only fields previously observed by the caller are eligible for removal. A watch conflict returns false, is not retried, and invalidates the local value. Redis does not roll back already executed commands after an individual EXEC error. Cluster resolves CLUSTER SLOTS before every WATCH and uses a direct, nonpersistent connection to the owning primary. Relay conditional reads use a connection with its local value cache disabled. Each owning connection can retain at most eight such sockets for subsequent transactions. Settings and native connection state must still match; failed or uncertain sockets are retired. When native state inspection is unavailable, the socket is closed after that operation as before. Native redirection or retry is not used to replay the transaction. Network access to the reported primary and permission for these commands are prerequisites. Connection reuse and its performance effect await the final test phase. Initial serializer/compression option failures now close the client and reject setup.
Persistent incr/decr now use the same watched-primary contract. Arithmetic,
PHP serialization and nonpersistent-group behavior are retained. A missing key,
conflict, write failure or unconfirmed EXEC returns false and invalidates the
request-local value. Only a confirmed SET publishes the new integer. A false
result after a lost reply does not prove that Redis stayed unchanged; do not
blindly retry a non-idempotent mutation.
Counters read and memoize INFO on the captured physical connection to select expiry handling. Redis 6+ uses one SET XX KEEPTTL. Older servers use watched PTTL and one SET XX PX with the remaining lifetime, reduced by local elapsed time; persistent keys stay persistent. This relative-TTL fallback can drift by queue/transport delay and does not promise the exact original deadline. Missing, expired or invalid TTLs abort. INFO (and PTTL on older servers) must be permitted; unavailable capability information fails the mutation without a write. No failed SET is followed by an unconditional SET. See the Redis SET command contract.
Sentinel retries only commands on the existing read-only allowlist. Ordinary writes and unknown/raw commands disable native retries for their first send, restore the previous retry policy afterwards, and never replay after an error. Primary rediscovery prepares a later caller-initiated command; discovery failure does not replace the original write error. If restoring retry settings fails, the socket is closed. Buffered transactions retain their existing pinned behavior. Network failures can therefore surface as false/errors instead of silent recovery. The additional round trips and conflict rate still require measurement.
Raw metadata/analytics callbacks keep serialization and compression disabled when Sentinel discovers replacement primary/replica connections. Newly discovered nodes join every active raw scope before use; nested callbacks restore the immediately previous mode, and the outer callback restores each client’s original options. Pinned Cluster/Relay transactions also inherit the active raw scope and restore their normal options before pool reuse. Setup/cleanup failures retain the first error and an unsuccessful restoration attempts to close the affected client. These changes have source review only; real failover and native-client validation remain part of the final test phase.
Bounded defaults and management
| Option | Default | Meaning |
|---|---|---|
| group_flush | incremental | Non-atomic PHP SCAN followed by deletion batches of at most 500 keys |
| prefetch_ttl | 3600 | Per-record metadata lifetime in seconds |
| prefetch_max_keys | 256 | Maximum recorded/prefetched keys in one request, across sites and runtime flushes |
| prefetch_max_requests | 1024 | Metadata request records per site, enforced through Redis transactions |
| log_limit | 500 | In-memory logger/command record cap; omitted records are counted |
| ttl_jitter | 0 | Optional 0-100% random reduction of each effective finite write TTL; disabled by default |
TTL jitter runs after maxttl/queryttl limits, separately for each valid key in
single/bulk writes and for split alloptions hashes. It never extends the capped
TTL or changes a finite TTL to zero. Effective TTLs of zero (persistent) and one
second stay unchanged. A 10% setting maps 600 seconds to 540-600 seconds inclusive.
It adds no Redis commands, but random sampling and earlier expiry have an
unmeasured cost. Counter increments/decrements still preserve expiry; internal
metadata and prefetch retention keep their dedicated policies. Disabling jitter
changes future writes only. No concurrent recomputation lock is provided by jitter.
The separate stampede/remember API work remains outstanding.
The same configured limit also caps the request’s diagnostic error list, which
retains its earliest records. Later errors still increment error metrics and reach
the configured logger. info()->errors_omitted and the performance snapshot’s
error_records_omitted report discarded error-list records; log_records_omitted
continues to describe the separate logger buffer. Do not add these counters: the
same error may be omitted from both buffers. Diagnostics, the overview widget and
Site Health report truncation, and the latter two include discarded records in
their error totals. A fresh cache initialization resets the error-list counter;
runtime flushes and repeated initialization of the existing cache do not.
SCAN COUNT is a hint; deletion batches impose their own limit. Concurrent writes
can occur between scan batches. Existing scan and keys Lua options remain
atomic per Redis node and can block that node. They do not create a transaction
across a cluster. group_flush=full retains database-wide behavior for the public
group-flush operation and all-sites management actions; the UI labels this scope.
Explicit site, prefetch and maintenance cleanup stays scoped and uses incremental
SCAN even when the public group-flush setting is full.
Prefetch metadata uses a record hash and an expiry index in the same site scope. Equal container counts do not prove equal membership. Before crediting enough evictions to stay within the request limit, writes verify the relevant records and the current request’s index membership. Detected inconsistencies reset only that site’s two disposable metadata containers; cached application values remain intact. This adds a ZSCORE read to normal writes and HEXISTS reads for required evictions. The performance cost has not been measured. WATCH conflicts and failed EXEC replies are not replayed. Redis does not roll back partially applied EXEC commands: a failed write can leave damaged metadata, including a temporary cap overrun, for a subsequent successful write to repair or bound.
wp redis doctor --format=json and --format=table expose allowlisted health
facts. Errors return nonzero; warnings remain visible with exit zero. TLS checks
describe configuration and the connection probe, not a successful probe of every
node. Method availability does not establish server ACL permission for every
operation. Reports do not include passwords, tokens or cached values.
The performance widget labels current-request measurements. Historical charts require analytics to be enabled. Instrumented command attempts include failed attempts; their wait is included. They are not identical to the server’s command counter (connection handshakes and client-internal work are separate). Legacy cache read/write counters remain available. No external telemetry is added.
REST operations require objectcache_manage and validate site scope on the server. Site managers cannot select another site or global groups. Network managers may select their network’s sites or the existing all-sites prefix scope. A preview deletes nothing and reports bounded observations, never a definitive key count. Raw keys and values are replaced by fingerprints. Long-running workers must reset runtime cache between jobs.
Rollout gates
- Preserve an immutable copy of the previous plugin/drop-in, its configuration, and the candidate archive and SHA-256 manifest. Record the current namespace.
- Verify identical plugin/drop-in versions and runtime requirements on staging. Refresh PHP workers/OPcache when changing code. Do not mix code generations.
- Complete the real WordPress/WooCommerce, multisite, concurrency, topology/TLS and Relay contracts listed in IMPLEMENTATION-STATUS.md before acceptance.
- Capture at least five cold/warm repetitions with the same fixture/configuration. Bind each report to the installed runtime, drop-in and measurement harness. A warm p95 or PHP peak-memory increase above 5% blocks rollout.
- Start on staging, then one low-traffic site, then the multisite network. Monitor database query load, errors/timeouts and latency during each cold transition. Stop expansion when the agreed performance thresholds are exceeded.
No target staging/production installation or deployment is performed by this development work. Local PHP regressions or a review archive cannot satisfy these gates.
Explicit legacy cleanup
Retain old records until rollback planning and observation are complete. The command requires the exact, nonempty, previously normalized prefix (1-32 lowercase letters, digits, underscores or hyphens). Do not guess it. Installations that used an empty or otherwise unsupported legacy prefix need a separately reviewed, explicit inventory; the command does not widen the match to compensate.
wp redis cleanup-legacy --prefix=old-prefix --site-id=2 --group=posts
wp redis cleanup-legacy --prefix=old-prefix --site-id=2 --group=posts --execute --yes
The first command only samples fingerprints. The second removes matching old keys in bounded batches and can partially complete before a failure. Site scope excludes shared/global records. Omitting site/group widens only within the exact legacy prefix. It never interprets v2 identifiers as legacy records.
Rollback without stale cache reuse
- Stop writes/traffic or isolate the target so old and new workers cannot overlap.
- Before starting previous code, configure a fresh, unused prefix within the
previous version’s length/character constraints, for example
rollback-20261004a. Verify that it has no keys. Do not restore the old pre-v2 prefix. - Restore both the previous plugin and its matching drop-in; restart/refresh PHP workers and OPcache. Keep the database and persistent business data intact.
- Warm and observe the empty rollback namespace, then reopen traffic gradually. Keep v2 records for diagnosis or remove only their explicitly selected scope.
Reinstalling v2 later should likewise use an unused prefix if its previous cache could have become stale while old code was serving requests.