default services conflit ?
This commit is contained in:
2
old.vendor/davedevelopment/stiphle/.gitignore
vendored
Normal file
2
old.vendor/davedevelopment/stiphle/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
vendor
|
||||
phpunit.xml
|
19
old.vendor/davedevelopment/stiphle/LICENCE
Normal file
19
old.vendor/davedevelopment/stiphle/LICENCE
Normal file
@@ -0,0 +1,19 @@
|
||||
Copyright (C) 2012 Dave Marshall <dave.marshall@atstsolutions.co.uk>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
138
old.vendor/davedevelopment/stiphle/README.md
Normal file
138
old.vendor/davedevelopment/stiphle/README.md
Normal file
@@ -0,0 +1,138 @@
|
||||
Stiphle
|
||||
======
|
||||
|
||||
Install via Composer
|
||||
-------
|
||||
|
||||
```
|
||||
composer require davedevelopment/stiphle
|
||||
```
|
||||
|
||||
What is it?
|
||||
-----------
|
||||
|
||||
Stiphle is a little library to try and provide an easy way of throttling/rate limit requests, for those without fancy hardware etc.
|
||||
|
||||
How does it work?
|
||||
-----------------
|
||||
|
||||
You create a throttle, and ask it how long you should wait. For example, given
|
||||
that $identifier is some means of identifying whatever it is you're throttling,
|
||||
and you want to throttle it to 5 requests per second:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
|
||||
$throttle = new Stiphle\Throttle\LeakyBucket;
|
||||
$identifier = 'dave';
|
||||
while(true) {
|
||||
// the throttle method returns the amount of milliseconds it slept for
|
||||
echo $throttle->throttle($identifier, 5, 1000);
|
||||
}
|
||||
# 0 0 0 0 0 200 200....
|
||||
|
||||
```
|
||||
|
||||
Use combinations of values to provide bursting etc, though use carefully as it
|
||||
screws with your mind
|
||||
|
||||
``` php
|
||||
<?php
|
||||
|
||||
$throttle = new Stiphle\Throttle\LeakyBucket;
|
||||
$identifier = 'dave';
|
||||
for(;;) {
|
||||
/**
|
||||
* Allow upto 5 per second, but limit to 20 a minute - I think
|
||||
**/
|
||||
echo "a:" . $throttle->throttle($identifier, 5, 1000);
|
||||
echo " b:" . $throttle->throttle($identifier, 20, 60000);
|
||||
echo "\n";
|
||||
}
|
||||
#a:0 b:0
|
||||
#a:0 b:0
|
||||
#a:0 b:0
|
||||
#a:0 b:0
|
||||
#a:0 b:0
|
||||
#a:199 b:0
|
||||
#a:200 b:0
|
||||
#a:199 b:0
|
||||
#a:200 b:0
|
||||
#a:200 b:0
|
||||
#a:199 b:0
|
||||
#a:200 b:0
|
||||
#a:199 b:0
|
||||
#a:200 b:0
|
||||
#a:200 b:0
|
||||
#a:199 b:0
|
||||
#a:200 b:0
|
||||
#a:200 b:0
|
||||
#a:199 b:0
|
||||
#a:200 b:0
|
||||
#a:199 b:0
|
||||
#a:200 b:2600
|
||||
#a:0 b:3000
|
||||
#a:0 b:2999
|
||||
|
||||
|
||||
```
|
||||
|
||||
Throttle Strategies
|
||||
-------------------
|
||||
|
||||
There are currently two types of throttles, [Leaky
|
||||
Bucket](http://en.wikipedia.org/wiki/Leaky_bucket) and a simple fixed time
|
||||
window.
|
||||
|
||||
``` php
|
||||
|
||||
/**
|
||||
* Throttle to 1000 per *rolling* 24 hours, e.g. the counter will not reset at
|
||||
* midnight
|
||||
*/
|
||||
$throttle = new Stiphle\Throttle\LeakyBucket;
|
||||
$throttle->throttle('api.request', 1000, 86400000);
|
||||
|
||||
/**
|
||||
* Throttle to 1000 per calendar day, counter will reset at midnight
|
||||
*/
|
||||
$throttle = new Stiphle\Throttle\TimeWindow;
|
||||
$throttle->throttle('api.request', 1000, 86400000);
|
||||
|
||||
```
|
||||
|
||||
__NB:__ The current implementation of the `TimeWindow` throttle will only work on 64-bit architectures!
|
||||
|
||||
Storage
|
||||
-------
|
||||
|
||||
Stiphle currently ships with 5 storage engines
|
||||
|
||||
* In process
|
||||
* APC
|
||||
* Memcached
|
||||
* Doctrine Cache
|
||||
* Redis
|
||||
|
||||
Stiphle uses the in process storage by default. A different storage engine can
|
||||
be injected after object creation.
|
||||
|
||||
``` php
|
||||
$throttle = new Stiphle\Throttle\LeakyBucket();
|
||||
$storage = new \Stiphle\Storage\Memcached(new \Memcached());
|
||||
$throttle->setStorage($storage);
|
||||
```
|
||||
|
||||
Todo
|
||||
----
|
||||
|
||||
* More Tests!
|
||||
* Decent *Unit* tests
|
||||
* More throttling methods
|
||||
* More storage adapters, the current ones are a little volatile, Mongo,
|
||||
Cassandra, MemcacheDB etc
|
||||
|
||||
Copyright
|
||||
---------
|
||||
|
||||
Copyright (c) 2011 Dave Marshall. See LICENCE for further details
|
32
old.vendor/davedevelopment/stiphle/composer.json
Normal file
32
old.vendor/davedevelopment/stiphle/composer.json
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"name": "davedevelopment/stiphle",
|
||||
"type": "library",
|
||||
"description": "Simple rate limiting/throttling for php",
|
||||
"keywords": ["throttle", "throttling", "rate limiting", "rate limit"],
|
||||
"homepage": "http://github.com/davedevelopment/stiphle",
|
||||
"license": "MIT",
|
||||
"authors": [{
|
||||
"name": "Dave Marshall",
|
||||
"email": "dave.marshall@atstsolutions.co.uk",
|
||||
"homepage": "http://davedevelopment.co.uk"
|
||||
}],
|
||||
|
||||
"require": {
|
||||
"php": ">=5.3.1"
|
||||
},
|
||||
|
||||
"suggest": {
|
||||
"doctrine/cache": "~1.0",
|
||||
"predis/predis": "~1.1"
|
||||
},
|
||||
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"Stiphle": "src/"
|
||||
}
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^5.5",
|
||||
"predis/predis": "^1.1"
|
||||
}
|
||||
}
|
20
old.vendor/davedevelopment/stiphle/phpunit.xml.dist
Normal file
20
old.vendor/davedevelopment/stiphle/phpunit.xml.dist
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- http://www.phpunit.de/manual/current/en/appendixes.configuration.html -->
|
||||
<phpunit
|
||||
backupGlobals = "false"
|
||||
backupStaticAttributes = "false"
|
||||
colors = "true"
|
||||
convertErrorsToExceptions = "true"
|
||||
convertNoticesToExceptions = "true"
|
||||
convertWarningsToExceptions = "true"
|
||||
processIsolation = "false"
|
||||
stopOnFailure = "false"
|
||||
syntaxCheck = "false"
|
||||
bootstrap = "./vendor/autoload.php" >
|
||||
|
||||
<testsuites>
|
||||
<testsuite name="Stiphle">
|
||||
<directory>./tests</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
</phpunit>
|
136
old.vendor/davedevelopment/stiphle/src/Stiphle/Storage/Apc.php
Normal file
136
old.vendor/davedevelopment/stiphle/src/Stiphle/Storage/Apc.php
Normal file
@@ -0,0 +1,136 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Stiphle
|
||||
* @subpackage Stiphle\Throttle\LeakyBucket\Storage
|
||||
*/
|
||||
namespace Stiphle\Storage;
|
||||
|
||||
/**
|
||||
* This file is part of Stiphle
|
||||
*
|
||||
* Copyright (c) 2011 Dave Marshall <dave.marshall@atstsolutuions.co.uk>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Use Apc as the storage, I hope apc_add is atomic and therefore we wont get
|
||||
* any race conditions with the locking....
|
||||
*
|
||||
* @author Dave Marshall <david.marshall@atstsolutions.co.uk>
|
||||
*/
|
||||
class Apc implements StorageInterface
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $lockWaitTimeout = 1000;
|
||||
|
||||
/**
|
||||
* @var int Time to sleep when attempting to get lock in microseconds
|
||||
*/
|
||||
protected $sleep = 100;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $ttl = 10000000;
|
||||
|
||||
/**
|
||||
* Set lock wait timeout
|
||||
*
|
||||
* @param int $milliseconds
|
||||
*/
|
||||
public function setLockWaitTimeout($milliseconds)
|
||||
{
|
||||
$this->lockWaitTimeout = $milliseconds;
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the sleep time in microseconds
|
||||
*
|
||||
* @param int
|
||||
* @return void
|
||||
*/
|
||||
public function setSleep($microseconds)
|
||||
{
|
||||
$this->sleep = $microseconds;
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the ttl for the apc records in seconds
|
||||
*
|
||||
* @param int $seconds
|
||||
* @return void
|
||||
*/
|
||||
public function setTtl($microseconds)
|
||||
{
|
||||
$this->ttl = $microseconds;
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lock
|
||||
*
|
||||
* If we're using storage, we might have multiple requests coming in at
|
||||
* once, so we lock the storage
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function lock($key)
|
||||
{
|
||||
$key = $key . "::LOCK";
|
||||
$start = microtime(true);
|
||||
while(!apc_add($key, true, $this->ttl)) {
|
||||
$passed = (microtime(true) - $start) * 1000;
|
||||
if ($passed > $this->lockWaitTimeout) {
|
||||
throw new LockWaitTimeoutException();
|
||||
}
|
||||
usleep($this->sleep);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlock
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function unlock($key)
|
||||
{
|
||||
$key = $key . "::LOCK";
|
||||
apc_delete($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get last modified
|
||||
*
|
||||
* @param string $key
|
||||
* @return int
|
||||
*/
|
||||
public function get($key)
|
||||
{
|
||||
return apc_fetch($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* set
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
*/
|
||||
public function set($key, $value)
|
||||
{
|
||||
apc_store($key, $value, $this->ttl);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
127
old.vendor/davedevelopment/stiphle/src/Stiphle/Storage/Apcu.php
Normal file
127
old.vendor/davedevelopment/stiphle/src/Stiphle/Storage/Apcu.php
Normal file
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Stiphle
|
||||
* @subpackage Stiphle\Throttle\LeakyBucket\Storage
|
||||
*/
|
||||
namespace Stiphle\Storage;
|
||||
|
||||
/**
|
||||
* This file is part of Stiphle
|
||||
*
|
||||
* Copyright (c) 2011 Dave Marshall <dave.marshall@atstsolutuions.co.uk>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
class Apcu implements StorageInterface
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $lockWaitTimeout = 1000;
|
||||
|
||||
/**
|
||||
* @var int Time to sleep when attempting to get lock in microseconds
|
||||
*/
|
||||
protected $sleep = 100;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $ttl = 10000000;
|
||||
|
||||
/**
|
||||
* Set lock wait timeout
|
||||
*
|
||||
* @param int $milliseconds
|
||||
*/
|
||||
public function setLockWaitTimeout($milliseconds)
|
||||
{
|
||||
$this->lockWaitTimeout = $milliseconds;
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the sleep time in microseconds
|
||||
*
|
||||
* @param int
|
||||
* @return void
|
||||
*/
|
||||
public function setSleep($microseconds)
|
||||
{
|
||||
$this->sleep = $microseconds;
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the ttl for the apc records in seconds
|
||||
*
|
||||
* @param int $seconds
|
||||
* @return void
|
||||
*/
|
||||
public function setTtl($microseconds)
|
||||
{
|
||||
$this->ttl = $microseconds;
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lock
|
||||
*
|
||||
* If we're using storage, we might have multiple requests coming in at
|
||||
* once, so we lock the storage
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function lock($key)
|
||||
{
|
||||
$key = $key . "::LOCK";
|
||||
$start = microtime(true);
|
||||
while(!apcu_add($key, true, $this->ttl)) {
|
||||
$passed = (microtime(true) - $start) * 1000;
|
||||
if ($passed > $this->lockWaitTimeout) {
|
||||
throw new LockWaitTimeoutException();
|
||||
}
|
||||
usleep($this->sleep);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlock
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function unlock($key)
|
||||
{
|
||||
$key = $key . "::LOCK";
|
||||
apcu_delete($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get last modified
|
||||
*
|
||||
* @param string $key
|
||||
* @return int
|
||||
*/
|
||||
public function get($key)
|
||||
{
|
||||
return apcu_fetch($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* set
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
*/
|
||||
public function set($key, $value)
|
||||
{
|
||||
apcu_store($key, $value, $this->ttl);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
namespace Stiphle\Storage;
|
||||
|
||||
use Doctrine\Common\Cache\Cache;
|
||||
|
||||
/**
|
||||
* This file is part of Stiphle
|
||||
*
|
||||
* Copyright (c) 2011 Dave Marshall <dave.marshall@atstsolutuions.co.uk>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
class DoctrineCache implements StorageInterface
|
||||
{
|
||||
protected $lockWaitTimeout = 1000;
|
||||
protected $lockWaitInterval = 100;
|
||||
|
||||
public function __construct(Cache $cache, $lockWaitTimeout = 1000, $lockWaitInterval = 100)
|
||||
{
|
||||
$this->cache = $cache;
|
||||
$this->lockWaitTimeout = $lockWaitTimeout;
|
||||
$this->lockWaitInterval = $lockWaitInterval;
|
||||
}
|
||||
|
||||
public function setLockWaitTimeout($milliseconds)
|
||||
{
|
||||
$this->lockWaitTimeout = $milliseconds;
|
||||
return;
|
||||
}
|
||||
|
||||
public function setSleep($microseconds)
|
||||
{
|
||||
$this->sleep = $microseconds;
|
||||
return;
|
||||
}
|
||||
|
||||
public function lock($key)
|
||||
{
|
||||
$key = $key . "::LOCK";
|
||||
$start = microtime(true);
|
||||
while ($this->cache->contains($key)) {
|
||||
$passed = (microtime(true) - $start) * 1000;
|
||||
if ($passed > $this->lockWaitTimeout) {
|
||||
throw new LockWaitTimeoutException();
|
||||
}
|
||||
usleep($this->sleep);
|
||||
}
|
||||
$this->cache->save($key, true);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
public function unlock($key)
|
||||
{
|
||||
$key = $key . "::LOCK";
|
||||
$this->cache->delete($key);
|
||||
}
|
||||
|
||||
public function get($key)
|
||||
{
|
||||
return $this->cache->fetch($key);
|
||||
}
|
||||
|
||||
public function set($key, $value)
|
||||
{
|
||||
$this->cache->save($key, $value);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Stiphle
|
||||
* @subpackage Stiphle\Storage
|
||||
*/
|
||||
namespace Stiphle\Storage;
|
||||
|
||||
/**
|
||||
* This file is part of Stiphle
|
||||
*
|
||||
* Copyright (c) 2011 Dave Marshall <dave.marshall@atstsolutuions.co.uk>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Thrown when a request for a lock timesout
|
||||
*
|
||||
* @author Dave Marshall <david.marshall@atstsolutions.co.uk>
|
||||
*/
|
||||
class LockWaitTimeoutException extends \Exception {}
|
@@ -0,0 +1,150 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Stiphle
|
||||
* @subpackage Stiphle\Storage
|
||||
*/
|
||||
namespace Stiphle\Storage;
|
||||
|
||||
/**
|
||||
* This file is part of Stiphle
|
||||
*
|
||||
* Copyright (c) 2011 Dave Marshall <dave.marshall@atstsolutuions.co.uk>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Use memcached via PHP's memcached extension
|
||||
*
|
||||
* @author Dave Marshall <david.marshall@atstsolutions.co.uk>
|
||||
*/
|
||||
class Memcached implements StorageInterface
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $lockWaitTimeout = 1000;
|
||||
|
||||
/**
|
||||
* @var int Time to sleep when attempting to get lock in microseconds
|
||||
*/
|
||||
protected $sleep = 100;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $ttl = 3600;
|
||||
|
||||
/**
|
||||
* Memcached instance
|
||||
*/
|
||||
protected $memcached;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
*/
|
||||
public function __construct(\Memcached $memcached)
|
||||
{
|
||||
$this->memcached = $memcached;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set lock wait timeout
|
||||
*
|
||||
* @param int $milliseconds
|
||||
*/
|
||||
public function setLockWaitTimeout($milliseconds)
|
||||
{
|
||||
$this->lockWaitTimeout = $milliseconds;
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the sleep time in microseconds
|
||||
*
|
||||
* @param int
|
||||
* @return void
|
||||
*/
|
||||
public function setSleep($microseconds)
|
||||
{
|
||||
$this->sleep = $microseconds;
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the ttl for the apc records in seconds
|
||||
*
|
||||
* @param int $seconds
|
||||
* @return void
|
||||
*/
|
||||
public function setTtl($microseconds)
|
||||
{
|
||||
$this->ttl = $microseconds;
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lock
|
||||
*
|
||||
* If we're using storage, we might have multiple requests coming in at
|
||||
* once, so we lock the storage
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function lock($key)
|
||||
{
|
||||
$key = $key . "::LOCK";
|
||||
$start = microtime(true);
|
||||
|
||||
while(!$this->memcached->add($key, true, $this->ttl)) {
|
||||
$passed = (microtime(true) - $start) * 1000;
|
||||
if ($passed > $this->lockWaitTimeout) {
|
||||
throw new LockWaitTimeoutException();
|
||||
}
|
||||
usleep($this->sleep);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlock
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function unlock($key)
|
||||
{
|
||||
$key = $key . "::LOCK";
|
||||
$this->memcached->delete($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get last modified
|
||||
*
|
||||
* @param string $key
|
||||
* @return int
|
||||
*/
|
||||
public function get($key)
|
||||
{
|
||||
return $this->memcached->get($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* set
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
*/
|
||||
public function set($key, $value)
|
||||
{
|
||||
$this->memcached->set($key, $value, $this->ttl);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Stiphle
|
||||
* @subpackage Stiphle\Storage
|
||||
*/
|
||||
namespace Stiphle\Storage;
|
||||
|
||||
/**
|
||||
* This file is part of Stiphle
|
||||
*
|
||||
* Copyright (c) 2011 Dave Marshall <dave.marshall@atstsolutuions.co.uk>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Basic in-process storage of values
|
||||
*
|
||||
* @author Dave Marshall <david.marshall@atstsolutions.co.uk>
|
||||
*/
|
||||
class Process implements StorageInterface
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $lockWaitTimeout = 1000;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $locked = array();
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $values = array();
|
||||
|
||||
/**
|
||||
* Set lock wait timeout
|
||||
*
|
||||
* @param int $milliseconds
|
||||
*/
|
||||
public function setLockWaitTimeout($milliseconds)
|
||||
{
|
||||
$this->lockWaitTimeout = $milliseconds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lock
|
||||
*
|
||||
* If we're using storage, we might have multiple requests coming in at
|
||||
* once, so we lock the storage
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function lock($key)
|
||||
{
|
||||
if (!isset($this->locked[$key])) {
|
||||
$this->locked[$key] = false;
|
||||
}
|
||||
|
||||
$start = microtime(true);
|
||||
while($this->locked[$key]) {
|
||||
$passed = (microtime(true) - $start) * 1000;
|
||||
if ($passed > $this->lockWaitTimeout) {
|
||||
throw new LockWaitTimeoutException();
|
||||
}
|
||||
}
|
||||
|
||||
$this->locked[$key] = true;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlock
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function unlock($key)
|
||||
{
|
||||
$this->locked[$key] = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get
|
||||
*
|
||||
* @param string $key
|
||||
* @return int
|
||||
*/
|
||||
public function get($key)
|
||||
{
|
||||
if (isset($this->values[$key])) {
|
||||
return $this->values[$key];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* set
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
*/
|
||||
public function set($key, $value)
|
||||
{
|
||||
$this->values[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
namespace Stiphle\Storage;
|
||||
|
||||
/**
|
||||
* Redis storage via Predis package
|
||||
*
|
||||
* @author Jacob Christiansen <jacob@colourbox.com>
|
||||
*/
|
||||
class Redis implements StorageInterface
|
||||
{
|
||||
protected $lockWaitTimeout = 1000;
|
||||
protected $redisClient;
|
||||
|
||||
public function __construct(\Predis\Client $redisClient)
|
||||
{
|
||||
$this->redisClient = $redisClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function setLockWaitTimeout($milliseconds)
|
||||
{
|
||||
$this->lockWaitTimeout = $milliseconds;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function lock($key)
|
||||
{
|
||||
$start = microtime(true);
|
||||
|
||||
while (is_null($this->redisClient->set($this->getLockKey($key), 'LOCKED', 'PX', 3600, 'NX'))) {
|
||||
$passed = (microtime(true) - $start) * 1000;
|
||||
if ($passed > $this->lockWaitTimeout) {
|
||||
throw new LockWaitTimeoutException();
|
||||
}
|
||||
usleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function unlock($key)
|
||||
{
|
||||
$this->redisClient->del($this->getLockKey($key));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function get($key)
|
||||
{
|
||||
return $this->redisClient->get($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function set($key, $value)
|
||||
{
|
||||
$this->redisClient->set($key, $value);
|
||||
}
|
||||
|
||||
private function getLockKey($key)
|
||||
{
|
||||
return $key . "::LOCK";
|
||||
}
|
||||
}
|
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Stiphle
|
||||
* @subpackage Stiphle\Throttle\LeakyBucket\Storage
|
||||
*/
|
||||
namespace Stiphle\Storage;
|
||||
|
||||
/**
|
||||
* This file is part of Stiphle
|
||||
*
|
||||
* Copyright (c) 2011 Dave Marshall <dave.marshall@atstsolutuions.co.uk>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Interface describing a persistant storage mechanism for the LeakyBucket
|
||||
* throttle
|
||||
*
|
||||
* @author Dave Marshall <david.marshall@atstsolutions.co.uk>
|
||||
*/
|
||||
interface StorageInterface
|
||||
{
|
||||
/**
|
||||
* Set lock wait timout
|
||||
*
|
||||
* @param int $milliseconds
|
||||
*/
|
||||
public function setLockWaitTimeout($milliseconds);
|
||||
|
||||
/**
|
||||
* Lock
|
||||
*
|
||||
* We might have multiple requests coming in at once, so we lock the storage
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function lock($key);
|
||||
|
||||
/**
|
||||
* Unlock
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function unlock($key);
|
||||
|
||||
/**
|
||||
* Get
|
||||
*
|
||||
* @param string $key
|
||||
* @return int
|
||||
*/
|
||||
public function get($key);
|
||||
|
||||
/**
|
||||
* set last modified
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
*/
|
||||
public function set($key, $value);
|
||||
}
|
||||
|
||||
|
||||
|
@@ -0,0 +1,192 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Stiphle
|
||||
* @subpackage Stiphle\Throttle
|
||||
*/
|
||||
namespace Stiphle\Throttle;
|
||||
|
||||
use Stiphle\Throttle\ThrottleInterface;
|
||||
use Stiphle\Storage\StorageInterface;
|
||||
use Stiphle\Storage\Process;
|
||||
|
||||
/**
|
||||
* This file is part of Stiphle
|
||||
*
|
||||
* Copyright (c) 2011 Dave Marshall <dave.marshall@atstsolutuions.co.uk>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A 'leaky bucket' style rate limiter
|
||||
*
|
||||
* @see http://stackoverflow.com/questions/1375501/how-do-i-throttle-my-sites-api-users
|
||||
* @author Dave Marshall <david.marshall@atstsolutions.co.uk>
|
||||
*/
|
||||
class LeakyBucket implements ThrottleInterface
|
||||
{
|
||||
/**
|
||||
* @var StorageInterface
|
||||
*/
|
||||
protected $storage;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->storage = new Process();
|
||||
}
|
||||
|
||||
/**
|
||||
* Throttle
|
||||
*
|
||||
* @param string $key - A unique key for what we're throttling
|
||||
* @param int $limit - How many are allowed
|
||||
* @param int $milliseconds - In this many milliseconds
|
||||
* @return int
|
||||
*/
|
||||
public function throttle($key, $limit, $milliseconds)
|
||||
{
|
||||
/**
|
||||
* Try and do our waiting without a lock
|
||||
*/
|
||||
$key = $this->getStorageKey($key, $limit, $milliseconds);
|
||||
$wait = 0;
|
||||
$newRatio = $this->getNewRatio($key, $limit, $milliseconds);
|
||||
|
||||
if ($newRatio > $milliseconds) {
|
||||
$wait = ceil($newRatio - $milliseconds);
|
||||
}
|
||||
usleep($wait * 1000);
|
||||
|
||||
/**
|
||||
* Lock, record and release
|
||||
*/
|
||||
$this->storage->lock($key);
|
||||
$newRatio = $this->getNewRatio($key, $limit, $milliseconds);
|
||||
$this->setLastRatio($key, $newRatio);
|
||||
$this->setLastRequest($key, microtime(1));
|
||||
$this->storage->unlock($key);
|
||||
return $wait;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Estimate (doesn't require lock)
|
||||
*
|
||||
* How long would I have to wait to make a request?
|
||||
*
|
||||
* @param string $key - A unique key for what we're throttling
|
||||
* @param int $limit - How many are allowed
|
||||
* @param int $milliseconds - In this many milliseconds
|
||||
* @return int - the number of milliseconds before this request should be allowed
|
||||
* to pass
|
||||
*/
|
||||
public function getEstimate($key, $limit, $milliseconds)
|
||||
{
|
||||
$key = $this->getStorageKey($key, $limit, $milliseconds);
|
||||
$newRatio = $this->getNewRatio($key, $limit, $milliseconds);
|
||||
$wait = 0;
|
||||
if ($newRatio > $milliseconds) {
|
||||
$wait = ceil($newRatio - $milliseconds);
|
||||
}
|
||||
return $wait;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get new ratio
|
||||
*
|
||||
* Assuming we're making a request, get the ratio of requests made to
|
||||
* requests allowed
|
||||
*
|
||||
* @param string $key - A unique key for what we're throttling
|
||||
* @param int $limit - How many are allowed
|
||||
* @param int $milliseconds - In this many milliseconds
|
||||
* @return float
|
||||
*/
|
||||
protected function getNewRatio($key, $limit, $milliseconds)
|
||||
{
|
||||
$lastRequest = $this->getLastRequest($key) ?: 0;
|
||||
$lastRatio = $this->getLastRatio($key) ?: 0;
|
||||
|
||||
$diff = (microtime(1) - $lastRequest) * 1000;
|
||||
|
||||
$newRatio = $lastRatio - $diff;
|
||||
$newRatio = $newRatio < 0 ? 0 : $newRatio;
|
||||
$newRatio+= $milliseconds/$limit;
|
||||
|
||||
return $newRatio;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get storage key
|
||||
*
|
||||
* @param string $key - A unique key for what we're throttling
|
||||
* @param int $limit - How many are allowed
|
||||
* @param int $milliseconds - In this many milliseconds
|
||||
* @return string
|
||||
*/
|
||||
protected function getStorageKey($key, $limit, $milliseconds)
|
||||
{
|
||||
return $key . '::' . $limit . '::' . $milliseconds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Storage
|
||||
*
|
||||
* @param StorageInterface $storage
|
||||
* @return LeakyBucket
|
||||
*/
|
||||
public function setStorage(StorageInterface $storage)
|
||||
{
|
||||
$this->storage = $storage;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Last Ratio
|
||||
*
|
||||
* @param string $key
|
||||
* @return float
|
||||
*/
|
||||
protected function getLastRatio($key)
|
||||
{
|
||||
return $this->storage->get($key . '::LASTRATIO');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Last Ratio
|
||||
*
|
||||
* @param string $key
|
||||
* @param float $ratio
|
||||
* @return void
|
||||
*/
|
||||
protected function setLastRatio($key, $ratio)
|
||||
{
|
||||
return $this->storage->set($key . '::LASTRATIO', $ratio);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Last Request
|
||||
*
|
||||
* @param string $key
|
||||
* @return float
|
||||
*/
|
||||
protected function getLastRequest($key)
|
||||
{
|
||||
return $this->storage->get($key . '::LASTREQUEST');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Last Request
|
||||
*
|
||||
* @param string $key
|
||||
* @param float $request
|
||||
* @return void
|
||||
*/
|
||||
protected function setLastRequest($key, $request)
|
||||
{
|
||||
return $this->storage->set($key . '::LASTREQUEST', $request);
|
||||
}
|
||||
}
|
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Stiphle
|
||||
* @subpackage
|
||||
*/
|
||||
|
||||
namespace Stiphle\Throttle;
|
||||
|
||||
/**
|
||||
* This file is part of Stiphle
|
||||
*
|
||||
* Copyright (c) 2011 Dave Marshall <dave.marshall@atstsolutuions.co.uk>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Interface describing a throttle
|
||||
*
|
||||
* @author Dave Marshall <david.marshall@atstsolutions.co.uk>
|
||||
*/
|
||||
interface ThrottleInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Throttle
|
||||
*
|
||||
* @param string $key - A unique key for what we're throttling
|
||||
* @param int $limit - How many are allowed
|
||||
* @param int $milliseconds - In this many milliseconds
|
||||
* @return void
|
||||
*/
|
||||
public function throttle($key, $limit, $milliseconds);
|
||||
|
||||
/**
|
||||
* Get Estimate
|
||||
*
|
||||
* If I were to throttle now, how long would I be waiting
|
||||
*
|
||||
* @param string $key - A unique key for what we're throttling
|
||||
* @param int $limit - How many are allowed
|
||||
* @param int $milliseconds - In this many milliseconds
|
||||
* @return int - the number of milliseconds before this request should be allowed
|
||||
*/
|
||||
public function getEstimate($key, $limit, $milliseconds);
|
||||
}
|
||||
|
||||
|
||||
|
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
|
||||
namespace Stiphle\Throttle;
|
||||
|
||||
use Stiphle\Throttle\ThrottleInterface;
|
||||
use Stiphle\Storage\StorageInterface;
|
||||
use Stiphle\Storage\Process;
|
||||
|
||||
/**
|
||||
* This file is part of Stiphle
|
||||
*
|
||||
* Copyright (c) 2011 Dave Marshall <dave.marshall@atstsolutuions.co.uk>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A throttle based on a fixed time window
|
||||
*
|
||||
* @author Dave Marshall <david.marshall@atstsolutions.co.uk>
|
||||
*/
|
||||
class TimeWindow implements ThrottleInterface
|
||||
{
|
||||
/**
|
||||
* @var StorageInterface
|
||||
*/
|
||||
protected $storage;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->storage = new Process();
|
||||
}
|
||||
|
||||
/**
|
||||
* Throttle
|
||||
*
|
||||
* @param string $key - A unique key for what we're throttling
|
||||
* @param int $limit - How many are allowed
|
||||
* @param int $milliseconds - In this many milliseconds
|
||||
* @return void
|
||||
*/
|
||||
public function throttle($key, $limit, $milliseconds)
|
||||
{
|
||||
/**
|
||||
* Try do our waiting without a lock, so may sneak through because of
|
||||
* this...
|
||||
*/
|
||||
$wait = $this->getEstimate($key, $limit, $milliseconds);
|
||||
if ($wait > 0) {
|
||||
usleep($wait * 1000);
|
||||
}
|
||||
|
||||
$key = $this->getStorageKey($key, $limit, $milliseconds);
|
||||
$this->storage->lock($key);
|
||||
$count = $this->storage->get($key);
|
||||
$count++;
|
||||
$this->storage->set($key, $count);
|
||||
$this->storage->unlock($key);
|
||||
return $wait;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Estimate (doesn't require lock)
|
||||
*
|
||||
* How long would I have to wait to make a request?
|
||||
*
|
||||
* @param string $key - A unique key for what we're throttling
|
||||
* @param int $limit - How many are allowed
|
||||
* @param int $milliseconds - In this many milliseconds
|
||||
* @return int - the number of milliseconds before this request should be allowed
|
||||
* to pass
|
||||
*/
|
||||
public function getEstimate($key, $limit, $milliseconds)
|
||||
{
|
||||
$key = $this->getStorageKey($key, $limit, $milliseconds);
|
||||
$count = $this->storage->get($key);
|
||||
if ($count < $limit) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return $milliseconds - ((microtime(1) * 1000) % (float) $milliseconds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get storage key
|
||||
*
|
||||
* @param string $key - A unique key for what we're throttling
|
||||
* @param int $limit - How many are allowed
|
||||
* @param int $milliseconds - In this many milliseconds
|
||||
* @return string
|
||||
*/
|
||||
protected function getStorageKey($key, $limit, $milliseconds)
|
||||
{
|
||||
$window = $milliseconds * (floor((microtime(1) * 1000)/$milliseconds));
|
||||
$date = date('YmdHis', $window/1000);
|
||||
return $date . '::' . $key . '::' . $limit . '::' . $milliseconds . '::COUNT';
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Storage
|
||||
*
|
||||
* @param StorageInterface $storage
|
||||
* @return LeakyBucket
|
||||
*/
|
||||
public function setStorage(StorageInterface $storage)
|
||||
{
|
||||
$this->storage = $storage;
|
||||
return $this;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
/**
|
||||
* @package
|
||||
* @subpackage
|
||||
*/
|
||||
namespace Stiphle\Storage;
|
||||
|
||||
use \PHPUnit_Framework_TestCase;
|
||||
|
||||
/**
|
||||
* This file is part of Stiphle
|
||||
*
|
||||
* Copyright (c) 2011 Dave Marshall <dave.marshall@atstsolutuions.co.uk>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* TITLE
|
||||
*
|
||||
* DESCRIPTION
|
||||
*
|
||||
* @author Dave Marshall <david.marshall@atstsolutions.co.uk>
|
||||
*/
|
||||
class ApcTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
protected $storage = null;
|
||||
|
||||
public function setup()
|
||||
{
|
||||
$this->storage = new Apc();
|
||||
}
|
||||
|
||||
public function tearDown()
|
||||
{
|
||||
apc_delete('dave::LOCK');
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Stiphle\Storage\LockWaitTimeoutException
|
||||
*/
|
||||
public function testLockThrowsLockWaitTimeoutException()
|
||||
{
|
||||
if (!ini_get('apc.enable_cli') && !ini_get('apcu.enable_cli')) {
|
||||
$this->markTestSkipped('APC and APCu needs enabling for the cli via apc.enable_cli=1 or apcu.enable_cli=1');
|
||||
}
|
||||
|
||||
$this->storage->lock('dave');
|
||||
$this->storage->lock('dave');
|
||||
}
|
||||
|
||||
|
||||
public function testLockRespectsLockWaitTimeoutValue()
|
||||
{
|
||||
if (!ini_get('apc.enable_cli') && !ini_get('apcu.enable_cli')) {
|
||||
$this->markTestSkipped('APC and APCu needs enabling for the cli via apc.enable_cli=1 or apcu.enable_cli=1');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test we can do this
|
||||
*/
|
||||
$this->storage->lock('dave');
|
||||
try {
|
||||
$start = microtime(1);
|
||||
$this->storage->lock('dave');
|
||||
} catch (LockWaitTimeoutException $e) {
|
||||
$caught = microtime(1);
|
||||
$diff = $caught - $start;
|
||||
if (round($diff) != 1) {
|
||||
$this->markTestSkipped("Don't think the timings will be accurate enough, expected exception after 1 second, was $diff");
|
||||
}
|
||||
}
|
||||
|
||||
$this->storage->setLockWaitTimeout(2000);
|
||||
try {
|
||||
$start = microtime(1);
|
||||
$this->storage->lock('dave');
|
||||
$this->fail("should not get to this point");
|
||||
} catch (LockWaitTimeoutException $e) {
|
||||
$caught = microtime(1);
|
||||
$diff = $caught - $start;
|
||||
$this->assertEquals(2, round($diff), "Exception thrown after approximately 2000 milliseconds");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
/**
|
||||
* @package
|
||||
* @subpackage
|
||||
*/
|
||||
namespace Stiphle\Storage;
|
||||
|
||||
use \PHPUnit_Framework_TestCase;
|
||||
|
||||
/**
|
||||
* This file is part of Stiphle
|
||||
*
|
||||
* Copyright (c) 2011 Dave Marshall <dave.marshall@atstsolutuions.co.uk>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* TITLE
|
||||
*
|
||||
* DESCRIPTION
|
||||
*
|
||||
* @author Dave Marshall <david.marshall@atstsolutions.co.uk>
|
||||
*/
|
||||
class ProcessTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
protected $storage = null;
|
||||
|
||||
public function setup()
|
||||
{
|
||||
$this->storage = new Process();
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Stiphle\Storage\LockWaitTimeoutException
|
||||
*/
|
||||
public function testLockThrowsLockWaitTimeoutException()
|
||||
{
|
||||
$this->storage->lock('dave');
|
||||
$this->storage->lock('dave');
|
||||
}
|
||||
|
||||
|
||||
public function testLockRespectsLockWaitTimeoutValue()
|
||||
{
|
||||
/**
|
||||
* Test we can do this
|
||||
*/
|
||||
$this->storage->lock('dave');
|
||||
try {
|
||||
$start = microtime(1);
|
||||
$this->storage->lock('dave');
|
||||
} catch (LockWaitTimeoutException $e) {
|
||||
$caught = microtime(1);
|
||||
$diff = $caught - $start;
|
||||
if (round($diff) != 1) {
|
||||
$this->markTestSkipped("Don't think the timings will be accurate enough, expected exception after 1 second, was $diff");
|
||||
}
|
||||
}
|
||||
|
||||
$this->storage->setLockWaitTimeout(2000);
|
||||
try {
|
||||
$start = microtime(1);
|
||||
$this->storage->lock('dave');
|
||||
$this->fail("should not get to this point");
|
||||
} catch (LockWaitTimeoutException $e) {
|
||||
$caught = microtime(1);
|
||||
$diff = $caught - $start;
|
||||
$this->assertEquals(2, round($diff), "Exception thrown after approximately 2000 milliseconds");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
/* vim: set ts=4 sw=4 tw=0 et :*/
|
||||
|
||||
namespace Stiphle\Storage;
|
||||
|
||||
use \PHPUnit_Framework_TestCase;
|
||||
|
||||
class RedisTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testLockThrowsLockWaitTimeoutException()
|
||||
{
|
||||
$redisClient = $this->getMockBuilder(\Predis\Client::class)
|
||||
->setMethods(['set'])
|
||||
->getMock();
|
||||
|
||||
$redisClient->expects($this->at(0))
|
||||
->method('set')
|
||||
->with('dave::LOCK', 'LOCKED', 'PX', 3600, 'NX')
|
||||
->will($this->returnValue(1));
|
||||
|
||||
$redisClient->expects($this->any())
|
||||
->method('set')
|
||||
->with('dave::LOCK', 'LOCKED', 'PX', 3600, 'NX')
|
||||
->will($this->returnValue(null));
|
||||
|
||||
$this->expectException(\Stiphle\Storage\LockWaitTimeoutException::class);
|
||||
|
||||
$storage = new Redis($redisClient);
|
||||
|
||||
$storage->lock('dave');
|
||||
$storage->lock('dave');
|
||||
}
|
||||
|
||||
public function testStorageCanBeUnlocked()
|
||||
{
|
||||
$redisClient = $this->getMockBuilder(\Predis\Client::class)
|
||||
->setMethods(['del'])
|
||||
->getMock();
|
||||
|
||||
$redisClient->expects($this->once())
|
||||
->method('del')
|
||||
->with('dave::LOCK');
|
||||
|
||||
$storage = new Redis($redisClient);
|
||||
|
||||
$storage->unlock('dave');
|
||||
}
|
||||
}
|
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
/**
|
||||
* @package
|
||||
* @subpackage
|
||||
*/
|
||||
namespace Stiphle\Throttle;
|
||||
|
||||
use \PHPUnit_Framework_TestCase;
|
||||
use Storage\Process;
|
||||
|
||||
/**
|
||||
* This file is part of Stiphle
|
||||
*
|
||||
* Copyright (c) 2011 Dave Marshall <dave.marshall@atstsolutuions.co.uk>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* TITLE
|
||||
*
|
||||
* DESCRIPTION
|
||||
*
|
||||
* @author Dave Marshall <david.marshall@atstsolutions.co.uk>
|
||||
*/
|
||||
class LeakyBucketTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
protected $storage = null;
|
||||
|
||||
public function setup()
|
||||
{
|
||||
$this->throttle = new LeakyBucket();
|
||||
}
|
||||
|
||||
/**
|
||||
* This test assumes your machine is capable of processing the first five
|
||||
* calls in less that a second :)
|
||||
*
|
||||
* Nothing special here, ideally we need to mock the storage out and test it
|
||||
* with different values etc
|
||||
*/
|
||||
public function testGetEstimate()
|
||||
{
|
||||
$this->assertEquals(0, $this->throttle->throttle('dave', 5, 1000));
|
||||
$this->assertEquals(0, $this->throttle->throttle('dave', 5, 1000));
|
||||
$this->assertEquals(0, $this->throttle->throttle('dave', 5, 1000));
|
||||
$this->assertEquals(0, $this->throttle->throttle('dave', 5, 1000));
|
||||
$this->assertEquals(0, $this->throttle->throttle('dave', 5, 1000));
|
||||
$this->assertGreaterThan(0, $this->throttle->getEstimate('dave', 5, 1000));
|
||||
$this->assertGreaterThan(0, $this->throttle->throttle('dave', 5, 1000));
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace Stiphle\Throttle;
|
||||
|
||||
use \PHPUnit_Framework_TestCase;
|
||||
use Storage\Process;
|
||||
|
||||
/**
|
||||
* This file is part of Stiphle
|
||||
*
|
||||
* Copyright (c) 2011 Dave Marshall <dave.marshall@atstsolutuions.co.uk>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* TITLE
|
||||
*
|
||||
* DESCRIPTION
|
||||
*
|
||||
* @author Dave Marshall <david.marshall@atstsolutions.co.uk>
|
||||
*/
|
||||
class TimeWindowTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
protected $storage = null;
|
||||
|
||||
public function setup()
|
||||
{
|
||||
$this->throttle = new TimeWindow();
|
||||
}
|
||||
|
||||
/**
|
||||
* Really crap test here, without mocking the system time, it's difficult to
|
||||
* know when you're going to throttled...
|
||||
*/
|
||||
public function testGetEstimate()
|
||||
{
|
||||
$timeout = strtotime('+5 seconds', microtime(1));
|
||||
$count = 0;
|
||||
while (microtime(1) < $timeout) {
|
||||
$wait = $this->throttle->throttle('dave', 5, 1000);
|
||||
if (microtime(1) < $timeout) {
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
|
||||
$this->assertEquals(25, $count);
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user