VOOZH about

URL: https://deepwiki.com/hypervel/cache/3.3-stack-store

⇱ Stack Store | hypervel/cache | DeepWiki


Loading...
Menu

Stack Store

The Stack Store implements multi-tier caching by combining multiple cache stores in a layered hierarchy. It enables sophisticated caching strategies where frequently accessed data is stored in fast, volatile storage (Layer 1) while maintaining longer-term persistence in slower, distributed storage (Layer 2 and beyond). This composite store automatically handles cache restoration, propagating data from lower tiers to upper tiers on cache misses.

For information about the individual stores that can be combined in a stack, see Database Store, Swoole Store, Redis Store, and File Store. For the overall store architecture, see Store Contracts.

Architecture Overview

The Stack Store combines multiple Store implementations into a single logical cache with tiered access patterns.


Sources: src/StackStore.php1-184 src/StackStoreProxy.php1-83

Configuration

The stack store is configured in the cache configuration file with an array of store names or store configurations with TTL overrides.

Basic Configuration Format

The stack driver configuration accepts a stores array where each element can be either:

  • A string referencing another configured store
  • An associative array with the store name as key and a ttl parameter

Configuration Parameters:

ParameterTypeDescription
driverstringMust be 'stack'
storesarrayOrdered list of store configurations, evaluated from first to last (Layer 1 to Layer N)
stores[].ttlint|nullOptional TTL override in seconds for this layer

Sources: publish/cache.php64-72

Store Composition

The StackStore class accepts an array of Store implementations in its constructor. When instantiated by the CacheManager, each configured store is resolved and optionally wrapped in a StackStoreProxy.


The StackStoreProxy wraps individual stores to enforce maximum TTL limits for that layer. If a TTL is specified in the configuration, writes to that layer will never exceed the configured TTL, regardless of what the application requests.

Sources: src/StackStore.php13-18 src/StackStoreProxy.php10-14

Operation Patterns

Read Operations with Cache Restoration

The Stack Store implements an intelligent cache restoration mechanism through the getOrRestoreRecord() method. When a key is requested:

  1. Query Layer 1 (fastest store)
  2. If miss, query Layer 2
  3. If hit on Layer 2, restore the value to Layer 1
  4. Continue through all layers until a hit or complete miss
  5. Return the value (or null if not found in any layer)

The restoration logic is implemented using a stacked closure pattern in callStoresStacked(), which iterates through stores from first to last, checking each one and propagating values upward.

Sources: src/StackStore.php20-25 src/StackStore.php107-127

Write Operations (Write-Through Strategy)

Write operations propagate through all layers using a write-through strategy. All stores must successfully complete the write, or the entire operation is rolled back.


The write-through logic ensures consistency across all layers. If any layer fails after previous layers succeeded, the callStores() method invokes the rollback closure to undo writes on earlier layers.

Sources: src/StackStore.php32-40 src/StackStore.php129-135 src/StackStore.php165-183

Delete Operations

Delete operations propagate through all layers regardless of individual success or failure when the force parameter is set to true. This ensures that a key is removed from all tiers even if one tier reports failure.


Sources: src/StackStore.php86-92 src/StackStore.php94-100

TTL Enforcement with StackStoreProxy

The StackStoreProxy class wraps a Store implementation to enforce a maximum TTL for that layer. This ensures fast, volatile stores don't accumulate long-lived data.

TTL Override Logic

OperationTTL SpecifiedBehavior
put()YesUses minimum of requested TTL and proxy TTL
put()NoDelegates to wrapped store normally
forever()YesConverts to put() with proxy TTL
forever()NoDelegates to wrapped store normally
Other opsN/ADelegates to wrapped store

Example: If Layer 1 has ttl: 3 and the application calls put('key', 'value', 60), Layer 1 will store the value for 3 seconds, while Layer 2 stores it for 60 seconds.

Sources: src/StackStoreProxy.php26-33 src/StackStoreProxy.php50-57

Record Format and Normalization

The Stack Store uses a normalized record format internally to track values along with their expiration metadata across all layers.

Internal Record Structure

[
 'value' => mixed, // The actual cached value
 'expiration' => int, // Unix timestamp when record expires
 'ttl' => int, // Seconds until expiration (alternative to expiration)
]

The putToStore() method normalizes records before writing to individual stores, converting between TTL and expiration timestamps as needed:

  1. If neither expiration nor ttl is present, stores the record forever
  2. Calculates expiration from ttl if only ttl is present
  3. Calculates ttl from expiration if only expiration is present
  4. Normalizes to {value, expiration} format for storage

Sources: src/StackStore.php137-154

Implementation Details

Stacked Closure Pattern

The callStoresStacked() method implements a functional programming pattern that processes stores in sequence while maintaining a continuation (next function) for each layer.

callStoresStacked(handler, bottomLayer):
 reversed_stores = reverse(stores)
 
 stack = bottomLayer
 for each store in reversed_stores:
 previous_stack = stack
 stack = () => handler(store, previous_stack)
 
 return stack()

This pattern enables:

  • Sequential processing with early termination
  • Passing results from lower layers to upper layers
  • Rollback on failure using the continuation

Sources: src/StackStore.php156-163

Atomic Increment/Decrement

The increment and decrement operations maintain atomicity by:

  1. Retrieving the current record (with restoration if needed)
  2. Calculating the new value
  3. Writing the updated record to all layers
  4. Returning the new value or false on failure

If the key doesn't exist, the increment operation initializes it with the increment value and stores it forever.

Sources: src/StackStore.php53-69 src/StackStore.php71-74

Class Reference

StackStore

Namespace: Hypervel\Cache
Implements: Hypervel\Cache\Contracts\Store
Location: src/StackStore.php11

Constructor:

  • __construct(array $stores) - Accepts array of Store implementations

Public Methods:

  • get(string $key): mixed - Retrieve with auto-restoration
  • many(array $keys): array - Retrieve multiple keys
  • put(string $key, mixed $value, int $seconds): bool - Write-through to all layers
  • putMany(array $values, int $seconds): bool - Batch write-through
  • increment(string $key, int $value = 1): bool|int - Atomic increment
  • decrement(string $key, int $value = 1): bool|int - Atomic decrement
  • forever(string $key, mixed $value): bool - Store permanently in all layers
  • forget(string $key): bool - Remove from all layers
  • flush(): bool - Clear all layers
  • getPrefix(): string - Returns empty string (prefix handled by wrapped stores)

StackStoreProxy

Namespace: Hypervel\Cache
Implements: Hypervel\Cache\Contracts\Store
Location: src/StackStoreProxy.php10

Constructor:

  • __construct(Store $store, ?int $ttl = null) - Wraps store with optional TTL limit

Behavior:

  • Delegates all operations to wrapped store
  • Enforces maximum TTL on put() and forever() operations
  • Transparent proxy for all other operations

Sources: src/StackStore.php11-184 src/StackStoreProxy.php10-83