VOOZH about

URL: https://deepwiki.com/hypervel/components/6.3-stack-store-and-multi-tier-caching

⇱ Stack Store and Multi-Tier Caching | hypervel/components | DeepWiki


Loading...
Last indexed: 7 March 2026 (96fbab)
Menu

Stack Store and Multi-Tier Caching

Purpose and Scope

The Stack Store implements a multi-tier caching strategy that combines multiple cache stores into a layered hierarchy. It provides automatic cache restoration from slower, persistent layers to faster, volatile layers using a read-through pattern, and write-through semantics for consistency across all tiers. The system supports per-layer TTL limits and automatic rollback on write failures.

For information about individual cache drivers (Swoole, Redis, File), see Swoole Store and Memory Management. For cache manager configuration and driver resolution, see Cache Manager and Drivers.

Sources: src/cache/src/StackStore.php1-184

Architecture Overview

The Stack Store operates as a wrapper around an ordered array of cache stores, where stores are arranged from fastest/most-volatile (first) to slowest/most-persistent (last). Common configurations include:

Layer PositionTypical StoreCharacteristics
Layer 1SwooleStoreIn-memory, fastest, volatile
Layer 2RedisStoreNetwork-based, fast, persistent
Layer 3FileStoreDisk-based, slow, persistent

Stack Store Layered Architecture


Sources: src/cache/src/StackStore.php11-18 src/cache/src/StackStore.php107-127 src/cache/src/StackStoreProxy.php10-14

Read Operations and Cache Restoration

Read-Through Pattern

When retrieving a cache key, the StackStore queries each layer sequentially until a value is found. Upon finding a value in a lower layer, it automatically restores the value to all upper layers that missed.

Cache Read Flow with Restoration


The restoration logic is implemented in the getOrRestoreRecord() method at src/cache/src/StackStore.php107-127 It uses the callStoresStacked() helper to create a nested closure chain, where each layer attempts to retrieve the value and, on failure, delegates to the next layer via the $next() closure. When a value is found, putToStore() restores it to all previous layers.

Sources: src/cache/src/StackStore.php107-127 src/cache/src/StackStore.php156-163 tests/Cache/CacheStackStoreTest.php38-53

TTL Calculation During Restoration

When restoring a cache record from a lower layer to an upper layer, the StackStore recalculates the TTL based on the original expiration time:

ttl = record['expiration'] - current_timestamp

This ensures that all layers respect the same absolute expiration time, preventing stale data in upper layers after lower layers expire.

Sources: src/cache/src/StackStore.php137-154 tests/Cache/CacheStackStoreTest.php55-72

Stacked Closure Execution

The callStoresStacked() method at src/cache/src/StackStore.php156-163 implements layer traversal using array_reduce() with array_reverse():

callStoresStacked() Closure Chain


The method signature is:


The $handler closure receives two parameters:

  • Store $store - the current layer's store
  • Closure $next - continuation to invoke the next layer

This pattern allows each layer to short-circuit on success or delegate to deeper layers on failure.

Sources: src/cache/src/StackStore.php156-163 src/cache/src/StackStore.php109-124

Write Operations and Write-Through Pattern

Write-Through Strategy

All write operations (put, putMany, forever, increment, decrement) propagate to all layers simultaneously. This ensures consistency across tiers but requires all layers to succeed or trigger a rollback.

Write Operation Flow


The put() method at src/cache/src/StackStore.php32-40 creates a record array with value and ttl, then delegates to putRecord() which uses callStores() to propagate the write to all layers.

Sources: src/cache/src/StackStore.php32-40 src/cache/src/StackStore.php129-135 src/cache/src/StackStore.php165-183 tests/Cache/CacheStackStoreTest.php101-115

Rollback on Failure

If any layer fails during a write operation, the StackStore executes a rollback by calling forget() on all previously successful layers. This prevents partial writes across the cache hierarchy.

Rollback Mechanism


The rollback is implemented in callStores() at src/cache/src/StackStore.php165-183:


When $handler($store) returns false and $force is not set, the method returns false immediately. If the next layer also fails, the $rollback closure is invoked on all previously successful stores to undo their writes.

Sources: src/cache/src/StackStore.php165-183 src/cache/src/StackStore.php129-135 tests/Cache/CacheStackStoreTest.php133-147

Force Mode for Deletions

The forget() and flush() methods use force: true mode, which continues execution even if individual layers fail. This ensures deletions propagate to all layers regardless of individual failures.

