Skip to content

Commit 94ffda8

Browse files
committed
Handle failures ourselves, handle exceptions you already have
1 parent b6a9cdb commit 94ffda8

File tree

4 files changed

+126
-18
lines changed

4 files changed

+126
-18
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@ vendor
1010
node_modules
1111

1212
docs/*.blade.php
13-
docs/**/*.blade.php
13+
docs/**/*.blade.php
14+
.phpunit.cache/test-results

src/Arbiter.php

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class Arbiter
1919

2020
public $totalFailuresAllowed = INF;
2121

22-
public $throw = true;
22+
public $handleFailuresWith;
2323

2424
protected $key;
2525

@@ -41,10 +41,19 @@ public function __construct($id)
4141
$this->totalFailures = Arr::get($stats, 'total', 0);
4242
$this->consecutiveFailures = Arr::get($stats, 'consecutive', 0);
4343
$this->deadline = Arr::get($stats, 'deadline');
44+
45+
$this->handleFailuresWith = function ($e) {
46+
throw $e;
47+
};
4448
}
4549

46-
public function handle($exception)
50+
public function handle($exception, $bypassProtections = false)
4751
{
52+
if ($bypassProtections) {
53+
$this->callHandler($exception);
54+
return;
55+
}
56+
4857
$this->deadline = $this->deadline ?? $this->freshDeadline();
4958

5059
if ($exception) {
@@ -55,10 +64,15 @@ public function handle($exception)
5564
$this->updateCachedStats($exception);
5665

5766
if ($this->outOfBounds() && !is_null($exception)) {
58-
$this->throw ? throw $exception : report($exception);
67+
$this->callHandler($exception);
5968
}
6069
}
6170

71+
public function handleFailures($callback)
72+
{
73+
$this->handleFailuresWith = $callback;
74+
}
75+
6276
public function outOfBounds()
6377
{
6478
return $this->tooManyConsecutiveFailures() || $this->tooManyTotalFailures() || $this->beyondDeadline();
@@ -79,6 +93,11 @@ public function beyondDeadline()
7993
return now()->timestamp > $this->deadline;
8094
}
8195

96+
protected function callHandler($exception)
97+
{
98+
call_user_func($this->handleFailuresWith, $exception);
99+
}
100+
82101
protected function freshDeadline()
83102
{
84103
return now()->addSeconds($this->failuresAllowedForSeconds)->timestamp;

src/Flaky.php

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -45,26 +45,38 @@ public function __construct($id)
4545

4646
public function run(callable $callable)
4747
{
48-
$failed = false;
4948
$exception = null;
5049
$value = null;
5150

5251
try {
5352
$value = retry($this->retry['times'], $callable, $this->retry['sleep'], $this->retry['when']);
5453
} catch (Throwable $e) {
55-
$failed = true;
5654
$exception = $e;
5755
}
5856

59-
if ($this->protectionsBypassed() && $failed) {
60-
throw $exception;
61-
}
62-
63-
$this->arbiter->handle($exception);
57+
$this->arbiter->handle($exception, $this->protectionsBypassed());
6458

6559
return new Result($value, $exception);
6660
}
6761

62+
public function handle(Throwable $exception = null)
63+
{
64+
return $this->run(function () use ($exception) {
65+
if (!is_null($exception)) {
66+
throw $exception;
67+
}
68+
});
69+
}
70+
71+
public function disableLocally()
72+
{
73+
if (app()->environment('local')) {
74+
$this->disableFlakyProtection();
75+
}
76+
77+
return $this;
78+
}
79+
6880
public function disableFlakyProtection($disabled = true)
6981
{
7082
$this->flakyProtectionDisabled = $disabled;
@@ -84,18 +96,25 @@ public function retry($times = 0, $sleepMilliseconds = 0, $when = null)
8496
return $this;
8597
}
8698

87-
public function reportFailures()
99+
public function handleFailures($callback)
88100
{
89-
$this->arbiter->throw = false;
101+
$this->arbiter->handleFailures($callback);
90102

91103
return $this;
92104
}
93105

94-
public function throwFailures()
106+
public function reportFailures()
95107
{
96-
$this->arbiter->throw = true;
108+
return $this->handleFailures(function ($e) {
109+
report($e);
110+
});
111+
}
97112

98-
return $this;
113+
public function throwFailures()
114+
{
115+
return $this->handleFailures(function ($e) {
116+
throw $e;
117+
});
99118
}
100119

101120
public function allowFailuresFor($seconds = 0, $minutes = 0, $hours = 0, $days = 0)

tests/Unit/BasicTest.php

Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,7 @@ public function too_many_total_throw()
9393
/** @test */
9494
public function reported_instead_of_thrown()
9595
{
96-
$handler = new class
97-
{
96+
$handler = new class {
9897
public $reported;
9998

10099
public function report(Throwable $e)
@@ -117,4 +116,74 @@ public function report(Throwable $e)
117116
$this->assertNotNull($handler->reported);
118117
$this->assertInstanceOf(Exception::class, $handler->reported);
119118
}
119+
120+
/** @test */
121+
public function can_disable()
122+
{
123+
$this->expectException(Exception::class);
124+
$this->expectExceptionMessage('Oops');
125+
126+
Flaky::make(__FUNCTION__)
127+
->allowFailuresForADay()
128+
->disableFlakyProtection()
129+
->run(function () {
130+
throw new Exception('Oops');
131+
});
132+
133+
config(['app.env' => 'production']);
134+
135+
}
136+
137+
/** @test */
138+
public function can_disable_locally()
139+
{
140+
Flaky::make(__FUNCTION__)
141+
->allowFailuresForADay()
142+
->run(function () {
143+
throw new Exception('Oops');
144+
});
145+
146+
$this->app->detectEnvironment(function () {
147+
return 'local';
148+
});
149+
150+
$this->expectException(Exception::class);
151+
$this->expectExceptionMessage('Oops');
152+
153+
Flaky::make(__FUNCTION__)
154+
->allowFailuresForADay()
155+
->disableLocally()
156+
->run(function () {
157+
throw new Exception('Oops');
158+
});
159+
}
160+
161+
/** @test */
162+
public function can_handle_failures_ourselves()
163+
{
164+
$caught = null;
165+
$handled = false;
166+
167+
Flaky::make(__FUNCTION__)
168+
->allowConsecutiveFailures(0)
169+
->handleFailures(function ($e) use (&$caught, &$handled) {
170+
$caught = $e;
171+
$handled = true;
172+
})
173+
->run(function () {
174+
throw new Exception('Oops');
175+
});
176+
177+
$this->assertTrue($handled);
178+
$this->assertInstanceOf(Exception::class, $caught);
179+
}
180+
181+
/** @test */
182+
public function can_pass_in_our_own_exception()
183+
{
184+
$result = Flaky::make(__FUNCTION__)->handle(new Exception('Oops'));
185+
186+
$this->assertInstanceOf(Result::class, $result);
187+
}
188+
120189
}

0 commit comments

Comments
 (0)