Skip to content

Commit 3fd8a02

Browse files
committed
Added FetchExceptionHandler interface.
Added Stateless- and ExponentialSleep FetchExceptionHandler implementations. Changed FEH defacto type from callable to FetchExceptionHandler.
1 parent 1bae9b7 commit 3fd8a02

File tree

8 files changed

+91
-20
lines changed

8 files changed

+91
-20
lines changed

src/Connector/ConnectionContext.php

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
<?php
22
namespace ScriptFUSION\Porter\Connector;
33

4+
use ScriptFUSION\Porter\Connector\FetchExceptionHandler\FetchExceptionHandler;
5+
46
/**
57
* Specifies runtime connection settings and provides utility methods.
68
*/
@@ -24,7 +26,7 @@ final class ConnectionContext
2426

2527
private $maxFetchAttempts;
2628

27-
public function __construct($mustCache, callable $fetchExceptionHandler, $maxFetchAttempts)
29+
public function __construct($mustCache, FetchExceptionHandler $fetchExceptionHandler, $maxFetchAttempts)
2830
{
2931
$this->mustCache = (bool)$mustCache;
3032
$this->fetchExceptionHandler = $fetchExceptionHandler;
@@ -50,11 +52,13 @@ public function mustCache()
5052
*/
5153
public function retry(callable $callback)
5254
{
55+
$userHandlerReset = false;
56+
5357
return \ScriptFUSION\Retry\retry(
5458
$this->maxFetchAttempts,
5559
$callback,
56-
function (\Exception $exception) {
57-
// Throw exception if unrecoverable.
60+
function (\Exception $exception) use (&$userHandlerReset) {
61+
// Throw exception instead of retrying, if unrecoverable.
5862
if (!$exception instanceof RecoverableConnectorException) {
5963
throw $exception;
6064
}
@@ -64,7 +68,12 @@ function (\Exception $exception) {
6468
call_user_func($this->providerFetchExceptionHandler, $exception);
6569
}
6670

67-
// TODO Clone exception handler to avoid persisting state between calls.
71+
if (!$userHandlerReset) {
72+
$this->fetchExceptionHandler->reset();
73+
$userHandlerReset = true;
74+
}
75+
76+
// TODO: Remove call_user_func calls when PHP 5 support dropped.
6877
call_user_func($this->fetchExceptionHandler, $exception);
6978
}
7079
);
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
namespace ScriptFUSION\Porter\Connector\FetchExceptionHandler;
3+
4+
use ScriptFUSION\Retry\ExceptionHandler\ExponentialBackoffExceptionHandler;
5+
6+
/**
7+
* Sleeps for an exponentially increasing series of delays specified in microseconds.
8+
*/
9+
class ExponentialSleepFetchExceptionHandler implements FetchExceptionHandler
10+
{
11+
private $handler;
12+
13+
public function reset()
14+
{
15+
$this->handler = new ExponentialBackoffExceptionHandler;
16+
}
17+
18+
public function __invoke(\Exception $exception)
19+
{
20+
call_user_func($this->handler, $exception);
21+
}
22+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
namespace ScriptFUSION\Porter\Connector\FetchExceptionHandler;
3+
4+
interface FetchExceptionHandler
5+
{
6+
public function reset();
7+
8+
public function __invoke(\Exception $exception);
9+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
namespace ScriptFUSION\Porter\Connector\FetchExceptionHandler;
3+
4+
/**
5+
* Contains a stateless fetch exception handler that does not respond to reset() calls.
6+
*/
7+
final class StatelessFetchExceptionHandler implements FetchExceptionHandler
8+
{
9+
private $handler;
10+
11+
public function __construct(callable $handler)
12+
{
13+
$this->handler = $handler;
14+
}
15+
16+
public function reset()
17+
{
18+
// Intentionally empty.
19+
}
20+
21+
public function __invoke(\Exception $exception)
22+
{
23+
call_user_func($this->handler, $exception);
24+
}
25+
}

src/Specification/ImportSpecification.php

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
<?php
22
namespace ScriptFUSION\Porter\Specification;
33

4+
use ScriptFUSION\Porter\Connector\FetchExceptionHandler\ExponentialSleepFetchExceptionHandler;
5+
use ScriptFUSION\Porter\Connector\FetchExceptionHandler\FetchExceptionHandler;
46
use ScriptFUSION\Porter\Provider\Resource\ProviderResource;
57
use ScriptFUSION\Porter\Transform\Transformer;
6-
use ScriptFUSION\Retry\ExceptionHandler\ExponentialBackoffExceptionHandler;
78

89
/**
910
* Specifies which resource to import and how the data should be transformed.
@@ -237,21 +238,22 @@ final public function setMaxFetchAttempts($attempts)
237238
/**
238239
* Gets the exception handler invoked each time a fetch attempt fails.
239240
*
240-
* @return callable Exception handler.
241+
* @return FetchExceptionHandler Fetch exception handler.
241242
*/
242243
final public function getFetchExceptionHandler()
243244
{
244-
return $this->fetchExceptionHandler ?: $this->fetchExceptionHandler = new ExponentialBackoffExceptionHandler;
245+
return $this->fetchExceptionHandler ?: $this->fetchExceptionHandler
246+
= new ExponentialSleepFetchExceptionHandler;
245247
}
246248

247249
/**
248250
* Sets the exception handler invoked each time a fetch attempt fails.
249251
*
250-
* @param callable $fetchExceptionHandler Exception handler.
252+
* @param FetchExceptionHandler $fetchExceptionHandler Fetch exception handler.
251253
*
252254
* @return $this
253255
*/
254-
final public function setFetchExceptionHandler(callable $fetchExceptionHandler)
256+
final public function setFetchExceptionHandler(FetchExceptionHandler $fetchExceptionHandler)
255257
{
256258
$this->fetchExceptionHandler = $fetchExceptionHandler;
257259

test/FixtureFactory.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
namespace ScriptFUSIONTest;
33

44
use ScriptFUSION\Porter\Connector\ConnectionContext;
5+
use ScriptFUSION\Porter\Connector\FetchExceptionHandler\StatelessFetchExceptionHandler;
56
use ScriptFUSION\Porter\Specification\ImportSpecification;
67
use ScriptFUSION\StaticClass;
78

@@ -25,9 +26,9 @@ public static function buildConnectionContext(
2526
) {
2627
return new ConnectionContext(
2728
$cacheAdvice,
28-
$fetchExceptionHandler ?: function () {
29+
$fetchExceptionHandler ?: new StatelessFetchExceptionHandler(static function () {
2930
// Intentionally empty.
30-
},
31+
}),
3132
$maxFetchAttempts
3233
);
3334
}

test/Integration/Porter/PorterTest.php

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
use ScriptFUSION\Porter\Connector\ConnectionContext;
1313
use ScriptFUSION\Porter\Connector\Connector;
1414
use ScriptFUSION\Porter\Connector\ConnectorOptions;
15+
use ScriptFUSION\Porter\Connector\FetchExceptionHandler\FetchExceptionHandler;
16+
use ScriptFUSION\Porter\Connector\FetchExceptionHandler\StatelessFetchExceptionHandler;
1517
use ScriptFUSION\Porter\Connector\ImportConnector;
1618
use ScriptFUSION\Porter\Connector\RecoverableConnectorException;
1719
use ScriptFUSION\Porter\ImportException;
@@ -25,7 +27,6 @@
2527
use ScriptFUSION\Porter\Specification\StaticDataImportSpecification;
2628
use ScriptFUSION\Porter\Transform\FilterTransformer;
2729
use ScriptFUSION\Porter\Transform\Transformer;
28-
use ScriptFUSION\Retry\ExceptionHandler\ExponentialBackoffExceptionHandler;
2930
use ScriptFUSION\Retry\FailingTooHardException;
3031
use ScriptFUSIONTest\MockFactory;
3132

@@ -286,15 +287,17 @@ public function testUnrecoverableException()
286287
}
287288

288289
/**
289-
* Tests that a when custom fetch exception handler is specified and the connector throws a recoverable exception
290+
* Tests that when a custom fetch exception handler is specified and the connector throws a recoverable exception
290291
* type, the handler is called on each retry.
291292
*/
292293
public function testCustomFetchExceptionHandler()
293294
{
294295
$this->specification->setFetchExceptionHandler(
295-
\Mockery::mock(ExponentialBackoffExceptionHandler::class)
296+
\Mockery::mock(FetchExceptionHandler::class)
297+
->shouldReceive('reset')
298+
->once()
296299
->shouldReceive('__invoke')
297-
->times(ImportSpecification::DEFAULT_FETCH_ATTEMPTS - 1)
300+
->times(ImportSpecification::DEFAULT_FETCH_ATTEMPTS - 1)
298301
->getMock()
299302
);
300303

@@ -310,9 +313,9 @@ public function testCustomFetchExceptionHandler()
310313
*/
311314
public function testCustomProviderFetchExceptionHandler()
312315
{
313-
$this->specification->setFetchExceptionHandler(function () {
316+
$this->specification->setFetchExceptionHandler(new StatelessFetchExceptionHandler(function () {
314317
throw new \LogicException('This exception must not be thrown!');
315-
});
318+
}));
316319

317320
$this->arrangeConnectorException($connectorException =
318321
new RecoverableConnectorException('This exception is caught by the provider handler.'));

test/Unit/Porter/ImportSpecificationTest.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
<?php
22
namespace ScriptFUSIONTest\Unit\Porter;
33

4+
use ScriptFUSION\Porter\Connector\FetchExceptionHandler\FetchExceptionHandler;
45
use ScriptFUSION\Porter\Provider\Resource\ProviderResource;
56
use ScriptFUSION\Porter\Specification\DuplicateTransformerException;
67
use ScriptFUSION\Porter\Specification\ImportSpecification;
78
use ScriptFUSION\Porter\Transform\Transformer;
8-
use ScriptFUSIONTest\Stubs\Invokable;
99

1010
/**
1111
* @see ImportSpecification
@@ -30,7 +30,7 @@ public function testClone()
3030
$this->specification
3131
->addTransformer(\Mockery::mock(Transformer::class))
3232
->setContext($context = (object)[])
33-
->setFetchExceptionHandler($handler = new Invokable)
33+
->setFetchExceptionHandler($handler = \Mockery::mock(FetchExceptionHandler::class))
3434
;
3535

3636
$specification = clone $this->specification;
@@ -141,7 +141,7 @@ public function provideFetchAttempts()
141141
public function testExceptionHandler()
142142
{
143143
self::assertSame(
144-
$handler = new Invokable,
144+
$handler = \Mockery::mock(FetchExceptionHandler::class),
145145
$this->specification->setFetchExceptionHandler($handler)->getFetchExceptionHandler()
146146
);
147147
}

0 commit comments

Comments
 (0)