Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/style.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: "8.3"
php-version: "8.4"
extensions: json, dom, curl, libxml, mbstring
coverage: none

Expand Down
15 changes: 1 addition & 14 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
strategy:
fail-fast: false
matrix:
php: [ 8.1, 8.2, 8.3 ]
php: [ 8.1, 8.2, 8.3, 8.4 ]
laravel: [ '10.*', '11.*', '12.*' ]
dependency-version: [ prefer-lowest, prefer-stable ]

Expand All @@ -29,19 +29,6 @@ jobs:

name: P${{ matrix.php }} / L${{ matrix.laravel }} / ${{ matrix.dependency-version }}

services:
mysql:
image: mysql:8.0
env:
MYSQL_DATABASE: fast_paginate
MYSQL_HOST: 127.0.0.1
MYSQL_USER: test
MYSQL_PASSWORD: root
MYSQL_ROOT_PASSWORD: root
ports:
- 3306:3306
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3

steps:
- name: Checkout code
uses: actions/checkout@v4
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ node_modules
docs/*.blade.php
docs/**/*.blade.php
.phpunit.cache/test-results
.claude/settings.local.json
14 changes: 7 additions & 7 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@
}
],
"require": {
"php": "^8.0",
"illuminate/support": "^10|^11.0|^12.0",
"illuminate/cache": "^10|^11.0|^12.0",
"illuminate/console": "^10|^11.|^12.00"
"php": "^8.1",
"illuminate/support": "^10.0|^11.0|^12.0",
"illuminate/cache": "^10.0|^11.0|^12.0",
"illuminate/console": "^10.0|^11.0|^12.0"
},
"require-dev": {
"mockery/mockery": "^1.3.3",
"phpunit/phpunit": ">=8.5.23|^9|^10",
"orchestra/testbench": "^8.0|^9.0|^10.0"
"mockery/mockery": "^1.6",
"phpunit/phpunit": "^10.5|^11.0",
"orchestra/testbench": "^8.21|^9.5|^10.0"
},
"autoload": {
"psr-4": {
Expand Down
48 changes: 26 additions & 22 deletions src/Arbiter.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,102 +6,106 @@

namespace AaronFrancis\Flaky;

use Illuminate\Contracts\Cache\Repository;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Traits\Macroable;
use Throwable;

class Arbiter
{
use Macroable;

public $failuresAllowedForSeconds = 60 * 60 * 24 * 365 * 10;
public int $failuresAllowedForSeconds = 60 * 60 * 24 * 365 * 10;

public $consecutiveFailuresAllowed = INF;
public int|float $consecutiveFailuresAllowed = INF;

public $totalFailuresAllowed = INF;
public int|float $totalFailuresAllowed = INF;

/** @var callable(Throwable): void */
public $handleFailuresWith;

protected $key;
protected string $key;

protected $totalFailures;
protected int $totalFailures;

protected $consecutiveFailures;
protected int $consecutiveFailures;

protected $deadline;
protected ?int $deadline;

protected $cache;
protected Repository $cache;

public function __construct($id)
public function __construct(string $id)
{
$this->key = "flaky::$id";
$this->cache = Cache::store();

/** @var array{total?: int, consecutive?: int, deadline?: int|null} $stats */
$stats = $this->cache->get($this->key, []);

$this->totalFailures = Arr::get($stats, 'total', 0);
$this->consecutiveFailures = Arr::get($stats, 'consecutive', 0);
$this->deadline = Arr::get($stats, 'deadline');

$this->handleFailuresWith = function ($e) {
$this->handleFailuresWith = function (Throwable $e): never {
throw $e;
};
}

public function handle($exception)
public function handle(?Throwable $exception): void
{
$this->deadline = $this->deadline ?? $this->freshDeadline();

if ($exception) {
if ($exception !== null) {
$this->totalFailures++;
$this->consecutiveFailures++;
}

$this->updateCachedStats($exception);

if (!is_null($exception) && $this->outOfBounds()) {
if ($exception !== null && $this->outOfBounds()) {
$this->callHandler($exception);
}
}

public function handleFailures($callback)
public function handleFailures(callable $callback): void
{
$this->handleFailuresWith = $callback;
}

public function outOfBounds()
public function outOfBounds(): bool
{
return $this->tooManyConsecutiveFailures() || $this->tooManyTotalFailures() || $this->beyondDeadline();
}

public function tooManyConsecutiveFailures()
public function tooManyConsecutiveFailures(): bool
{
return $this->consecutiveFailures > $this->consecutiveFailuresAllowed;
}

public function tooManyTotalFailures()
public function tooManyTotalFailures(): bool
{
return $this->totalFailures > $this->totalFailuresAllowed;
}

public function beyondDeadline()
public function beyondDeadline(): bool
{
return now()->timestamp > $this->deadline;
}

protected function callHandler($exception)
protected function callHandler(Throwable $exception): void
{
call_user_func($this->handleFailuresWith, $exception);
}

protected function freshDeadline()
protected function freshDeadline(): int
{
return now()->addSeconds($this->failuresAllowedForSeconds)->timestamp;
}

protected function updateCachedStats($exception)
protected function updateCachedStats(?Throwable $exception): void
{
$failed = !is_null($exception);
$failed = $exception !== null;

$stats = $failed ? [
// Reset if we passed, otherwise just store the incremented value.
Expand Down
Loading