{"id":46116688,"url":"https://github.com/thamtech/yii2-refresh-ahead-cache","last_synced_at":"2026-03-01T23:30:25.248Z","repository":{"id":57067552,"uuid":"191236660","full_name":"thamtech/yii2-refresh-ahead-cache","owner":"thamtech","description":"A Refresh-Ahead Cache Strategy for Yii2","archived":false,"fork":false,"pushed_at":"2020-12-09T19:17:30.000Z","size":60,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-05-20T19:43:21.961Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/thamtech.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null},"funding":{"liberapay":"thamtech"}},"created_at":"2019-06-10T19:56:56.000Z","updated_at":"2020-12-09T19:17:32.000Z","dependencies_parsed_at":"2022-08-24T14:54:06.701Z","dependency_job_id":null,"html_url":"https://github.com/thamtech/yii2-refresh-ahead-cache","commit_stats":null,"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"purl":"pkg:github/thamtech/yii2-refresh-ahead-cache","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thamtech%2Fyii2-refresh-ahead-cache","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thamtech%2Fyii2-refresh-ahead-cache/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thamtech%2Fyii2-refresh-ahead-cache/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thamtech%2Fyii2-refresh-ahead-cache/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/thamtech","download_url":"https://codeload.github.com/thamtech/yii2-refresh-ahead-cache/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thamtech%2Fyii2-refresh-ahead-cache/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29987692,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-01T22:42:38.399Z","status":"ssl_error","status_checked_at":"2026-03-01T22:41:51.863Z","response_time":124,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-03-01T23:30:24.628Z","updated_at":"2026-03-01T23:30:25.236Z","avatar_url":"https://github.com/thamtech.png","language":"PHP","funding_links":["https://liberapay.com/thamtech"],"categories":[],"sub_categories":[],"readme":"Yii2 Refresh-Ahead Cache\n========================\n\nYii2 Refresh-Ahead Cache can decorate Yii2 cache components or other components\nto implement a refresh-ahead cache strategy.\n\nThe Refresh-Ahead cache strategy (also called Read-Ahead) is used to refresh\ncached data before it expires. By refreshing cached data before it expires\n(and doing it asynchronously), end-users never have to suffer the delay of\nthe refresh. Furthermore, it can also help avoid a\n[Cache Stampede](https://en.wikipedia.org/wiki/Cache_stampede).\n\nFor license information check the [LICENSE](LICENSE.md)-file.\n\nInstallation\n------------\n\nThe preferred way to install this extension is through [composer](http://getcomposer.org/download/).\n\n```\nphp composer.phar require --prefer-dist thamtech/yii2-read-ahead-cache\n```\n\nor add\n\n```\n\"thamtech/yii2-read-ahead-cache\": \"*\"\n```\n\nto the `require` section of your `composer.json` file.\n\nUsage\n-----\n\n### Decorate Cache Component\n\nYou can add Refresh-Ahead capability to your application's cache component\nby attaching the `RefreshAheadCacheBehavior`. For example, in your application\nconfiguration:\n\n```php\n\u003c?php\n[\n    'components' =\u003e [\n        'cache' =\u003e [\n            'class' =\u003e 'yii\\redis\\Cache',\n            'as refreshAhead' =\u003e 'thamtech\\caching\\refreshahead\\RefreshAheadCacheBehavior',\n        ],\n    ],\n];\n```\n\nThere are a number of parameters you can configure if you declare the behavior\nas a configuration array:\n\n```php\n\u003c?php\n[\n    'components' =\u003e [\n        'redisCache' =\u003e [\n            'class' =\u003e 'yii\\redis\\Cache',\n        ],\n        'appMutex' =\u003e [\n            'class' =\u003e 'yii\\redis\\Mutex',\n        ],\n        'cache' =\u003e [\n            'class' =\u003e 'yii\\caching\\FileCache',\n            'as refreshAhead' =\u003e [\n                'class' =\u003e 'thamtech\\caching\\refreshahead\\RefreshAheadCacheBehavior',\n                'refreshTimeoutCache' =\u003e 'redisCache',\n                'refreshAheadFactor' =\u003e 0.5,\n                'refreshTimeoutKeySuffix' =\u003e 'refresh-ahead-timeout',\n                'mutex' =\u003e 'appMutex',\n            ],\n        ],\n    ],\n];\n```\n\n### Decorate any Component\n\nBy default, `RefreshAheadCacheBehavior` assumes that its owner (the component\nit is attached to as a\n[behavior](https://www.yiiframework.com/doc/guide/2.0/en/concept-behaviors))\nis a cache component that will be used for both storage of the cached data\nas well as the storage of the refresh timeout key. However, you can specify\nwhich cache components to use in these cases, so you do not have to attach\nRefreshAheadCacheBehavior to a Cache component. You can attach it to any Yii\n[Component](https://www.yiiframework.com/doc/guide/2.0/en/concept-components)\nprovided that you specify the cache component(s) to use in the behavior's\nconfiguration.\n\n```php\n\u003c?php\n$dataManager = Yii::createObject([\n    'class' =\u003e DataManager::class,\n    'as refreshAhead' =\u003e [\n        'class' =\u003e 'thamtech\\caching\\refreshahead\\RefreshAheadCacheBehavior',\n        \n        // use application 'cache' component for data storage\n        'dataCache' =\u003e 'cache',\n        \n        // use application 'cache' component for refresh timeout key storage\n        'refreshTimeoutCache' =\u003e 'cache',\n    ],\n]);\n```\n\nIf both data values and refresh timeout keys will be stored in the same cache\ncomponent, you can set the single `cache` property as a shortcut for setting\nboth `dataCache` and `refreshTimeoutCache`. The following configuration is\nequivalent to the one above:\n\n```php\n\u003c?php\n$dataManager = Yii::createObject([\n    'class' =\u003e DataManager::class,\n    'as refreshAhead' =\u003e [\n        'class' =\u003e 'thamtech\\caching\\refreshahead\\RefreshAheadCacheBehavior',\n        \n        // use application 'cache' component for both data storage and refresh\n        // timeout key storage\n        'cache' =\u003e 'cache',\n    ],\n]);\n```\n\n### Drop-In Replacement for getOrSet\n\nRefreshAheadCacheBehavior adds a `getRefreshOrSet()` method to the cache or\nany other component it decorates. This method has the same signature as\n[getOrSet()](https://www.yiiframework.com/doc/api/2.0/yii-caching-cache#getOrSet%28%29-detail),\nso you can perform a drop-in replacement where you currently use `getOrSet()`.\nFor example,\n\n```php\n\u003c?php\n$data = $cache-\u003egetOrSet($key, function ($cache) {\n    return $this-\u003ecalculateSomething();\n});\n\n// drop-in replacement:\n$data = $cache-\u003egetRefreshOrSet($key, function ($cache) {\n    return $this-\u003ecalculateSomething();\n});\n\n\n// with specified $duration and $dependency\n$data = $cache-\u003egetOrSet($key, function ($cache) {\n    return $this-\u003ecalculateSomething();\n}, $duration, $dependency);\n\n// drop-in replacement\n$data = $cache-\u003egetRefreshOrSet($key, function ($cache) {\n    return $this-\u003ecalculateSomething();\n}, $duration, $dependency);\n```\n\nThe Refresh Ahead strategy attempts to\n[add()](https://www.yiiframework.com/doc/api/2.0/yii-caching-cache#add%28%29-detail)\na refresh timeout key in the refresh timeout cache component with a duration\nshorter than the requested `$duration` (half of `$duration` by default). If the\nadd is successful, it means any previous refresh timeout key had expired and the\ncached data is due for a refresh.\n\nWhen `getRefreshOrSet()` is called with a single callable parameter like the\nexamples above, the Refresh Ahead strategy calls the callable, stores the\nreturned value into the data cache component using the specified `$duration`,\nand returns that value (into the `$data` variable in the examples above).\n\nOn the other hand, if the attempt to add the refresh timeout key was not\nsuccessful, it means the key already exists and is not expired, and therefore,\nno refresh is currently called for. The Refresh Ahead strategy uses `$key` to\nlook up the cached value in the data cache component and returns it if it finds\nit. If it doesn't find it in the data cache (perhaps the cache was flushed\nor the key was evicted), then the callable is invoked to calculate the new\nvalue. The new value is set in the data cache component using the specified\n`$duration` and the value is returned.\n\n### Typical Usage\n\nThe usage can be further improved if you can support asynchronous refreshing.\nIn order to do this, we must provide the Refresh Ahead strategy with *two*\ncallables: one to trigger a refresh asynchronously and one that will refresh\nthe data synchronously and return the result.\n\nThe usage is similar to the examples above, except that the second parameter\nto `getRefreshOrSet()` will be a `GeneratorInterface` object or configuration\narray instead of a single callable. For example,\n\n```php\n\u003c?php\n$data = $cache-\u003egetRefreshOrSet($key, [\n    // called by Refresh Ahead strategy if the data is still cached, but it is\n    // time to refresh it\n    'refresh' =\u003e function ($cache) {\n        // queue the refresh task to be run at a later time\n        // return `true` when task is queued, `false` if the task was not queued\n        // (in which case the `refresh` callable will be called again in a\n        // subsequent request)\n        return $this-\u003etaskQueue-\u003eappend('calculateSomething');\n    },\n    \n    // called by Refresh Ahead strategy if the data is not in cache (it may\n    // have expired before it could be refreshed, or it could have been\n    // flushed or evicted, etc.)\n    'generate' =\u003e function ($cache) {\n        return $this-\u003ecalculateSomething();\n    }\n], $duration, $dependency);\n```\n\nIf you've configured a `mutex` component in the `RefreshAheadCacheBehavior`,\nyou can specify a timeout for acquiring a lock using the `mutexLockTimeout`\nproperty:\n\n```php\n\u003c?php\n$generator = [\n    'refresh' =\u003e function ($cache) {\n        return $this-\u003etaskQueue-\u003eappend('calculateSomething');\n    },\n    'generate' =\u003e function ($cache) {\n        return $this-\u003ecalculateSomething();\n    },\n    // Attempt to acquire a mutex lock for 12 seconds before invoking\n    // the 'generate' callback. If the lock is acquired, Refresh Ahead will\n    // check to see if the value is in the data cache once more before invoking\n    // 'generate', in case another process was generating and caching the value\n    // already.\n    'mutexLockTimeout' =\u003e 12,\n];\n\n$data = $cache-\u003egetRefreshOrSet($key, $generator, $duration, $dependency);\n```\n\nBy configuring a `mutex` component on the behavior and setting the\n`mutexLockTimeout` as a property on the generator, the Refresh\nAhead strategy will attempt to acquire a lock to invoke the `generate` callable.\nThis way, if multiple requests come in around the same time when the value\nhas expired (a [Cache Stampede](https://en.wikipedia.org/wiki/Cache_stampede)),\nthe process that first acquires the lock will compute the value and store it in\ncache. The other processes wait for the lock to be released. Once the first\nprocess releases the lock, the value has been computed and is in cache, so the\nother processes will check for it in cache, find it, and return it without\nhaving to invoke the `generate` callable.\n\nIf your task queue can run asynchronously, such as in a cron task, you can\nuse the same `$generator` in a call to `generateAndSet()` to complete\nthe refresh process and update the cache value in the background. For example,\n\n```php\n\u003c?php\n// using the same parameters defined in the previous example:\n$data = $cache-\u003egenerateAndSet($key, $generator, $duration, $dependency);\n```\n\nThis will invoke the `generate` callable (if the item hasn't already been cached\nby another invocation of `generate` at the same time), and sets the result in\nthe cache before returning it.\n\n\nSee Also\n--------\n\n* [Cache Stampede](https://en.wikipedia.org/wiki/Cache_stampede)\n\n* [Cache Concurrency Control - Case Study](https://www.braze.com/perspectives/article/cache-concurrency-control)\n\n* [All things caching](https://medium.com/datadriveninvestor/all-things-caching-use-cases-benefits-strategies-choosing-a-caching-technology-exploring-fa6c1f2e93aa)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthamtech%2Fyii2-refresh-ahead-cache","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthamtech%2Fyii2-refresh-ahead-cache","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthamtech%2Fyii2-refresh-ahead-cache/lists"}