Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/sobstel/metaphore
Cache slam defense using a semaphore to prevent dogpile effect.
https://github.com/sobstel/metaphore
caching php
Last synced: about 1 month ago
JSON representation
Cache slam defense using a semaphore to prevent dogpile effect.
- Host: GitHub
- URL: https://github.com/sobstel/metaphore
- Owner: sobstel
- License: mit
- Created: 2013-08-16T16:33:54.000Z (over 11 years ago)
- Default Branch: master
- Last Pushed: 2024-02-24T11:42:47.000Z (10 months ago)
- Last Synced: 2024-05-06T08:34:17.360Z (7 months ago)
- Topics: caching, php
- Language: PHP
- Homepage:
- Size: 123 KB
- Stars: 101
- Watchers: 12
- Forks: 9
- Open Issues: 2
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Funding: .github/FUNDING.yml
- License: LICENSE
Awesome Lists containing this project
- awesome-php - Metaphore - Cache slam defense using a semaphore to prevent dogpile effect. (Table of Contents / Caching and Locking)
- awesome-php-cn - Metaphore - 缓存大满贯防御使用信号量,防止dogpile效果. (目录 / 缓存和锁定 Caching and Locking)
- awesome-projects - Metaphore - Cache slam defense using a semaphore to prevent dogpile effect. (PHP / Caching and Locking)
- awesome-php - Metaphore - Cache slam defense using a semaphore to prevent dogpile effect. (Table of Contents / Caching and Locking)
README
metaphore
=========PHP cache slam defense using a semaphore to prevent dogpile effect (aka clobbering updates, stampeding herd or
Slashdot effect).**Problem**: too many requests hit your website at the same time while it tries to regenerate same content slamming
your database, eg. when cache expired.**Solution:** first request generates new content while all the subsequent requests get (stale) content from cache
until it's refreshed by the first request.Read [http://www.sobstel.org/blog/preventing-dogpile-effect/](http://www.sobstel.org/blog/preventing-dogpile-effect/)
for more details.Installation
------------In composer.json file:
```
"require": {
"sobstel/metaphore": "2.0.*"
}
```or just `composer require sobstel/metaphore`
Usage
-----``` php
use Metaphore\Cache;
use Metaphore\Store\MemcachedStore;// initialize $memcached object (new Memcached())
$cache = new Cache(new MemcachedStore($memcached));
$cache->cache('key', function() {
// generate content
}, 30);
```Public API (methods)
--------------------- `__construct(ValueStoreInterface $valueStore, LockManager $lockManager = null)`
- `cache($key, callable $callable, [$ttl, [$onNoStaleCacheCallable]])` - returns result
- `delete($key)`
- `getValue($key)` - returns Value object
- `setResult($key, $result, Ttl $ttl)` - sets result (without anti-dogpile-effect mechanism)
- `onNoStaleCache($callable)`- `getValueStore()`
- `getLockManager()`Value store vs lock store
-------------------------Cache values and locks can be handled by different stores.
``` php
$valueStore = new Metaphore\MemcachedStore($memcached);$lockStore = new Your\Custom\MySQLLockStore($connection);
$lockManager = new Metaphore\LockManager($lockStore);$cache = new Metaphore\Cache($valueStore, $lockManager);
```By default - if no 2nd argument passed to Cache constructor - value store is used as a lock store.
Sample use case might be to have custom MySQL GET_LOCK/RELEASE_LOCK for locks and still use in-built
Memcached store for storing values.Time-to-live
------------You can pass simple integer value...
``` php
$cache->cache('key', callback, 30); // cache for 30 secs
```.. or use more advanced `Metaphore\TTl` object, which gives you control over grace period and lock ttl.
``` php
// $ttl, $grace_ttl, $lock_ttl
$ttl = new Ttl(30, 60, 15);$cache->cache('key', callback, $ttl);
```- `$ttl` - regular cache time (in seconds)
- `$grace_ttl` - grace period, how long to allow to serve stale content while new one is being generated (in seconds),
similar to HTTP's stale-while-revalidate, default is 60s
- `$lock_ttl` - lock time, how long to prevent other request(s) from generating same content, default is 5sTtl value is added to current timestamp (`time() + $ttl`).
No stale cache
--------------In rare situations, when cache gets expired and there's no stale (generated earlier) content available, all requests
will start generating new content.You can add listener to catch this:
``` php
$cache->onNoStaleCache(function (NoStaleCacheEvent $event) {
Logger::log(sprintf('no stale cache detected for key %s', $event->getKey()));
});
```You can also affect value that is returned:
``` php
$cache->onNoStaleCache(function (NoStaleCacheEvent $event) {
$event->setResult('new custom result');
});
```Tests
-----Run all tests: `phpunit`.
If no memcached or/and redis installed: `phpunit --exclude-group=notisolated` or `phpunit --exclude-group=memcached,redis`.