chore: 添加缺失的静态资源文件
添加多个前端界面所需的图片、字体、音频等静态资源文件,包括: - 管理员后台的图标、背景图、组件图片 - 移动端页面的商品图片、用户中心图片、活动图标 - 第三方库的示例图片和字体文件 - 浏览器兼容性配置文件 - 图标字体文件(eot、ttf、woff、woff2格式) 这些资源是前端界面正常显示所必需的,之前可能因版本控制忽略或遗漏而缺失。
This commit is contained in:
3
vendor/stechstudio/backoff/.gitignore
vendored
Executable file
3
vendor/stechstudio/backoff/.gitignore
vendored
Executable file
@@ -0,0 +1,3 @@
|
||||
vendor
|
||||
.idea
|
||||
composer.lock
|
||||
21
vendor/stechstudio/backoff/LICENSE.md
vendored
Executable file
21
vendor/stechstudio/backoff/LICENSE.md
vendored
Executable file
@@ -0,0 +1,21 @@
|
||||
# The MIT License (MIT)
|
||||
|
||||
Copyright (c) Signature Tech Studio, Inc <info@stechstudio.com>
|
||||
|
||||
> 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.
|
||||
216
vendor/stechstudio/backoff/README.md
vendored
Executable file
216
vendor/stechstudio/backoff/README.md
vendored
Executable file
@@ -0,0 +1,216 @@
|
||||
# PHP Backoff
|
||||
|
||||
[](https://scrutinizer-ci.com/g/stechstudio/backoff)
|
||||
[](LICENSE.md)
|
||||
[](https://scrutinizer-ci.com/g/stechstudio/backoff)
|
||||
[](https://packagist.org/packages/stechstudio/backoff)
|
||||
|
||||
Easily wrap your code with retry functionality. This library provides:
|
||||
|
||||
1. 4 backoff strategies (plus the ability to use your own)
|
||||
2. Optional jitter / randomness to spread out retries and minimize collisions
|
||||
3. Wait time cap
|
||||
4. Callbacks for custom retry logic or error handling
|
||||
|
||||
## Installation
|
||||
|
||||
```
|
||||
composer require stechstudio/backoff
|
||||
```
|
||||
|
||||
## Defaults
|
||||
|
||||
This library provides sane defaults so you can hopefully just jump in for most of your use cases.
|
||||
|
||||
By default the backoff is quadratic with a 100ms base time (`attempt^2 * 100`), a max of 5 retries, and no jitter.
|
||||
|
||||
## Quickstart
|
||||
|
||||
The simplest way to use Backoff is with the global `backoff` helper function:
|
||||
|
||||
```
|
||||
$result = backoff(function() {
|
||||
return doSomeWorkThatMightFail();
|
||||
});
|
||||
```
|
||||
|
||||
If successful `$result` will contain the result of the closure. If max attempts are exceeded the inner exception is re-thrown.
|
||||
|
||||
You can of course provide other options via the helper method if needed.
|
||||
|
||||
Method parameters are `$callback`, `$maxAttempts`, `$strategy`, `$waitCap`, `$useJitter`.
|
||||
|
||||
## Backoff class usage
|
||||
|
||||
The Backoff class constructor parameters are `$maxAttempts`, `$strategy`, `$waitCap`, `$useJitter`.
|
||||
|
||||
```
|
||||
$backoff = new Backoff(10, 'exponential', 10000, true);
|
||||
$result = $backoff->run(function() {
|
||||
return doSomeWorkThatMightFail();
|
||||
});
|
||||
```
|
||||
|
||||
Or if you are injecting the Backoff class with a dependency container, you can set it up with setters after the fact. Note that setters are chainable.
|
||||
|
||||
```
|
||||
// Assuming a fresh instance of $backoff was handed to you
|
||||
$result = $backoff
|
||||
->setStrategy('constant')
|
||||
->setMaxAttempts(10)
|
||||
->enableJitter()
|
||||
->run(function() {
|
||||
return doSomeWorkThatMightFail();
|
||||
});
|
||||
```
|
||||
|
||||
## Changing defaults
|
||||
|
||||
If you find you want different defaults, you can modify them via static class properties:
|
||||
|
||||
```
|
||||
Backoff::$defaultMaxAttempts = 10;
|
||||
Backoff::$defaultStrategy = 'exponential';
|
||||
Backoff::$defaultJitterEnabled = true;
|
||||
```
|
||||
|
||||
You might want to do this somewhere in your application bootstrap for example. These defaults will be used anytime you create an instance of the Backoff class or use the `backoff()` helper function.
|
||||
|
||||
## Strategies
|
||||
|
||||
There are four built-in strategies available: constant, linear, polynomial, and exponential.
|
||||
|
||||
The default base time for all strategies is 100 milliseconds.
|
||||
|
||||
### Constant
|
||||
|
||||
```
|
||||
$strategy = new ConstantStrategy(500);
|
||||
```
|
||||
|
||||
This strategy will sleep for 500 milliseconds on each retry loop.
|
||||
|
||||
### Linear
|
||||
|
||||
```
|
||||
$strategy = new LinearStrategy(200);
|
||||
```
|
||||
|
||||
This strategy will sleep for `attempt * baseTime`, providing linear backoff starting at 200 milliseconds.
|
||||
|
||||
### Polynomial
|
||||
|
||||
```
|
||||
$strategy = new PolynomialStrategy(100, 3);
|
||||
```
|
||||
|
||||
This strategy will sleep for `(attempt^degree) * baseTime`, so in this case `(attempt^3) * 100`.
|
||||
|
||||
The default degree if none provided is 2, effectively quadratic time.
|
||||
|
||||
### Exponential
|
||||
|
||||
```
|
||||
$strategy = new ExponentialStrategy(100);
|
||||
```
|
||||
|
||||
This strategy will sleep for `(2^attempt) * baseTime`.
|
||||
|
||||
## Specifying strategy
|
||||
|
||||
In our earlier code examples we specified the strategy as a string:
|
||||
|
||||
```
|
||||
backoff(function() {
|
||||
...
|
||||
}, 10, 'constant');
|
||||
|
||||
// OR
|
||||
|
||||
$backoff = new Backoff(10, 'constant');
|
||||
```
|
||||
|
||||
This would use the `ConstantStrategy` with defaults, effectively giving you a 100 millisecond sleep time.
|
||||
|
||||
You can create the strategy instance yourself in order to modify these defaults:
|
||||
|
||||
```
|
||||
backoff(function() {
|
||||
...
|
||||
}, 10, new LinearStrategy(500));
|
||||
|
||||
// OR
|
||||
|
||||
$backoff = new Backoff(10, new LinearStrategy(500));
|
||||
```
|
||||
|
||||
You can also pass in an integer as the strategy, will translates to a ConstantStrategy with the integer as the base time in milliseconds:
|
||||
|
||||
```
|
||||
backoff(function() {
|
||||
...
|
||||
}, 10, 1000);
|
||||
|
||||
// OR
|
||||
|
||||
$backoff = new Backoff(10, 1000);
|
||||
```
|
||||
|
||||
Finally, you can pass in a closure as the strategy if you wish. This closure should receive an integer `attempt` and return a sleep time in milliseconds.
|
||||
|
||||
```
|
||||
backoff(function() {
|
||||
...
|
||||
}, 10, function($attempt) {
|
||||
return (100 * $attempt) + 5000;
|
||||
});
|
||||
|
||||
// OR
|
||||
|
||||
$backoff = new Backoff(10);
|
||||
$backoff->setStrategy(function($attempt) {
|
||||
return (100 * $attempt) + 5000;
|
||||
});
|
||||
```
|
||||
|
||||
## Wait cap
|
||||
|
||||
You may want to use a fast growing backoff time (like exponential) but then also set a max wait time so that it levels out after a while.
|
||||
|
||||
This cap can be provided as the fourth argument to the `backoff` helper function, or using the `setWaitCap()` method on the Backoff class.
|
||||
|
||||
## Jitter
|
||||
|
||||
If you have a lot of clients starting a job at the same time and encountering failures, any of the above backoff strategies could mean the workers continue to collide at each retry.
|
||||
|
||||
The solution for this is to add randomness. See here for a good explanation:
|
||||
|
||||
https://www.awsarchitectureblog.com/2015/03/backoff.html
|
||||
|
||||
You can enable jitter by passing `true` in as the fifth argument to the `backoff` helper function, or by using the `enableJitter()` method on the Backoff class.
|
||||
|
||||
We use the "FullJitter" approach outlined in the above article, where a random number between 0 and the sleep time provided by your selected strategy is used.
|
||||
|
||||
## Custom retry decider
|
||||
|
||||
By default Backoff will retry if an exception is encountered, and if it has not yet hit max retries.
|
||||
|
||||
You may provide your own retry decider for more advanced use cases. Perhaps you want to retry based on time rather than number of retries, or perhaps there are scenarios where you would want retry even when an exception was not encountered.
|
||||
|
||||
Provide the decider as a callback, or an instance of a class with an `__invoke` method. Backoff will hand it four parameters: the current attempt, max attempts, the last result received, and the exception if one was encountered. Your decider needs to return true or false.
|
||||
|
||||
```php
|
||||
$backoff->setDecider(function($attempt, $maxAttempts, $result, $exception = null) {
|
||||
return someCustomLogic();
|
||||
});
|
||||
```
|
||||
|
||||
## Error handler callback
|
||||
|
||||
You can provide a custom error handler to be notified anytime an exception occurs, even if we have yet to reach max attempts. This is a useful place to do logging for example.
|
||||
|
||||
```php
|
||||
$backoff->setErrorHandler(function($exception, $attempt, $maxAttempts) {
|
||||
Log::error("On run $attempt we hit a problem: " . $exception->getMessage());
|
||||
});
|
||||
```
|
||||
23
vendor/stechstudio/backoff/composer.json
vendored
Executable file
23
vendor/stechstudio/backoff/composer.json
vendored
Executable file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"name": "stechstudio/backoff",
|
||||
"description": "PHP library providing retry functionality with multiple backoff strategies and jitter support",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Joseph Szobody",
|
||||
"email": "joseph@stechstudio.com"
|
||||
}
|
||||
],
|
||||
"require": {},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "5.5.*"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4":{
|
||||
"STS\\Backoff\\": "src"
|
||||
},
|
||||
"files": [
|
||||
"src/helpers.php"
|
||||
]
|
||||
}
|
||||
}
|
||||
17
vendor/stechstudio/backoff/phpunit.xml
vendored
Executable file
17
vendor/stechstudio/backoff/phpunit.xml
vendored
Executable file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit backupGlobals="false"
|
||||
backupStaticAttributes="false"
|
||||
bootstrap="vendor/autoload.php"
|
||||
colors="true"
|
||||
convertErrorsToExceptions="true"
|
||||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
processIsolation="false"
|
||||
stopOnFailure="false"
|
||||
syntaxCheck="false">
|
||||
<testsuites>
|
||||
<testsuite name="Backoff Test Suite">
|
||||
<directory>./tests</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
</phpunit>
|
||||
348
vendor/stechstudio/backoff/src/Backoff.php
vendored
Executable file
348
vendor/stechstudio/backoff/src/Backoff.php
vendored
Executable file
@@ -0,0 +1,348 @@
|
||||
<?php
|
||||
namespace STS\Backoff;
|
||||
|
||||
use Exception;
|
||||
use InvalidArgumentException;
|
||||
use STS\Backoff\Strategies\ConstantStrategy;
|
||||
use STS\Backoff\Strategies\ExponentialStrategy;
|
||||
use STS\Backoff\Strategies\LinearStrategy;
|
||||
use STS\Backoff\Strategies\PolynomialStrategy;
|
||||
|
||||
/**
|
||||
* Class Retry
|
||||
* @package STS\Backoff
|
||||
*/
|
||||
class Backoff
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public static $defaultStrategy = "polynomial";
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
public static $defaultMaxAttempts = 5;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public static $defaultJitterEnabled = false;
|
||||
|
||||
/**
|
||||
* This callable should take an 'attempt' integer, and return a wait time in milliseconds
|
||||
*
|
||||
* @var callable
|
||||
*/
|
||||
protected $strategy;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $strategies = [
|
||||
'constant' => ConstantStrategy::class,
|
||||
'linear' => LinearStrategy::class,
|
||||
'polynomial' => PolynomialStrategy::class,
|
||||
'exponential' => ExponentialStrategy::class
|
||||
];
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $maxAttempts;
|
||||
|
||||
/**
|
||||
* The max wait time you want to allow, regardless of what the strategy says
|
||||
*
|
||||
* @var int|null In milliseconds
|
||||
*/
|
||||
protected $waitCap;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $useJitter = false;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $exceptions = [];
|
||||
|
||||
/**
|
||||
* This will decide whether to retry or not.
|
||||
* @var callable
|
||||
*/
|
||||
protected $decider;
|
||||
|
||||
/**
|
||||
* This receive any exceptions we encounter.
|
||||
* @var callable
|
||||
*/
|
||||
protected $errorHandler;
|
||||
|
||||
/**
|
||||
* @param int $maxAttempts
|
||||
* @param mixed $strategy
|
||||
* @param int $waitCap
|
||||
* @param bool $useJitter
|
||||
* @param callable $decider
|
||||
*/
|
||||
public function __construct(
|
||||
$maxAttempts = null,
|
||||
$strategy = null,
|
||||
$waitCap = null,
|
||||
$useJitter = null,
|
||||
$decider = null
|
||||
) {
|
||||
$this->setMaxAttempts($maxAttempts ?: self::$defaultMaxAttempts);
|
||||
$this->setStrategy($strategy ?: self::$defaultStrategy);
|
||||
$this->setJitter($useJitter ?: self::$defaultJitterEnabled);
|
||||
$this->setWaitCap($waitCap);
|
||||
$this->setDecider($decider ?: $this->getDefaultDecider());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param integer $attempts
|
||||
*/
|
||||
public function setMaxAttempts($attempts)
|
||||
{
|
||||
$this->maxAttempts = $attempts;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return integer
|
||||
*/
|
||||
public function getMaxAttempts()
|
||||
{
|
||||
return $this->maxAttempts;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int|null $cap
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setWaitCap($cap)
|
||||
{
|
||||
$this->waitCap = $cap;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int|null
|
||||
*/
|
||||
public function getWaitCap()
|
||||
{
|
||||
return $this->waitCap;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $useJitter
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setJitter($useJitter)
|
||||
{
|
||||
$this->useJitter = $useJitter;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function enableJitter()
|
||||
{
|
||||
$this->setJitter(true);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function disableJitter()
|
||||
{
|
||||
$this->setJitter(false);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function jitterEnabled()
|
||||
{
|
||||
return $this->useJitter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return callable
|
||||
*/
|
||||
public function getStrategy()
|
||||
{
|
||||
return $this->strategy;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $strategy
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setStrategy($strategy)
|
||||
{
|
||||
$this->strategy = $this->buildStrategy($strategy);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a callable strategy.
|
||||
*
|
||||
* @param mixed $strategy Can be a string that matches a key in $strategies, an instance of AbstractStrategy
|
||||
* (or any other instance that has an __invoke method), a callback function, or
|
||||
* an integer (which we interpret to mean you want a ConstantStrategy)
|
||||
*
|
||||
* @return callable
|
||||
*/
|
||||
protected function buildStrategy($strategy)
|
||||
{
|
||||
if (is_string($strategy) && array_key_exists($strategy, $this->strategies)) {
|
||||
return new $this->strategies[$strategy];
|
||||
}
|
||||
|
||||
if (is_callable($strategy)) {
|
||||
return $strategy;
|
||||
}
|
||||
|
||||
if (is_int($strategy)) {
|
||||
return new ConstantStrategy($strategy);
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException("Invalid strategy: " . $strategy);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param callable $callback
|
||||
*
|
||||
* @return mixed
|
||||
* @throws Exception
|
||||
*/
|
||||
public function run($callback)
|
||||
{
|
||||
$attempt = 0;
|
||||
$try = true;
|
||||
|
||||
while ($try) {
|
||||
|
||||
$result = null;
|
||||
$exception = null;
|
||||
|
||||
$this->wait($attempt);
|
||||
try {
|
||||
$result = call_user_func($callback);
|
||||
} catch (\Throwable $e) {
|
||||
if ($e instanceof \Error) {
|
||||
$e = new Exception($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
$this->exceptions[] = $e;
|
||||
$exception = $e;
|
||||
} catch (Exception $e) {
|
||||
$this->exceptions[] = $e;
|
||||
$exception = $e;
|
||||
}
|
||||
$try = call_user_func($this->decider, ++$attempt, $this->getMaxAttempts(), $result, $exception);
|
||||
|
||||
if($try && isset($this->errorHandler)) {
|
||||
call_user_func($this->errorHandler, $exception, $attempt, $this->getMaxAttempts());
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the decider callback
|
||||
* @param callable $callback
|
||||
* @return $this
|
||||
*/
|
||||
public function setDecider($callback)
|
||||
{
|
||||
$this->decider = $callback;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the error handler callback
|
||||
* @param callable $callback
|
||||
* @return $this
|
||||
*/
|
||||
public function setErrorHandler($callback)
|
||||
{
|
||||
$this->errorHandler = $callback;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a default decider that simply check exceptions and maxattempts
|
||||
* @return \Closure
|
||||
*/
|
||||
protected function getDefaultDecider()
|
||||
{
|
||||
return function ($retry, $maxAttempts, $result = null, $exception = null) {
|
||||
if($retry >= $maxAttempts && ! is_null($exception)) {
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
return $retry < $maxAttempts && !is_null($exception);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $attempt
|
||||
*/
|
||||
public function wait($attempt)
|
||||
{
|
||||
if ($attempt == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
usleep($this->getWaitTime($attempt) * 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $attempt
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getWaitTime($attempt)
|
||||
{
|
||||
$waitTime = call_user_func($this->getStrategy(), $attempt);
|
||||
|
||||
return $this->jitter($this->cap($waitTime));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $waitTime
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function cap($waitTime)
|
||||
{
|
||||
return is_int($this->getWaitCap())
|
||||
? min($this->getWaitCap(), $waitTime)
|
||||
: $waitTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $waitTime
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function jitter($waitTime)
|
||||
{
|
||||
return $this->jitterEnabled()
|
||||
? mt_rand(0, $waitTime)
|
||||
: $waitTime;
|
||||
}
|
||||
}
|
||||
57
vendor/stechstudio/backoff/src/Strategies/AbstractStrategy.php
vendored
Executable file
57
vendor/stechstudio/backoff/src/Strategies/AbstractStrategy.php
vendored
Executable file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
namespace STS\Backoff\Strategies;
|
||||
|
||||
/**
|
||||
* Class AbstractStrategy
|
||||
* @package STS\Backoff\Strategies
|
||||
*/
|
||||
abstract class AbstractStrategy
|
||||
{
|
||||
/**
|
||||
* Base wait time in ms
|
||||
* @var int
|
||||
*/
|
||||
protected $base = 100;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $jitter = true;
|
||||
|
||||
/**
|
||||
* AbstractStrategy constructor.
|
||||
*
|
||||
* @param int $base
|
||||
*/
|
||||
public function __construct($base = null)
|
||||
{
|
||||
if(is_int($base)) {
|
||||
$this->base = $base;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $attempt
|
||||
*
|
||||
* @return int Time to wait in ms
|
||||
*/
|
||||
abstract public function getWaitTime($attempt);
|
||||
|
||||
/**
|
||||
* @param int $attempt
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function __invoke($attempt)
|
||||
{
|
||||
return $this->getWaitTime($attempt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getBase()
|
||||
{
|
||||
return $this->base;
|
||||
}
|
||||
}
|
||||
19
vendor/stechstudio/backoff/src/Strategies/ConstantStrategy.php
vendored
Executable file
19
vendor/stechstudio/backoff/src/Strategies/ConstantStrategy.php
vendored
Executable file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
namespace STS\Backoff\Strategies;
|
||||
|
||||
/**
|
||||
* Class ConstantStrategy
|
||||
* @package STS\Backoff\Strategies
|
||||
*/
|
||||
class ConstantStrategy extends AbstractStrategy
|
||||
{
|
||||
/**
|
||||
* @param int $attempt
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getWaitTime($attempt)
|
||||
{
|
||||
return $this->base;
|
||||
}
|
||||
}
|
||||
22
vendor/stechstudio/backoff/src/Strategies/ExponentialStrategy.php
vendored
Executable file
22
vendor/stechstudio/backoff/src/Strategies/ExponentialStrategy.php
vendored
Executable file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
namespace STS\Backoff\Strategies;
|
||||
|
||||
/**
|
||||
* Class ExponentialStrategy
|
||||
* @package STS\Backoff\Strategies
|
||||
*/
|
||||
class ExponentialStrategy extends AbstractStrategy
|
||||
{
|
||||
/**
|
||||
* @param int $attempt
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getWaitTime($attempt)
|
||||
{
|
||||
return (int) ($attempt == 1
|
||||
? $this->base
|
||||
: pow(2, $attempt) * $this->base
|
||||
);
|
||||
}
|
||||
}
|
||||
19
vendor/stechstudio/backoff/src/Strategies/LinearStrategy.php
vendored
Executable file
19
vendor/stechstudio/backoff/src/Strategies/LinearStrategy.php
vendored
Executable file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
namespace STS\Backoff\Strategies;
|
||||
|
||||
/**
|
||||
* Class LinearStrategy
|
||||
* @package STS\Backoff\Strategies
|
||||
*/
|
||||
class LinearStrategy extends AbstractStrategy
|
||||
{
|
||||
/**
|
||||
* @param int $attempt
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getWaitTime($attempt)
|
||||
{
|
||||
return $attempt * $this->base;
|
||||
}
|
||||
}
|
||||
47
vendor/stechstudio/backoff/src/Strategies/PolynomialStrategy.php
vendored
Executable file
47
vendor/stechstudio/backoff/src/Strategies/PolynomialStrategy.php
vendored
Executable file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
namespace STS\Backoff\Strategies;
|
||||
|
||||
/**
|
||||
* Class PolynomialStrategy
|
||||
* @package STS\Backoff\Strategies
|
||||
*/
|
||||
class PolynomialStrategy extends AbstractStrategy
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $degree = 2;
|
||||
|
||||
/**
|
||||
* PolynomialStrategy constructor.
|
||||
*
|
||||
* @param int $degree
|
||||
* @param int $base
|
||||
*/
|
||||
public function __construct($base = null, $degree = null)
|
||||
{
|
||||
if(!is_null($degree)) {
|
||||
$this->degree = $degree;
|
||||
}
|
||||
|
||||
parent::__construct($base);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $attempt
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getWaitTime($attempt)
|
||||
{
|
||||
return (int) pow($attempt, $this->degree) * $this->base;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int|null
|
||||
*/
|
||||
public function getDegree()
|
||||
{
|
||||
return $this->degree;
|
||||
}
|
||||
}
|
||||
7
vendor/stechstudio/backoff/src/helpers.php
vendored
Executable file
7
vendor/stechstudio/backoff/src/helpers.php
vendored
Executable file
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
if(!function_exists('backoff')) {
|
||||
function backoff($callback, $maxAttempts = null, $strategy = null, $waitCap = null, $useJitter = null)
|
||||
{
|
||||
return (new \STS\Backoff\Backoff($maxAttempts, $strategy, $waitCap, $useJitter))->run($callback);
|
||||
}
|
||||
}
|
||||
320
vendor/stechstudio/backoff/tests/BackoffTest.php
vendored
Executable file
320
vendor/stechstudio/backoff/tests/BackoffTest.php
vendored
Executable file
@@ -0,0 +1,320 @@
|
||||
<?php
|
||||
namespace STS\Backoff;
|
||||
|
||||
use Exception;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use STS\Backoff\Strategies\ConstantStrategy;
|
||||
use STS\Backoff\Strategies\ExponentialStrategy;
|
||||
use STS\Backoff\Strategies\LinearStrategy;
|
||||
use STS\Backoff\Strategies\PolynomialStrategy;
|
||||
|
||||
class BackoffTest extends TestCase
|
||||
{
|
||||
public function testDefaults()
|
||||
{
|
||||
$b = new Backoff();
|
||||
|
||||
$this->assertEquals(5, $b->getMaxAttempts());
|
||||
$this->assertInstanceOf(PolynomialStrategy::class, $b->getStrategy());
|
||||
$this->assertFalse($b->jitterEnabled());
|
||||
}
|
||||
|
||||
public function testFluidApi()
|
||||
{
|
||||
$b = new Backoff();
|
||||
$result = $b
|
||||
->setStrategy('constant')
|
||||
->setMaxAttempts(10)
|
||||
->setWaitCap(5)
|
||||
->enableJitter();
|
||||
|
||||
$this->assertEquals(10, $b->getMaxAttempts());
|
||||
$this->assertEquals(5, $b->getWaitCap());
|
||||
$this->assertTrue($b->jitterEnabled());
|
||||
$this->assertInstanceOf(ConstantStrategy::class, $b->getStrategy());
|
||||
}
|
||||
|
||||
public function testChangingStaticDefaults()
|
||||
{
|
||||
Backoff::$defaultMaxAttempts = 15;
|
||||
Backoff::$defaultStrategy = "constant";
|
||||
Backoff::$defaultJitterEnabled = true;
|
||||
|
||||
$b = new Backoff();
|
||||
|
||||
$this->assertEquals(15, $b->getMaxAttempts());
|
||||
$this->assertInstanceOf(ConstantStrategy::class, $b->getStrategy());
|
||||
$this->assertTrue($b->jitterEnabled());
|
||||
|
||||
Backoff::$defaultStrategy = new LinearStrategy(250);
|
||||
|
||||
$b = new Backoff();
|
||||
|
||||
$this->assertInstanceOf(LinearStrategy::class, $b->getStrategy());
|
||||
|
||||
// Put them back!
|
||||
Backoff::$defaultMaxAttempts = 5;
|
||||
Backoff::$defaultStrategy = "polynomial";
|
||||
Backoff::$defaultJitterEnabled = false;
|
||||
}
|
||||
|
||||
public function testConstructorParams()
|
||||
{
|
||||
$b = new Backoff(10, "linear");
|
||||
|
||||
$this->assertEquals(10, $b->getMaxAttempts());
|
||||
$this->assertInstanceOf(LinearStrategy::class, $b->getStrategy());
|
||||
}
|
||||
|
||||
public function testStrategyKeys()
|
||||
{
|
||||
$b = new Backoff();
|
||||
|
||||
$b->setStrategy("constant");
|
||||
$this->assertInstanceOf(ConstantStrategy::class, $b->getStrategy());
|
||||
|
||||
$b->setStrategy("linear");
|
||||
$this->assertInstanceOf(LinearStrategy::class, $b->getStrategy());
|
||||
|
||||
$b->setStrategy("polynomial");
|
||||
$this->assertInstanceOf(PolynomialStrategy::class, $b->getStrategy());
|
||||
|
||||
$b->setStrategy("exponential");
|
||||
$this->assertInstanceOf(ExponentialStrategy::class, $b->getStrategy());
|
||||
}
|
||||
|
||||
public function testStrategyInstances()
|
||||
{
|
||||
$b = new Backoff();
|
||||
|
||||
$b->setStrategy(new ConstantStrategy());
|
||||
$this->assertInstanceOf(ConstantStrategy::class, $b->getStrategy());
|
||||
|
||||
$b->setStrategy(new LinearStrategy());
|
||||
$this->assertInstanceOf(LinearStrategy::class, $b->getStrategy());
|
||||
|
||||
$b->setStrategy(new PolynomialStrategy());
|
||||
$this->assertInstanceOf(PolynomialStrategy::class, $b->getStrategy());
|
||||
|
||||
$b->setStrategy(new ExponentialStrategy());
|
||||
$this->assertInstanceOf(ExponentialStrategy::class, $b->getStrategy());
|
||||
}
|
||||
|
||||
public function testClosureStrategy()
|
||||
{
|
||||
$b = new Backoff();
|
||||
|
||||
$strategy = function () {
|
||||
return "hi there";
|
||||
};
|
||||
|
||||
$b->setStrategy($strategy);
|
||||
|
||||
$this->assertEquals("hi there", call_user_func($b->getStrategy()));
|
||||
}
|
||||
|
||||
public function testIntegerReturnsConstantStrategy()
|
||||
{
|
||||
$b = new Backoff();
|
||||
|
||||
$b->setStrategy(500);
|
||||
|
||||
$this->assertInstanceOf(ConstantStrategy::class, $b->getStrategy());
|
||||
}
|
||||
|
||||
public function testInvalidStrategy()
|
||||
{
|
||||
$b = new Backoff();
|
||||
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
$b->setStrategy("foo");
|
||||
}
|
||||
|
||||
public function testWaitTimes()
|
||||
{
|
||||
$b = new Backoff(1, "linear");
|
||||
|
||||
$this->assertEquals(100, $b->getStrategy()->getBase());
|
||||
|
||||
$this->assertEquals(100, $b->getWaitTime(1));
|
||||
$this->assertEquals(200, $b->getWaitTime(2));
|
||||
}
|
||||
|
||||
public function testWaitCap()
|
||||
{
|
||||
$b = new Backoff(1, new LinearStrategy(5000));
|
||||
|
||||
$this->assertEquals(10000, $b->getWaitTime(2));
|
||||
|
||||
$b->setWaitCap(5000);
|
||||
|
||||
$this->assertEquals(5000, $b->getWaitTime(2));
|
||||
}
|
||||
|
||||
public function testWait()
|
||||
{
|
||||
$b = new Backoff(1, new LinearStrategy(50));
|
||||
|
||||
$start = microtime(true);
|
||||
|
||||
$b->wait(2);
|
||||
|
||||
$end = microtime(true);
|
||||
|
||||
$elapsedMS = ($end - $start) * 1000;
|
||||
|
||||
// We expect that this took just barely over the 100ms we asked for
|
||||
$this->assertTrue($elapsedMS > 90 && $elapsedMS < 150,
|
||||
sprintf("Expected elapsedMS between 100 & 110, got: $elapsedMS\n"));
|
||||
}
|
||||
|
||||
public function testSuccessfulWork()
|
||||
{
|
||||
$b = new Backoff();
|
||||
|
||||
$result = $b->run(function () {
|
||||
return "done";
|
||||
});
|
||||
|
||||
$this->assertEquals("done", $result);
|
||||
}
|
||||
|
||||
public function testFirstAttemptDoesNotCallStrategy()
|
||||
{
|
||||
$b = new Backoff();
|
||||
$b->setStrategy(function () {
|
||||
throw new \Exception("We shouldn't be here");
|
||||
});
|
||||
|
||||
$result = $b->run(function () {
|
||||
return "done";
|
||||
});
|
||||
|
||||
$this->assertEquals("done", $result);
|
||||
}
|
||||
|
||||
public function testFailedWorkReThrowsException()
|
||||
{
|
||||
$b = new Backoff(2, new ConstantStrategy(0));
|
||||
|
||||
$this->expectException(\Exception::class);
|
||||
$this->expectExceptionMessage("failure");
|
||||
|
||||
$b->run(function () {
|
||||
throw new \Exception("failure");
|
||||
});
|
||||
}
|
||||
|
||||
public function testHandleErrorsPhp7()
|
||||
{
|
||||
$b = new Backoff(2, new ConstantStrategy(0));
|
||||
|
||||
$this->expectException(\Exception::class);
|
||||
$this->expectExceptionMessage("Modulo by zero");
|
||||
|
||||
$b->run(function () {
|
||||
if (version_compare(PHP_VERSION, '7.0.0') >= 0) {
|
||||
return 1 % 0;
|
||||
} else {
|
||||
// Handle version < 7
|
||||
throw new Exception("Modulo by zero");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public function testAttempts()
|
||||
{
|
||||
$b = new Backoff(10, new ConstantStrategy(0));
|
||||
|
||||
$attempt = 0;
|
||||
|
||||
$result = $b->run(function () use (&$attempt) {
|
||||
$attempt++;
|
||||
|
||||
if ($attempt < 5) {
|
||||
throw new \Exception("failure");
|
||||
}
|
||||
|
||||
return "success";
|
||||
});
|
||||
|
||||
$this->assertEquals(5, $attempt);
|
||||
$this->assertEquals("success", $result);
|
||||
}
|
||||
|
||||
public function testCustomDeciderAttempts()
|
||||
{
|
||||
$b = new Backoff(10, new ConstantStrategy(0));
|
||||
$b->setDecider(
|
||||
function ($retry, $maxAttempts, $result = null, $exception = null) {
|
||||
if ($retry >= $maxAttempts || $result == "success") {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
);
|
||||
|
||||
$attempt = 0;
|
||||
|
||||
$result = $b->run(function () use (&$attempt) {
|
||||
$attempt++;
|
||||
|
||||
if ($attempt < 5) {
|
||||
throw new \Exception("failure");
|
||||
}
|
||||
|
||||
if ($attempt < 7) {
|
||||
return 'not yet';
|
||||
}
|
||||
|
||||
return "success";
|
||||
});
|
||||
|
||||
$this->assertEquals(7, $attempt);
|
||||
$this->assertEquals("success", $result);
|
||||
}
|
||||
|
||||
public function testErrorHandler()
|
||||
{
|
||||
$log = [];
|
||||
|
||||
$b = new Backoff(10, new ConstantStrategy(0));
|
||||
$b->setErrorHandler(function($exception, $attempt, $maxAttempts) use(&$log) {
|
||||
$log[] = "Attempt $attempt of $maxAttempts: " . $exception->getMessage();
|
||||
});
|
||||
|
||||
$attempt = 0;
|
||||
|
||||
$result = $b->run(function () use (&$attempt) {
|
||||
$attempt++;
|
||||
|
||||
if ($attempt < 5) {
|
||||
throw new \Exception("failure");
|
||||
}
|
||||
|
||||
return "success";
|
||||
});
|
||||
|
||||
$this->assertEquals(4, count($log));
|
||||
$this->assertEquals("Attempt 4 of 10: failure", array_pop($log));
|
||||
$this->assertEquals("success", $result);
|
||||
}
|
||||
|
||||
public function testJitter()
|
||||
{
|
||||
$b = new Backoff(10, new ConstantStrategy(1000));
|
||||
|
||||
// First without jitter
|
||||
$this->assertEquals(1000, $b->getWaitTime(1));
|
||||
|
||||
// Now with jitter
|
||||
$b->enableJitter();
|
||||
|
||||
// Because it's still possible that I could get 1000 back even with jitter, I'm going to generate two
|
||||
$waitTime1 = $b->getWaitTime(1);
|
||||
$waitTime2 = $b->getWaitTime(1);
|
||||
|
||||
// And I'm banking that I didn't hit the _extremely_ rare chance that both were randomly chosen to be 1000 still
|
||||
$this->assertTrue($waitTime1 < 1000 || $waitTime2 < 1000);
|
||||
}
|
||||
}
|
||||
69
vendor/stechstudio/backoff/tests/HelpersTest.php
vendored
Executable file
69
vendor/stechstudio/backoff/tests/HelpersTest.php
vendored
Executable file
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
namespace STS\Backoff;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use STS\Backoff\Strategies\ConstantStrategy;
|
||||
|
||||
class HelpersTest extends TestCase
|
||||
{
|
||||
public function testSuccessWithDefaults()
|
||||
{
|
||||
$result = backoff(function() {
|
||||
return "success";
|
||||
});
|
||||
|
||||
$this->assertEquals("success", $result);
|
||||
}
|
||||
|
||||
public function testFailureWithDefaults()
|
||||
{
|
||||
$this->expectException(\Exception::class);
|
||||
$this->expectExceptionMessage("failure");
|
||||
|
||||
backoff(function() {
|
||||
throw new \Exception("failure");
|
||||
}, 2);
|
||||
}
|
||||
|
||||
public function testStrategy()
|
||||
{
|
||||
$start = microtime(true);
|
||||
|
||||
// We're going to run a test for 100 attempts, just to verify we were able to
|
||||
// set our own strategy with a low sleep time.
|
||||
|
||||
try {
|
||||
backoff(function() {
|
||||
throw new \Exception("failure");
|
||||
}, 100, new ConstantStrategy(1));
|
||||
} catch(\Exception $e) {}
|
||||
|
||||
$end = microtime(true);
|
||||
|
||||
$elapsedMS = ($end - $start) * 1000;
|
||||
|
||||
// We expect that this took just a bit over the 100ms that we slept
|
||||
$this->assertTrue($elapsedMS > 100 && $elapsedMS < 200);
|
||||
}
|
||||
|
||||
public function testWaitCap()
|
||||
{
|
||||
$start = microtime(true);
|
||||
|
||||
// We're going to specify a really long sleep time, but with a short cap to override.
|
||||
|
||||
try {
|
||||
backoff(function() {
|
||||
throw new \Exception("failure");
|
||||
}, 2, new ConstantStrategy(100000), 100);
|
||||
} catch(\Exception $e) {}
|
||||
|
||||
$end = microtime(true);
|
||||
|
||||
$elapsedMS = ($end - $start) * 1000;
|
||||
|
||||
// We expect that this took just a bit over the 100ms that we slept
|
||||
$this->assertTrue($elapsedMS > 90 && $elapsedMS < 150,
|
||||
sprintf("Expected elapsedMS between 90 & 200, got: $elapsedMS\n"));
|
||||
}
|
||||
}
|
||||
23
vendor/stechstudio/backoff/tests/Strategies/ConstantStrategyTest.php
vendored
Executable file
23
vendor/stechstudio/backoff/tests/Strategies/ConstantStrategyTest.php
vendored
Executable file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
namespace STS\Backoff\Strategies;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class ConstantStrategyTest extends TestCase
|
||||
{
|
||||
public function testDefaults()
|
||||
{
|
||||
$s = new ConstantStrategy();
|
||||
|
||||
$this->assertEquals(100, $s->getBase());
|
||||
}
|
||||
|
||||
public function testWaitTimes()
|
||||
{
|
||||
$s = new ConstantStrategy(100);
|
||||
|
||||
$this->assertEquals(100, $s->getWaitTime(1));
|
||||
$this->assertEquals(100, $s->getWaitTime(2));
|
||||
$this->assertEquals(100, $s->getWaitTime(3));
|
||||
}
|
||||
}
|
||||
28
vendor/stechstudio/backoff/tests/Strategies/ExponentialStrategyTest.php
vendored
Executable file
28
vendor/stechstudio/backoff/tests/Strategies/ExponentialStrategyTest.php
vendored
Executable file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
namespace STS\Backoff\Strategies;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class ExponentialStrategyTest extends TestCase
|
||||
{
|
||||
public function testDefaults()
|
||||
{
|
||||
$s = new ExponentialStrategy();
|
||||
|
||||
$this->assertEquals(100, $s->getBase());
|
||||
}
|
||||
|
||||
public function testWaitTimes()
|
||||
{
|
||||
$s = new ExponentialStrategy(200);
|
||||
|
||||
$this->assertEquals(200, $s->getWaitTime(1));
|
||||
$this->assertEquals(800, $s->getWaitTime(2));
|
||||
$this->assertEquals(1600, $s->getWaitTime(3));
|
||||
$this->assertEquals(3200, $s->getWaitTime(4));
|
||||
$this->assertEquals(6400, $s->getWaitTime(5));
|
||||
$this->assertEquals(12800, $s->getWaitTime(6));
|
||||
$this->assertEquals(25600, $s->getWaitTime(7));
|
||||
$this->assertEquals(51200, $s->getWaitTime(8));
|
||||
}
|
||||
}
|
||||
23
vendor/stechstudio/backoff/tests/Strategies/LinearStrategyTest.php
vendored
Executable file
23
vendor/stechstudio/backoff/tests/Strategies/LinearStrategyTest.php
vendored
Executable file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
namespace STS\Backoff\Strategies;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class LinearStrategyTest extends TestCase
|
||||
{
|
||||
public function testDefaults()
|
||||
{
|
||||
$s = new LinearStrategy();
|
||||
|
||||
$this->assertEquals(100, $s->getBase());
|
||||
}
|
||||
|
||||
public function testWaitTimes()
|
||||
{
|
||||
$s = new LinearStrategy(100);
|
||||
|
||||
$this->assertEquals(100, $s->getWaitTime(1));
|
||||
$this->assertEquals(200, $s->getWaitTime(2));
|
||||
$this->assertEquals(300, $s->getWaitTime(3));
|
||||
}
|
||||
}
|
||||
29
vendor/stechstudio/backoff/tests/Strategies/PolynomialStrategyTest.php
vendored
Executable file
29
vendor/stechstudio/backoff/tests/Strategies/PolynomialStrategyTest.php
vendored
Executable file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
namespace STS\Backoff\Strategies;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class PolynomialStrategyTest extends TestCase
|
||||
{
|
||||
public function testDefaults()
|
||||
{
|
||||
$s = new PolynomialStrategy();
|
||||
|
||||
$this->assertEquals(100, $s->getBase());
|
||||
$this->assertEquals(2, $s->getDegree());
|
||||
}
|
||||
|
||||
public function testWaitTimes()
|
||||
{
|
||||
$s = new PolynomialStrategy(200, 2);
|
||||
|
||||
$this->assertEquals(200, $s->getWaitTime(1));
|
||||
$this->assertEquals(800, $s->getWaitTime(2));
|
||||
$this->assertEquals(1800, $s->getWaitTime(3));
|
||||
$this->assertEquals(3200, $s->getWaitTime(4));
|
||||
$this->assertEquals(5000, $s->getWaitTime(5));
|
||||
$this->assertEquals(7200, $s->getWaitTime(6));
|
||||
$this->assertEquals(9800, $s->getWaitTime(7));
|
||||
$this->assertEquals(12800, $s->getWaitTime(8));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user