{"id":357611,"date":"2026-09-02T09:18:16","date_gmt":"2026-09-02T09:18:16","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/yac-object-cache\/"},"modified":"2026-09-02T10:00:40","modified_gmt":"2026-09-02T10:00:40","slug":"yac-object-cache","status":"publish","type":"plugin","link":"https:\/\/wordpress.org\/plugins\/yac-object-cache\/","author":23553343,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"1.2.2","stable_tag":"1.2.2","tested":"7.1","requires":"5.6","requires_php":"7.0","requires_plugins":null,"header_name":"Yac Object Cache","header_author":"Xinchen Hui<laruence@php.net>","header_description":"Yac (lock-free shared memory) backed object cache for WordPress. Auto-deploys the object-cache.php drop-in on activation. No external servers: the cache lives in shared memory inherited by PHP-FPM workers.","assets_banners_color":"351d56","last_updated":"2026-09-02 10:00:40","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"https:\/\/github.com\/laruence\/wp-yac-cache","header_author_uri":"https:\/\/www.laruence.com","rating":5,"author_block_rating":0,"active_installs":0,"downloads":51,"num_ratings":1,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"1.2.2":{"tag":"1.2.2","author":"laruence","date":"2026-09-02 10:00:40","revision":3677677}},"upgrade_notice":[],"ratings":{"1":0,"2":0,"3":0,"4":0,"5":1},"assets_icons":{"icon-128x128.png":{"filename":"icon-128x128.png","revision":3677662,"resolution":"128x128","location":"assets","locale":"","width":128,"height":128},"icon-256x256.png":{"filename":"icon-256x256.png","revision":3677662,"resolution":"256x256","location":"assets","locale":"","width":256,"height":256}},"assets_banners":{"banner-1544x500.png":{"filename":"banner-1544x500.png","revision":3677662,"resolution":"1544x500","location":"assets","locale":"","width":1544,"height":500},"banner-772x250.png":{"filename":"banner-772x250.png","revision":3677662,"resolution":"772x250","location":"assets","locale":"","width":772,"height":250}},"assets_blueprints":{},"all_blocks":[],"tagged_versions":["1.2.2"],"block_files":[],"assets_screenshots":{"screenshot-1.png":{"filename":"screenshot-1.png","revision":3677677,"resolution":"1","location":"assets","locale":"","width":1619,"height":756},"screenshot-2.png":{"filename":"screenshot-2.png","revision":3677677,"resolution":"2","location":"assets","locale":"","width":2262,"height":1398},"screenshot-3.png":{"filename":"screenshot-3.png","revision":3677677,"resolution":"3","location":"assets","locale":"","width":1622,"height":999}},"screenshots":{"1":"Cache health dashboard: hit-rate ring, cause-attributed metric bars and the live shared-memory round trip (0.005 ms).","2":"Storage overview: key slots in use, lookups, hit rate, values-memory pool and the memory-health verdict.","3":"Entry inspector: keys-by-group pie, occupancy totals and the largest entries by content length."}},"plugin_section":[262246],"plugin_tags":[146,7913,247,278828,278827],"plugin_category":[52,54],"plugin_contributors":[278829],"plugin_business_model":[],"class_list":["post-357611","plugin","type-plugin","status-publish","hentry","plugin_section-dashboard-widgets","plugin_tags-cache","plugin_tags-object-cache","plugin_tags-performance","plugin_tags-shared-memory","plugin_tags-yac","plugin_category-performance","plugin_category-security-and-spam-protection","plugin_contributors-laruence","plugin_committers-laruence"],"banners":{"banner":"https:\/\/ps.w.org\/yac-object-cache\/assets\/banner-772x250.png?rev=3677662","banner_2x":"https:\/\/ps.w.org\/yac-object-cache\/assets\/banner-1544x500.png?rev=3677662","banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":false,"icon":"https:\/\/ps.w.org\/yac-object-cache\/assets\/icon-128x128.png?rev=3677662","icon_2x":"https:\/\/ps.w.org\/yac-object-cache\/assets\/icon-256x256.png?rev=3677662","generated":false},"screenshots":[{"src":"https:\/\/ps.w.org\/yac-object-cache\/assets\/screenshot-1.png?rev=3677677","caption":"Cache health dashboard: hit-rate ring, cause-attributed metric bars and the live shared-memory round trip (0.005 ms)."},{"src":"https:\/\/ps.w.org\/yac-object-cache\/assets\/screenshot-2.png?rev=3677677","caption":"Storage overview: key slots in use, lookups, hit rate, values-memory pool and the memory-health verdict."},{"src":"https:\/\/ps.w.org\/yac-object-cache\/assets\/screenshot-3.png?rev=3677677","caption":"Entry inspector: keys-by-group pie, occupancy totals and the largest entries by content length."}],"raw_content":"<!--section=description-->\n<p>Yac uses the <a href=\"https:\/\/github.com\/laruence\/yac\">Yac<\/a> PHP extension as the backing store for the WordPress object cache.<\/p>\n\n<p>Unlike Memcached or Redis, Yac stores data in <strong>shared memory inherited by every PHP-FPM worker<\/strong> on the machine. There is no cache server to install, no socket to configure, and every cache read is a hash lookup in local memory \u2014 typically microseconds, with no network round trip and no global lock.<\/p>\n\n<p><strong>Highlights<\/strong><\/p>\n\n<ul>\n<li><strong>Fast.<\/strong> A cache read is a shared-memory hash lookup \u2014 the status page measures a 0.005 ms round trip. On a production site (laruence.com, PHP 8.1-FPM, 8 cores), full homepage renders ran ~20% faster than the classic Memcached drop-in: 141.6 vs 118.7 req\/s at 20 concurrent users, +18-20% across 20-100 concurrency, zero failed requests.<\/li>\n<li><strong>No external server.<\/strong> The cache lives in shared memory (<code>mmap<\/code>\/SysV), fork-inherited by FPM workers.<\/li>\n<li><strong>Lock-free.<\/strong> Per-slot CAS arbitration; throughput scales with worker count. No global lock.<\/li>\n<li><strong>Self-deploying.<\/strong> Activating the plugin writes <code>object-cache.php<\/code> to <code>wp-content\/<\/code>.<\/li>\n<li><strong>Simple keys, simple flush.<\/strong> Keys are stored verbatim while they fit Yac's 48-byte limit; <code>wp_cache_flush()<\/code> calls <code>Yac::flush()<\/code> and wipes the entire shared memory on the machine \u2014 know that before you flush.<\/li>\n<li><strong>Graceful degradation.<\/strong> If Yac is unavailable (extension missing), the drop-in falls back to a per-request in-memory cache and WordPress keeps working.<\/li>\n<li><strong>Single-node focus.<\/strong> Keys carry no per-blog prefix; installs sharing one PHP pool isolate via <code>YAC_OCACHE_KEY_PREFIX<\/code>. Multisite blogs share one namespace.<\/li>\n<li><strong>Entry inspector.<\/strong> On the admin page, click any top-entry key to see the deserialized value, padded size, expiry and \u2014 on newer yac builds \u2014 last access, hit count and whether the value is embedded in its slot; or delete the entry.<\/li>\n<\/ul>\n\n<p><strong>Best fit<\/strong><\/p>\n\n<p>Yac is a <em>local<\/em> cache. It is ideal for single-node or few-node WordPress installs where all PHP workers run on one machine. On large multi-server clusters with strict cross-node consistency needs, a network cache (Memcached\/Redis) may suit better.<\/p>\n\n<!--section=installation-->\n<ol>\n<li><p>Install the Yac extension, any of three ways (then enable <code>extension=yac.so<\/code> in php.ini):<\/p>\n\nPECL\n\n<p>pecl install yac<\/p>\n\nPIE (the PHP Foundation's PECL successor; yac is on Packagist)\n\n<p>pie install laruence\/yac<\/p>\n\nBuild from source\n\n<p>git clone https:\/\/github.com\/laruence\/yac.git &amp;&amp; cd yac\nphpize &amp;&amp; .\/configure &amp;&amp; make &amp;&amp; sudo make install<\/p><\/li>\n<li><p>Install and activate Yac Object Cache. Activation deploys <code>wp-content\/object-cache.php<\/code>.<\/p><\/li>\n<li><p>In <code>wp-config.php<\/code>, above the \"That's all, stop editing!\" line, add:<\/p>\n\n<p>define( 'WP_CACHE', true );\ndefine( 'YAC_OCACHE_KEY_PREFIX', 'ab_' ); \/\/ unique per install when sites share one PHP pool<\/p><\/li>\n<\/ol>\n\n<p>That is it. Visit <strong>Tools \u2192 Yac Object Cache<\/strong> to verify status.<\/p>\n\n<p><strong>Optional php.ini tuning<\/strong><\/p>\n\n<pre><code>yac.enable = 1\nyac.keys_memory_size = 4M       ; ~32K slots\nyac.values_memory_size = 64M    ; raise for large sites (big alloptions)\n<\/code><\/pre>\n\n<p><strong>Optional wp-config switches<\/strong><\/p>\n\n<pre><code>define( 'YAC_OCACHE_EMPTY_TTL', 0 );    \/\/ default 21600: lifetime cap (seconds) on empty-array negative cache results; set 0 to disable\ndefine( 'YAC_OCACHE_DISABLE', true );     \/\/ emergency escape hatch: force runtime-only mode\n<\/code><\/pre>\n\n<!--section=faq-->\n<dl>\n<dt id=\"do%20i%20need%20a%20memcached%20or%20redis%20server%3F\"><h3>Do I need a Memcached or Redis server?<\/h3><\/dt>\n<dd><p>No. That is the point \u2014 the cache lives in shared memory on the machine.<\/p><\/dd>\n<dt id=\"what%20happens%20if%20the%20yac%20extension%20is%20not%20installed%3F\"><h3>What happens if the Yac extension is not installed?<\/h3><\/dt>\n<dd><p>The drop-in degrades to a per-request in-memory cache. Your site keeps working; you just lose cross-request persistence. The status page flags the missing extension.<\/p><\/dd>\n<dt id=\"how%20are%20keys%20stored%2C%20given%20yac%27s%2048-byte%20key%20limit%3F\"><h3>How are keys stored, given Yac's 48-byte key limit?<\/h3><\/dt>\n<dd><p>Storage keys are <code>&lt;YAC_OCACHE_KEY_PREFIX&gt;:&lt;group&gt;:&lt;key&gt;<\/code> (default prefix <code>wp<\/code>), kept verbatim while they fit the 48-byte budget; over-long keys keep the group verbatim and hash (crc32b) only the key part. Keys carry no per-blog prefix: use a different YAC_OCACHE_KEY_PREFIX per install when sites share one PHP pool; multisite blogs share the namespace.<\/p><\/dd>\n<dt id=\"does%20flush%28%29%20clear%20other%20yac%20users%20on%20the%20same%20machine%3F\"><h3>Does flush() clear other Yac users on the same machine?<\/h3><\/dt>\n<dd><p>Yes. <code>wp_cache_flush()<\/code> calls <code>Yac::flush()<\/code>, which wipes the entire shared memory on the machine, including data written by other Yac users sharing the PHP pool. The admin page asks for confirmation before flushing.<\/p><\/dd>\n<dt id=\"multisite%3F\"><h3>Multisite?<\/h3><\/dt>\n<dd><p>Yes, with a caveat: blogs of one install share the cache namespace (keys carry no blog prefix), which is fine for most sites; <code>switch_to_blog()<\/code> does not re-namespace keys. Run separate installs with different <code>YAC_OCACHE_KEY_PREFIX<\/code> values when blogs must not share entries.<\/p><\/dd>\n<dt id=\"what%20about%20wp_cache_flush_group%28%29%3F\"><h3>What about wp_cache_flush_group()?<\/h3><\/dt>\n<dd><p>Yac cannot delete entries by prefix, so a group flush clears the request-level copy of that group; shared entries then expire via TTL. The plugin reports <code>flush_group<\/code> as unsupported so WordPress core does not rely on it.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>1.2.2<\/h4>\n\n<ul>\n<li>Fixed stale shared-memory writes: within one request, <code>wp_cache_set()<\/code> on a key already written by <code>wp_cache_add()<\/code> in that same request skipped the shared-memory write, so other requests kept reading the value the add stored (the <code>update_option()<\/code> pattern \u2014 <code>add()<\/code> inside <code>get_option()<\/code>, then <code>set()<\/code> \u2014 left the old option behind).<\/li>\n<li>Replaced <code>YAC_OCACHE_SKIP_EMPTY<\/code> with <code>YAC_OCACHE_EMPTY_TTL<\/code> (default 21600s): empty results (bot-probed <code>get_page_by_path()<\/code> paths, comment query misses) now share as usual but expire after that lifetime instead of occupying a slot forever. Set 0 to disable the cap.<\/li>\n<li>The health panel's \"keys used\" now reports the number of live entries rather than the <code>slots_used<\/code> high-water mark.<\/li>\n<\/ul>\n\n<h4>1.2.1<\/h4>\n\n<ul>\n<li>Placed the PHPCS EscapeOutput annotations around the health-ring output so WordPress.org Plugin Check recognizes the exemption (the previous inline annotation sat on the wrong line of the multi-line statement).<\/li>\n<li>Annotated <code>pre_wp_cache_get<\/code> in the drop-in as a WordPress core hook.<\/li>\n<\/ul>\n\n<h4>1.2.0<\/h4>\n\n<ul>\n<li>Fixed the health-ring donut on the admin dashboard disappearing: the SVG was piped through <code>wp_kses_post()<\/code>, whose allowed-tag list strips SVG elements and left only the center text visible.<\/li>\n<li>Renamed every plugin-owned symbol from the <code>wp_yac_<\/code>\/<code>WP_YAC_<\/code>\/<code>wp-yac-<\/code> prefixes to <code>yac_ocache_<\/code>\/<code>YAC_OCACHE_<\/code>\/<code>yac-ocache-<\/code> for WordPress.org prefix policy (the <code>wp_<\/code> prefix is reserved for WordPress core). The wp-config switches were renamed accordingly: <code>YAC_OCACHE_KEY_PREFIX<\/code>, <code>YAC_OCACHE_SKIP_EMPTY<\/code>, <code>YAC_OCACHE_DISABLE<\/code>, <code>YAC_OCACHE_WARMUP_LOOKUPS<\/code>.<\/li>\n<li>The <code>wp_cache_*<\/code> functions and the <code>$wp_object_cache<\/code> global are the standard WordPress object cache API and keep their names.<\/li>\n<li>WP-CLI: the commands stay <code>wp yac status<\/code> \/ <code>wp yac flush<\/code>; the command class is now <code>YAC_OCACHE_CLI_Command<\/code>.<\/li>\n<\/ul>\n\n<h4>1.1.1<\/h4>\n\n<ul>\n<li>A stored <code>false<\/code> is now written to shared memory as <code>0<\/code>. Yac's <code>get()<\/code> returns <code>false<\/code> for both a miss and a stored <code>false<\/code>, so false negatives never survived past the request; with <code>0<\/code> they persist like any other value (readers comparing by value see <code>0<\/code> instead of <code>false<\/code>).<\/li>\n<li>Admin page: top entries grew a Hottest tab (by access count) alongside Largest, shown whenever this yac build reports per-entry hits; the entry inspector gains Hits, Embedded-in-slot and compressed-payload (<code>c_len<\/code>) metadata.<\/li>\n<li>Admin page: entry listings now page through <code>Yac::dump()<\/code> 1000 entries at a time instead of one <code>dump(-1)<\/code>, which could exhaust the PHP memory limit on a busy cache; builds older than 2.4.0 fall back to the single full dump.<\/li>\n<\/ul>\n\n<h4>1.1.0<\/h4>\n\n<ul>\n<li>Dashboard rebuilt around a cache-health verdict: hit-rate ring, cause-attributed metric bars, keys-by-group pie, largest entries by content length, configuration reference and runtime diagnostics.<\/li>\n<li>Key format rework: <code>&lt;prefix&gt;:&lt;group&gt;:&lt;key&gt;<\/code> with no per-blog prefix (default prefix <code>wp<\/code>); over-long keys keep the group verbatim and hash only the key part; <code>switch_to_blog()<\/code> no longer re-namespaces keys.<\/li>\n<li><code>YAC_OCACHE_SKIP_EMPTY<\/code> (on by default): empty <code>get_page_by_path:<\/code> negatives (bot 404 probes) stay request-local instead of filling slots.<\/li>\n<li><code>YAC_OCACHE_DISABLE<\/code> escape hatch forces runtime-only mode.<\/li>\n<\/ul>\n\n<h4>1.0.0<\/h4>\n\n<ul>\n<li>Initial release. Yac-backed object cache with self-deploying drop-in, verbatim keys with hashed fallback, multisite support, and graceful runtime-only fallback.<\/li>\n<li>Storage key prefix configurable via <code>YAC_OCACHE_KEY_PREFIX<\/code> (0-6 chars, default <code>wp_<\/code>); shorter is better.<\/li>\n<li><code>wp_cache_flush()<\/code> calls <code>Yac::flush()<\/code> and clears the entire shared memory on the machine \u2014 the admin page asks for confirmation and states the scope.<\/li>\n<li>Admin dashboard: status bar, key-slot donut, counters, memory-health advice, diagnostics, self-test, memory-contents snapshot, and the configuration reference inside the Environment panel.<\/li>\n<\/ul>","raw_excerpt":"Yac (lock-free shared memory) backed object cache for WordPress. Zero external servers, zero network round trips.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/357611","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=357611"}],"author":[{"embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/laruence"}],"wp:attachment":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=357611"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=357611"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=357611"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=357611"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=357611"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=357611"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}