Sources: src/cache/src/StackStore.php86-100 tests/Cache/CacheStackStoreTest.php280-288

StackStoreProxy and Per-Layer TTL Limits

Purpose of StackStoreProxy

The StackStoreProxy wraps individual cache stores and enforces a maximum TTL for that specific layer. This is useful for limiting how long data remains in fast, volatile layers like SwooleStore, while allowing longer TTLs in persistent layers.

TTL Enforcement

The StackStoreProxy::put() method at src/cache/src/StackStoreProxy.php26-33 enforces TTL limits:

StackStoreProxy TTL Logic


Implementation:


Sources: src/cache/src/StackStoreProxy.php26-33

Forever Operations with TTL Limits

The StackStoreProxy::forever() method at src/cache/src/StackStoreProxy.php50-57 converts indefinite storage to TTL-limited storage:


Behavior:

  • If $this->ttl is null: delegates to $store->forever()
  • If $this->ttl is set: converts to $store->put($key, $value, $this->ttl)

This ensures volatile stores like SwooleStore don't retain data indefinitely, even when the application requests permanent storage.

Sources: src/cache/src/StackStoreProxy.php50-57 tests/Cache/CacheStackStoreTest.php399-420

Configuration

Defining a Stack Store

The Stack Store is configured through the cache configuration file. The CacheManager::createStackDriver() method at src/cache/src/CacheManager.php264-280 processes the configuration:


The method resolves each store and wraps it:


Sources: src/cache/src/CacheManager.php264-280

Configuration Options

Each layer in the stores array can be:

Configuration FormatDescriptionExample
StringStore name only, no TTL limit'swoole'
Array with ttlStore name as key, TTL limit in config'redis' => ['ttl' => 60]

The CacheManager resolves each store reference and wraps it in a StackStoreProxy if a TTL limit is specified.

Sources: src/cache/src/CacheManager.php268-277

Record Format and Metadata

Internal Record Structure

The StackStore stores values as structured records containing metadata for TTL calculations and restoration:

FieldTypeDescription
valuemixedThe actual cached value
expirationintUnix timestamp when the record expires
ttlintOriginal TTL in seconds (optional)

Sources: src/cache/src/StackStore.php34-39

Record Normalization

The putToStore() method at src/cache/src/StackStore.php137-154 normalizes record format:

Record Format Transformation in putToStore()


The method ensures all records stored in underlying layers have both value and expiration fields for consistent TTL calculations during restoration.

Sources: src/cache/src/StackStore.php137-154

Implementation Details

Increment and Decrement Operations

Increment and decrement operations retrieve the current record, modify the value, and write back the entire record with preserved metadata:

  1. Retrieve the current record using getOrRestoreRecord()
  2. Calculate the new value: record['value'] + increment_value
  3. Create a new record with the updated value and original metadata
  4. Write the new record to all layers using putRecord()

If the key doesn't exist, it's initialized with the increment/decrement value.

Sources: src/cache/src/StackStore.php53-74 tests/Cache/CacheStackStoreTest.php174-190

Many Operations

The many() method retrieves multiple keys by mapping over the keys array and calling get() for each key individually. This allows each key to benefit from the restoration logic independently.

Similarly, putMany() iterates over the values and calls put() for each key-value pair.

Sources: src/cache/src/StackStore.php27-30 src/cache/src/StackStore.php42-51

Example: Three-Layer Configuration

The test testThreeStores() at tests/Cache/CacheStackStoreTest.php310-358 demonstrates a complete three-layer stack:


Three-Layer Cache Hierarchy Test


Test Coverage:

Sources: tests/Cache/CacheStackStoreTest.php310-358

Error Handling and Invalid Records

The StackStore treats non-array values from underlying stores as invalid records and returns null. This protects against corruption or misconfiguration where stores return unexpected data formats.

Sources: tests/Cache/CacheStackStoreTest.php360-372

Performance Considerations

The Stack Store optimizes read performance by:

  1. Short-circuiting on first hit: Once a value is found in any layer, deeper layers are not queried
  2. Automatic promotion: Frequently accessed items naturally migrate to faster layers through restoration
  3. TTL preservation: Upper layers expire at the same time as lower layers, preventing redundant reads

Write operations trade write latency for consistency by requiring all layers to succeed. This ensures that cache misses on upper layers can always be satisfied by lower layers.

Sources: src/cache/src/StackStore.php107-127 tests/Cache/CacheStackStoreTest.php74-87