55use Ackintosh \Ganesha ;
66use Ackintosh \Ganesha \Configuration ;
77use Ackintosh \Ganesha \Exception \StorageException ;
8+ use Ackintosh \Ganesha \NativeClock ;
89use Ackintosh \Ganesha \Storage ;
910use Ackintosh \Ganesha \StrategyInterface ;
1011use InvalidArgumentException ;
1112use LogicException ;
13+ use Psr \Clock \ClockInterface ;
1214
1315class Rate implements StrategyInterface
1416{
@@ -22,6 +24,8 @@ class Rate implements StrategyInterface
2224 */
2325 private $ storage ;
2426
27+ private ClockInterface $ clock ;
28+
2529 /**
2630 * @var array
2731 */
@@ -36,10 +40,14 @@ class Rate implements StrategyInterface
3640 /**
3741 * @param Configuration $configuration
3842 */
39- private function __construct (Configuration $ configuration , Storage $ storage )
40- {
43+ private function __construct (
44+ Configuration $ configuration ,
45+ Storage $ storage ,
46+ ClockInterface $ clock ,
47+ ) {
4148 $ this ->configuration = $ configuration ;
4249 $ this ->storage = $ storage ;
50+ $ this ->clock = $ clock ;
4351 }
4452
4553 /**
@@ -58,23 +66,28 @@ public static function validate(array $params): void
5866 }
5967 }
6068
61- public static function create (Storage \AdapterInterface $ adapter , Configuration $ configuration ): StrategyInterface
62- {
63- $ serviceNameDecorator = $ adapter instanceof Storage \Adapter \TumblingTimeWindowInterface ? self ::serviceNameDecorator ($ configuration ->timeWindow ()) : null ;
69+ public static function create (
70+ Storage \AdapterInterface $ adapter ,
71+ Configuration $ configuration ,
72+ ?ClockInterface $ clock = null
73+ ): StrategyInterface {
74+ $ clock = $ clock ?? new NativeClock ();
75+ $ serviceNameDecorator = $ adapter instanceof Storage \Adapter \TumblingTimeWindowInterface ? self ::serviceNameDecorator ($ configuration ->timeWindow (), $ clock ) : null ;
6476
6577 return new self (
6678 $ configuration ,
6779 new Storage (
6880 $ adapter ,
6981 $ configuration ->storageKeys (),
7082 $ serviceNameDecorator
71- )
83+ ),
84+ $ clock ,
7285 );
7386 }
7487
7588 public function recordFailure (string $ service ): int
7689 {
77- $ this ->storage ->setLastFailureTime ($ service , time ());
90+ $ this ->storage ->setLastFailureTime ($ service , $ this -> clock -> now ()-> getTimestamp ());
7891 $ this ->storage ->incrementFailureCount ($ service );
7992 if (
8093 $ this ->storage ->getStatus ($ service ) === Ganesha::STATUS_CALMED_DOWN
@@ -158,16 +171,16 @@ private function isClosedInCurrentTimeWindow(string $service): bool
158171
159172 private function isClosedInPreviousTimeWindow (string $ service ): bool
160173 {
161- $ failure = $ this ->storage ->getFailureCountByCustomKey (self ::keyForPreviousTimeWindow ($ service , $ this ->configuration ->timeWindow ()));
174+ $ failure = $ this ->storage ->getFailureCountByCustomKey (self ::keyForPreviousTimeWindow ($ service , $ this ->configuration ->timeWindow (), $ this -> clock ));
162175 if (
163176 $ failure === 0
164177 || ($ failure / $ this ->configuration ->minimumRequests ()) * 100 < $ this ->configuration ->failureRateThreshold ()
165178 ) {
166179 return true ;
167180 }
168181
169- $ success = $ this ->storage ->getSuccessCountByCustomKey (self ::keyForPreviousTimeWindow ($ service , $ this ->configuration ->timeWindow ()));
170- $ rejection = $ this ->storage ->getRejectionCountByCustomKey (self ::keyForPreviousTimeWindow ($ service , $ this ->configuration ->timeWindow ()));
182+ $ success = $ this ->storage ->getSuccessCountByCustomKey (self ::keyForPreviousTimeWindow ($ service , $ this ->configuration ->timeWindow (), $ this -> clock ));
183+ $ rejection = $ this ->storage ->getRejectionCountByCustomKey (self ::keyForPreviousTimeWindow ($ service , $ this ->configuration ->timeWindow (), $ this -> clock ));
171184
172185 return $ this ->isClosedInTimeWindow ($ failure , $ success , $ rejection );
173186 }
@@ -190,32 +203,36 @@ private function isClosedInTimeWindow(int $failure, int $success, int $rejection
190203 */
191204 private function isHalfOpen (string $ service ): bool
192205 {
206+ $ time = $ this ->clock ->now ()->getTimestamp ();
207+
193208 if (is_null ($ lastFailureTime = $ this ->storage ->getLastFailureTime ($ service ))) {
194209 return false ;
195210 }
196211
197- if ((time () - $ lastFailureTime ) > $ this ->configuration ->intervalToHalfOpen ()) {
198- $ this ->storage ->setLastFailureTime ($ service , time () );
212+ if (($ time - $ lastFailureTime ) > $ this ->configuration ->intervalToHalfOpen ()) {
213+ $ this ->storage ->setLastFailureTime ($ service , $ time );
199214 return true ;
200215 }
201216
202217 return false ;
203218 }
204219
205- private static function serviceNameDecorator (int $ timeWindow , $ current = true )
220+ private static function serviceNameDecorator (int $ timeWindow , ClockInterface $ clock , bool $ current = true ): \ Closure
206221 {
207- return function ($ service ) use ($ timeWindow , $ current ) {
222+ return function ($ service ) use ($ timeWindow , $ clock , $ current ) {
223+ $ time = $ clock ->now ()->getTimestamp ();
224+
208225 return sprintf (
209226 '%s.%d ' ,
210227 $ service ,
211- $ current ? (int )floor (time () / $ timeWindow ) : (int )floor ((time () - $ timeWindow ) / $ timeWindow )
228+ $ current ? (int )floor ($ time / $ timeWindow ) : (int )floor (($ time - $ timeWindow ) / $ timeWindow )
212229 );
213230 };
214231 }
215232
216- private static function keyForPreviousTimeWindow (string $ service , int $ timeWindow)
233+ private static function keyForPreviousTimeWindow (string $ service , int $ timeWindow, ClockInterface $ clock ): string
217234 {
218- $ f = self ::serviceNameDecorator ($ timeWindow , false );
235+ $ f = self ::serviceNameDecorator ($ timeWindow , $ clock , false );
219236 return $ f ($ service );
220237 }
221238}
0 commit comments