diff --git a/reference/yac/book.xml b/reference/yac/book.xml
index c0aba1418ee6..eb137f716dcf 100644
--- a/reference/yac/book.xml
+++ b/reference/yac/book.xml
@@ -25,7 +25,8 @@
path, a read is essentially a hash lookup in shared memory. As a
result, Yac is extremely fast, with microsecond-level read
latency, and its throughput can scale with the number of
- workers as long as writes are spread across keys.
+ workers as long as writes are spread across keys; see the
+ benchmarks.
Because Yac trades correctness guarantees for speed and throughput,
@@ -36,8 +37,9 @@
As of yac 2.4.0, small scalar values —
- NULL, booleans, integers, short strings of up to
- 7 bytes and empty arrays — are stored directly inside the hash slot
+ NULL, booleans, most integers (those that fit in
+ 60 signed bits on 64-bit builds), strings of up to 7 bytes and empty
+ arrays — are stored directly inside the hash slot
instead of in a separate value block ("embedded values"), which
removes the value-memory allocation and block copy on every access
and significantly improves performance while reducing memory usage.
@@ -54,6 +56,7 @@
&reference.yac.setup;
+ &reference.yac.memory;
&reference.yac.constants;
&reference.yac.yac;
diff --git a/reference/yac/configure.xml b/reference/yac/configure.xml
deleted file mode 100644
index d003246fda54..000000000000
--- a/reference/yac/configure.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-
-
-
-
- &reftitle.install;
-
-
- &pecl.info;
- &url.pecl.package;yac
-
-
-
-
-
-
-
diff --git a/reference/yac/constants.xml b/reference/yac/constants.xml
index a6c06d0eefbe..279d23bcde75 100644
--- a/reference/yac/constants.xml
+++ b/reference/yac/constants.xml
@@ -13,6 +13,7 @@
+ The version of the extension as a string.
@@ -23,7 +24,8 @@
- Max length of a key could be, it is 48 bytes.
+ Maximum length of a key in bytes: 48. An instance prefix counts
+ against this limit.
@@ -34,6 +36,9 @@
+ Maximum length of a value before serialization, in bytes:
+ 67,108,863 ((1 << 26) - 1). Larger values
+ are rejected.
@@ -44,6 +49,8 @@
+ Maximum size of a stored entry, in bytes: 1,048,576 (1M). Values
+ that do not fit even after compression are rejected.
@@ -54,7 +61,7 @@
- Use php serialize as serializer
+ Use the PHP serialize format as serializer (the default).
@@ -65,7 +72,8 @@
- Use json as serializer(requrie --enable-json)
+ Use JSON as the serializer. Requires the extension to be built with
+ .
@@ -76,7 +84,8 @@
- Use igbinary as serializer(require --enable-igbinary)
+ Use igbinary as the serializer. Requires the extension to be built
+ with .
@@ -87,7 +96,8 @@
- Use msgpack as serializer(require --enable-msgpack)
+ Use msgpack as the serializer. Requires the extension to be built
+ with .
@@ -98,7 +108,7 @@
- Which serialzier is yac used
+ The serializer currently used by the extension.
diff --git a/reference/yac/ini.xml b/reference/yac/ini.xml
index c9bf9f9551ba..cbf54bbd01d1 100644
--- a/reference/yac/ini.xml
+++ b/reference/yac/ini.xml
@@ -19,7 +19,7 @@
yac.compress_threshold
- -1
+ 4KINI_SYSTEM
@@ -43,7 +43,7 @@
yac.keys_memory_size
- 4M
+ 8MINI_SYSTEM
@@ -76,10 +76,17 @@
Serialized values larger than this number of bytes are
- compressed before being stored (currently with LZ4). Set it to
- -1 (the default) to disable compression
- entirely. Compressing large values saves shared memory at the cost
- of some CPU on both store and retrieve.
+ compressed (with LZ4, since Yac 2.4.0) before being stored.
+ Values above the 1M stored entry limit
+ (YAC_MAX_RAW_COMPRESSED_LEN) are always
+ compressed regardless of this setting, because they cannot be
+ stored uncompressed. The default of 4K
+ enables compression; -1 disables it for
+ values below the stored entry limit, and other positive values
+ are clamped to the
+ 1024..1M range.
+ Compressing large values saves shared memory at the cost of
+ some CPU on both store and retrieve.
@@ -128,12 +135,30 @@
- Amount of shared memory used for the hash slots that hold keys
- and bookkeeping. Each slot is a fixed-size structure, so this value
- determines how many items can be tracked at once. Defaults to
- 4M. Yac splits this area into segments; the
- segment size is 4M, so this value must be a multiple of
- 4M.
+ Amount of shared memory for the table of keys. It caps how many
+ entries can exist at once — the default of
+ 8M gives around 65,536 entries. Raise this
+ when the hit rate drops and kicks climb; see
+ for how a full table
+ behaves and when that actually matters.
+
+
+
+
+
+ yac.values_memory_size
+ string
+
+
+
+ Amount of shared memory for storing values. Values are allocated
+ in a bump allocator over segments of 4M each; when no segment has
+ room for a new value, an allocator cursor wraps and the oldest
+ values are silently overwritten ("recycled"). Their reads then
+ degrade to misses, detected by an integrity guard. Enlarge this
+ if recycles climb while slots still have room,
+ or enable yac.compress_threshold
+ to shrink large payloads. See .
@@ -155,21 +180,6 @@
-
-
- yac.values_memory_size
- string
-
-
-
- Amount of shared memory used to store the actual values.
- Defaults to 64M. Yac allocates this area in
- segments of 4M each, so this value must be a multiple of
- 4M. When the area is full, least recently used
- entries are kicked to make room for new ones.
-
-
-
diff --git a/reference/yac/memory.xml b/reference/yac/memory.xml
new file mode 100644
index 000000000000..fce411d067f6
--- /dev/null
+++ b/reference/yac/memory.xml
@@ -0,0 +1,245 @@
+
+
+
+
+ Memory Management
+
+
+ Yac keeps its data in two independent shared-memory pools, configured
+ with
+ yac.keys_memory_size
+ and
+ yac.values_memory_size.
+ They fill up and free up in different ways, so it helps to know which
+ pool a symptom belongs to before touching either knob.
+
+
+
+ What Each Pool Holds
+
+ The two pools store different halves of an entry. The key pool holds
+ the keys: one slot per cached key, carrying the key itself (up to 48
+ bytes) together with its hash, TTL, hit count and last-access time;
+ the value is only referenced through a pointer to the value pool. The
+ value pool holds the values: every stored value occupies one block of
+ serialized bytes — the compressed form when the entry went through
+ yac.compress_threshold.
+
+
+ The one exception is embedded values: tiny scalars — &null;,
+ &true;, &false;, most integers (those that fit in 60 signed bits on
+ 64-bit builds), strings of up to 7 bytes and empty arrays — are
+ stored directly inside the slot, in the pointer that would otherwise
+ reference a block. Such values occupy no space in the value pool at
+ all; only their key does.
+
+
+ As a rough sizing guide:
+
+
+
+ the key pool holds around 8,000 keys per MB, so size
+ yac.keys_memory_size
+ as the number of distinct keys divided by 8,000 per MB, rounded up
+ — the default 8M holds around 64,000 keys;
+
+
+
+
+ the value pool must hold every value that may still be read, so
+ size
+ yac.values_memory_size
+ as the number of live values times their average serialized size
+ (after compression), and allow roughly twice that: the pool is a
+ ring, and a value only dies once the allocator cursor comes back
+ around to overwrite it.
+
+
+
+ Embedded values occupy a slot like any other entry, but consume no
+ space in the value pool, so leave them out of the second calculation.
+
+
+
+
+ The key pool (slots)
+
+ yac.keys_memory_size
+ holds a fixed-size table of slots — the default of
+ 8M gives around 65,536 slots. Each
+ stored key occupies exactly one slot, so this pool caps the number
+ of entries that can exist at once; unlike the value pool, slots are
+ never individually freed. An expired slot — one past its TTL, or the
+ tombstone left by Yac::delete — is recycled
+ for free when a new key needs it. Only when all four candidate slots
+ of a probe path hold live entries is one of them evicted to make
+ room — one kick (the
+ kicks counter of Yac::info).
+
+
+ The eviction picks among the four live candidates of the colliding
+ probe path only:
+
+
+
+ the least recently used one (the oldest
+ atime) is evicted;
+
+
+
+
+ on a tie the least-hit entry, then the earliest probe position.
+
+
+
+
+
+ A common point of confusion: slots_used reaching
+ slots_size is not an error
+ condition. A cache whose working set of keys is larger than the slot
+ table simply runs at 100% occupancy from then on, evicting and
+ re-inserting as needed. The only thing that says whether the key
+ pool is sized correctly is the hit rate
+ (hits / (hits + miss), computed over the deltas
+ between two Yac::info snapshots rather than
+ the lifetime average). A high kicks count on its
+ own means nothing is wrong — the key distribution is simply not
+ uniform and some probe paths collide more than others. Only when the
+ hit rate andkicks are both
+ bad is the table too small for the key set, and the remedy is a
+ bigger yac.keys_memory_size.
+
+
+ A second consequence of slots never being freed: entries with no TTL
+ (ttl = 0) that are never read again keep occupying
+ a slot until an eviction happens to pick them. If an application
+ stores large amounts of such one-shot data, give those entries a TTL
+ so they expire and can be recycled without displacing live entries,
+ or size the key pool for the full key set.
+
+
+
+
+ The value pool (segments)
+
+ yac.values_memory_size
+ is split into segments of 4M each, managed as rings: writes advance a
+ per-segment cursor and space is never freed per entry. When an
+ allocation no longer fits, the cursor wraps back to the start of a
+ segment — one recycle (the
+ recycles counter of
+ Yac::info). A recycle does not invalidate
+ the segment at once: overwritten values stay readable until the
+ wrapped cursor actually overwrites them, at which point their reads
+ fail the integrity guard and turn into misses.
+
+
+ Two sizes matter for this pool: the total
+ yac.values_memory_size
+ must hold the working set of live values, and a single entry can
+ hold at most 1 MB as stored
+ (YAC_MAX_RAW_COMPRESSED_LEN). Values larger
+ than that are therefore always compressed before being stored; a
+ value that cannot shrink below 1 MB — most often because it is
+ random data — is rejected and bumps the
+ fails counter. The absolute size limit on the
+ value itself is much higher: serialized values above 64 MB
+ (YAC_MAX_VALUE_RAW_LEN, that is
+ (1 << 26) - 1 bytes) are rejected
+ outright.
+
+
+
+
+ Sizing and what to watch
+
+ Start with the defaults and watch the counters of
+ Yac::info — they accumulate from
+ start_time, so compare two snapshots taken some
+ time apart:
+
+
+
+
+ hit rate healthy (say >= 90%): the cache is fine; nothing to do,
+ whatever the other counters show;
+
+
+
+
+ hit rate low and kicks climbing: the key pool is
+ too small for the key set — live entries get evicted before they are
+ re-read. Raise
+ yac.keys_memory_size;
+
+
+
+
+ recycles frequent: this is a real problem, not a
+ benign counter. A recycle means the value allocator has wrapped and is
+ about to overwrite entries — anything overwritten dies before it could
+ be re-read, so the bytes spent storing it were wasted and the hit rate
+ suffers. The value pool is too small for the volume of live data. In
+ order of impact:
+
+
+
+ give entries a TTL. Values written with ttl = 0
+ stay live forever, so they keep occupying the pool and force the
+ cursor to wrap sooner. A TTL bounds how long each entry may live,
+ shrinking the live working set the pool has to hold;
+
+
+
+
+ raise
+ yac.values_memory_size
+ so the pool holds the whole live value set (remember to budget
+ roughly twice the live footprint — a value only dies once the
+ cursor comes back around to overwrite it);
+
+
+
+
+ store less per entry: lower
+ yac.compress_threshold
+ if it is set above the 1024 minimum, so large
+ payloads are compressed, and trim values that do not need to be
+ cached in full;
+
+
+
+
+
+
+
+ fails growing: values that could not be stored,
+ most often a single value larger than the 1 MB stored-size limit
+ even after compression — split the value.
+
+
+
+
+
+
+
+
diff --git a/reference/yac/yac/add.xml b/reference/yac/yac/add.xml
index 5025732e38fd..8da46466dc8e 100644
--- a/reference/yac/yac/add.xml
+++ b/reference/yac/yac/add.xml
@@ -11,7 +11,7 @@
&reftitle.description;
publicboolYac::add
- stringarraykeys
+ stringarraykeymixedvalueintttl0
@@ -31,7 +31,7 @@
&reftitle.parameters;
- keys
+ key
A string key, or an array of
@@ -45,7 +45,7 @@
The value to store. Every PHP type except resource can
be stored. Only used in the single-key form; when
- keys is an array, this argument is instead the
+ key is an array, this argument is instead the
optional ttl.
@@ -68,21 +68,6 @@
Returns &true; on success, &false; on failure. A store is also rejected
(returning &false;) when the key already exists and has not expired.
-
-
- Yac stores entries without locks. Under heavy contention a store can
- fail transiently; if the value must eventually be stored, retry:
-
-add("key", "value")) {
- // retry on transient failure
-}
-?>
-]]>
-
-
-
@@ -96,11 +81,31 @@ $yac = new Yac();
var_dump($yac->add("foo", "bar")); // bool(true)
var_dump($yac->add("foo", "baz")); // bool(false): "foo" already exists
+?>
+]]>
+
+
+
+ Adding an entry with a TTL
+
+add("short-lived", "value", 5);
sleep(6);
var_dump($yac->get("short-lived")); // bool(false): expired
+?>
+]]>
+
+
+
+ Adding multiple entries at once
+
+ value pairs with one call, with a ttl
$yac->add(array("a" => 1, "b" => 2), 60);
diff --git a/reference/yac/yac/delete.xml b/reference/yac/yac/delete.xml
index 68d3ad88a82f..524a958f4e1c 100644
--- a/reference/yac/yac/delete.xml
+++ b/reference/yac/yac/delete.xml
@@ -11,7 +11,7 @@
&reftitle.description;
publicboolYac::delete
- stringarraykeys
+ stringarraykeyintdelay0
@@ -38,7 +38,7 @@
&reftitle.parameters;
- keys
+ key
A string key, or an array of keys to be
@@ -96,13 +96,33 @@ var_dump($yac->delete("never")); // bool(false): was never stored
var_dump($yac->info()["slots_used"]); // int(1)
print_r($yac->dump()); // "foo" is still listed; its ttl
// is in the past
+?>
+]]>
+
+
+
+ Delayed deletion
+
+set("tmp", "value");
var_dump($yac->delete("tmp", 60)); // bool(true)
+?>
+]]>
+
+
+
+ Deleting multiple keys at once
+
+set("tmp", "value");
-// deleting several keys at once returns true only when every key
-// was present
+// returns true only when every key was present
var_dump($yac->delete(array("tmp", "nope"))); // bool(false): "nope" missing
?>
]]>
diff --git a/reference/yac/yac/dump.xml b/reference/yac/yac/dump.xml
index afd8377297a9..68b3843b335f 100644
--- a/reference/yac/yac/dump.xml
+++ b/reference/yac/yac/dump.xml
@@ -134,8 +134,9 @@
hits
A per-entry hit counter, bumped on each successful
- Yac::get, reset when the entry is overwritten,
- deleted or expires (as of yac 2.4.0).
+ Yac::get and reset when the entry is
+ overwritten by a new Yac::set or
+ Yac::add (as of yac 2.4.0).
@@ -232,6 +233,11 @@ Array
set("key$i", $i);
+}
+
$page_size = 100;
$page_num = 2;
diff --git a/reference/yac/yac/get.xml b/reference/yac/yac/get.xml
index 55ccf9cc72d4..fd8cd0548de4 100644
--- a/reference/yac/yac/get.xml
+++ b/reference/yac/yac/get.xml
@@ -11,7 +11,7 @@
&reftitle.description;
publicmixedYac::get
- stringarraykeys
+ stringarraykeymixeddefault&null;
@@ -23,7 +23,7 @@
&reftitle.parameters;
- keys
+ key
A string key, or an array of keys.
@@ -80,6 +80,16 @@ $yac = new Yac();
$yac->set("foo", "bar");
var_dump($yac->get("foo")); // string(3) "bar"
var_dump($yac->get("missing")); // bool(false): a miss
+?>
+]]>
+
+
+
+ Telling a stored false apart from a miss
+
+get("flag")); // bool(false): the stored value
var_dump($yac->get("missing", false)); // bool(false): a miss, same shape
var_dump($yac->get("flag", "__NONE__")); // bool(false): the stored value
var_dump($yac->get("missing", "__NONE__")); // string(8) "__NONE__": a miss
+?>
+]]>
+
+
+
+ Getting multiple keys at once
+
+set("foo", "bar");
$yac->set("foo2", "bar2");
+
+// with an array of keys, only the found keys are present in the result
var_dump($yac->get(array("foo", "foo2", "missing")));
// array(2) { ["foo"]=> string(3) "bar" ["foo2"]=> string(4) "bar2" }
?>
diff --git a/reference/yac/yac/info.xml b/reference/yac/yac/info.xml
index 8c4e9c3978e3..572c5fc14f4d 100644
--- a/reference/yac/yac/info.xml
+++ b/reference/yac/yac/info.xml
@@ -134,18 +134,18 @@ print_r($yac->info());
46137344
- [slots_memory_size] => 4194304
- [values_memory_size] => 41943040
+ [memory_size] => 75497472
+ [slots_memory_size] => 8388608
+ [values_memory_size] => 67108864
[segment_size] => 4194304
- [segment_num] => 10
+ [segment_num] => 16
[miss] => 0
[hits] => 0
[fails] => 0
[kicks] => 0
[recycles] => 0
[start_time] => 1725955200
- [slots_size] => 32768
+ [slots_size] => 65536
[slots_used] => 1
)
]]>
diff --git a/reference/yac/yac/set.xml b/reference/yac/yac/set.xml
index 5ab42a9e5822..c6d50eb6b02d 100644
--- a/reference/yac/yac/set.xml
+++ b/reference/yac/yac/set.xml
@@ -11,7 +11,7 @@
&reftitle.description;
publicboolYac::set
- stringarraykeys
+ stringarraykeymixedvalueintttl0
@@ -30,7 +30,7 @@
&reftitle.parameters;
- keys
+ key
A string key, or an array of
@@ -44,7 +44,7 @@
The value to store. Every PHP type except resource can
be stored. Only used in the single-key form; when
- keys is an array, this argument is instead the
+ key is an array, this argument is instead the
optional ttl.
@@ -79,11 +79,31 @@ $yac = new Yac();
$yac->set("foo", "bar"); // store a single value
$yac->set("foo", "baz"); // overwrite the existing entry
+?>
+]]>
+
+
+
+ Setting an entry with a TTL
+
+set("short-lived", "value", 5);
sleep(6);
var_dump($yac->get("short-lived")); // bool(false): expired
+?>
+]]>
+
+
+
+ Setting multiple entries at once
+
+ value pairs with one call
$yac->set(array("a" => 1, "b" => 2));