{"id":13624026,"url":"https://github.com/sobstel/metaphore","last_synced_at":"2025-04-15T20:33:06.341Z","repository":{"id":10100518,"uuid":"12163046","full_name":"sobstel/metaphore","owner":"sobstel","description":"Cache slam defense using a semaphore to prevent dogpile effect.","archived":false,"fork":false,"pushed_at":"2024-02-24T11:42:47.000Z","size":126,"stargazers_count":100,"open_issues_count":2,"forks_count":9,"subscribers_count":11,"default_branch":"master","last_synced_at":"2025-04-11T15:04:33.219Z","etag":null,"topics":["caching","php"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sobstel.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":null,"patreon":null,"open_collective":null,"ko_fi":"sobstel","tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"lfx_crowdfunding":null,"custom":null}},"created_at":"2013-08-16T16:33:54.000Z","updated_at":"2025-03-15T20:58:46.000Z","dependencies_parsed_at":"2024-06-18T21:24:44.675Z","dependency_job_id":"a4f479d3-7c73-46a4-927d-fc64965926f1","html_url":"https://github.com/sobstel/metaphore","commit_stats":{"total_commits":131,"total_committers":9,"mean_commits":"14.555555555555555","dds":"0.30534351145038163","last_synced_commit":"e76162c8012a7ffdbbc0202256c55892ba047865"},"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sobstel%2Fmetaphore","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sobstel%2Fmetaphore/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sobstel%2Fmetaphore/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sobstel%2Fmetaphore/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sobstel","download_url":"https://codeload.github.com/sobstel/metaphore/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249148281,"owners_count":21220509,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["caching","php"],"created_at":"2024-08-01T21:01:37.980Z","updated_at":"2025-04-15T20:33:06.042Z","avatar_url":"https://github.com/sobstel.png","language":"PHP","readme":"metaphore\n=========\n\nPHP cache slam defense using a semaphore to prevent dogpile effect (aka clobbering updates, stampeding herd or\nSlashdot effect).\n\n**Problem**: too many requests hit your website at the same time while it tries to regenerate same content slamming\nyour database, eg. when cache expired.\n\n**Solution:** first request generates new content while all the subsequent requests get (stale) content from cache\nuntil it's refreshed by the first request.\n\nRead [http://www.sobstel.org/blog/preventing-dogpile-effect/](http://www.sobstel.org/blog/preventing-dogpile-effect/)\nfor more details.\n\n\u003ca href='https://ko-fi.com/sobstel' target='_blank'\u003e\u003cimg height='32' src='https://az743702.vo.msecnd.net/cdn/kofi3.png?v=0' border='0' alt='Buy Me a Coffee at ko-fi.com' /\u003e\n\n\nInstallation\n------------\n\nIn composer.json file:\n\n```\n\"require\": {\n  \"sobstel/metaphore\": \"2.0.*\"\n}\n```\n\nor just `composer require sobstel/metaphore`\n\nUsage\n-----\n\n``` php\nuse Metaphore\\Cache;\nuse Metaphore\\Store\\MemcachedStore;\n\n// initialize $memcached object (new Memcached())\n\n$cache = new Cache(new MemcachedStore($memcached));\n$cache-\u003ecache('key', function() {\n    // generate content\n}, 30);\n```\n\nPublic API (methods)\n--------------------\n\n- `__construct(ValueStoreInterface $valueStore, LockManager $lockManager = null)`\n\n- `cache($key, callable $callable, [$ttl, [$onNoStaleCacheCallable]])` - returns result\n- `delete($key)`\n- `getValue($key)` - returns Value object\n- `setResult($key, $result, Ttl $ttl)` - sets result (without anti-dogpile-effect mechanism)\n- `onNoStaleCache($callable)`\n\n- `getValueStore()`\n- `getLockManager()`\n\nValue store vs lock store\n-------------------------\n\nCache values and locks can be handled by different stores.\n\n``` php\n$valueStore = new Metaphore\\MemcachedStore($memcached);\n\n$lockStore = new Your\\Custom\\MySQLLockStore($connection);\n$lockManager = new Metaphore\\LockManager($lockStore);\n\n$cache = new Metaphore\\Cache($valueStore, $lockManager);\n```\n\nBy default - if no 2nd argument passed to Cache constructor - value store is used as a lock store.\n\nSample use case might be to have custom MySQL GET_LOCK/RELEASE_LOCK for locks and still use in-built\nMemcached store for storing values.\n\nTime-to-live\n------------\n\nYou can pass simple integer value...\n\n``` php\n$cache-\u003ecache('key', callback, 30); // cache for 30 secs\n```\n\n.. or use more advanced `Metaphore\\TTl` object, which gives you control over grace period and lock ttl.\n\n``` php\n// $ttl, $grace_ttl, $lock_ttl\n$ttl = new Ttl(30, 60, 15);\n\n$cache-\u003ecache('key', callback, $ttl);\n```\n\n- `$ttl` - regular cache time (in seconds)\n- `$grace_ttl` - grace period, how long to allow to serve stale content while new one is being generated (in seconds),\n   similar to HTTP's stale-while-revalidate, default is 60s\n- `$lock_ttl` - lock time, how long to prevent other request(s) from generating same content, default is 5s\n\nTtl value is added to current timestamp (`time() + $ttl`).\n\nNo stale cache\n--------------\n\nIn rare situations, when cache gets expired and there's no stale (generated earlier) content available, all requests\nwill start generating new content.\n\nYou can add listener to catch this:\n\n``` php\n$cache-\u003eonNoStaleCache(function (NoStaleCacheEvent $event) {\n    Logger::log(sprintf('no stale cache detected for key %s', $event-\u003egetKey()));\n});\n```\n\nYou can also affect value that is returned:\n\n``` php\n$cache-\u003eonNoStaleCache(function (NoStaleCacheEvent $event) {\n    $event-\u003esetResult('new custom result');\n});\n```\n\nTests\n-----\n\nRun all tests: `phpunit`.\n\nIf no memcached or/and redis installed: `phpunit --exclude-group=notisolated` or `phpunit --exclude-group=memcached,redis`.\n","funding_links":["https://ko-fi.com/sobstel","https://ko-fi.com/sobstel'"],"categories":["目录","Table of Contents","PHP"],"sub_categories":["缓存和锁定 Caching and Locking","Caching and Locking","Caching"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsobstel%2Fmetaphore","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsobstel%2Fmetaphore","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsobstel%2Fmetaphore/lists"}