Skip to content

Commit 9a821e8

Browse files
Feijssunspikes
authored andcommitted
Improvements to cache keys and time multiplier constants (#6)
* Replaced magic time multiplier numbers with constants * Added helper methods for suffixed cache keys, changed const names to use suffix instead of prefix
1 parent d6870c4 commit 9a821e8

13 files changed

+117
-46
lines changed

src/Throttle/Throttler/ElasticWindowThrottler.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,6 @@ public function getRetryTimeout()
142142
return 0;
143143
}
144144

145-
return 1e3 * $this->ttl;
145+
return self::SECOND_TO_MILLISECOND_MULTIPLIER * $this->ttl;
146146
}
147147
}

src/Throttle/Throttler/FixedWindowThrottler.php

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@
2929

3030
final class FixedWindowThrottler extends AbstractWindowThrottler implements RetriableThrottlerInterface
3131
{
32-
const TIME_CACHE_KEY = ':time';
33-
const HITS_CACHE_KEY = ':hits';
32+
const CACHE_KEY_TIME = ':time';
33+
const CACHE_KEY_HITS = ':hits';
3434

3535
/**
3636
* @var int|null
@@ -46,11 +46,11 @@ public function hit()
4646

4747
// Update the window start time if the previous window has passed, or no cached window exists
4848
try {
49-
if (($this->timeProvider->now() - $this->cache->get($this->key.self::TIME_CACHE_KEY)) > $this->timeLimit) {
50-
$this->cache->set($this->key.self::TIME_CACHE_KEY, $this->timeProvider->now(), $this->cacheTtl);
49+
if (($this->timeProvider->now() - $this->cache->get($this->getTimeCacheKey())) > $this->timeLimit) {
50+
$this->cache->set($this->getTimeCacheKey(), $this->timeProvider->now(), $this->cacheTtl);
5151
}
5252
} catch (ItemNotFoundException $exception) {
53-
$this->cache->set($this->key.self::TIME_CACHE_KEY, $this->timeProvider->now(), $this->cacheTtl);
53+
$this->cache->set($this->getTimeCacheKey(), $this->timeProvider->now(), $this->cacheTtl);
5454
}
5555

5656
return $this;
@@ -62,7 +62,7 @@ public function hit()
6262
public function count()
6363
{
6464
try {
65-
if (($this->timeProvider->now() - $this->cache->get($this->key.self::TIME_CACHE_KEY)) > $this->timeLimit) {
65+
if (($this->timeProvider->now() - $this->cache->get($this->getTimeCacheKey())) > $this->timeLimit) {
6666
return 0;
6767
}
6868

@@ -78,7 +78,7 @@ public function count()
7878
public function clear()
7979
{
8080
$this->setCachedHitCount(0);
81-
$this->cache->set($this->key.self::TIME_CACHE_KEY, $this->timeProvider->now(), $this->cacheTtl);
81+
$this->cache->set($this->getTimeCacheKey(), $this->timeProvider->now(), $this->cacheTtl);
8282
}
8383

8484
/**
@@ -92,7 +92,9 @@ public function getRetryTimeout()
9292

9393
// Return the time until the current window ends
9494
// Try/catch for the ItemNotFoundException is not required, in that case $this->check() will return true
95-
return 1e3 * ($this->timeLimit - $this->timeProvider->now() + $this->cache->get($this->key.self::TIME_CACHE_KEY));
95+
$cachedTime = $this->cache->get($this->getTimeCacheKey());
96+
97+
return self::SECOND_TO_MILLISECOND_MULTIPLIER * ($this->timeLimit - $this->timeProvider->now() + $cachedTime);
9698
}
9799

98100
/**
@@ -106,7 +108,7 @@ private function getCachedHitCount()
106108
return $this->hitCount;
107109
}
108110

109-
return $this->cache->get($this->key.self::HITS_CACHE_KEY);
111+
return $this->cache->get($this->getHitsCacheKey());
110112
}
111113

112114
/**
@@ -115,6 +117,22 @@ private function getCachedHitCount()
115117
private function setCachedHitCount($hitCount)
116118
{
117119
$this->hitCount = $hitCount;
118-
$this->cache->set($this->key.self::HITS_CACHE_KEY, $hitCount, $this->cacheTtl);
120+
$this->cache->set($this->getHitsCacheKey(), $hitCount, $this->cacheTtl);
121+
}
122+
123+
/**
124+
* @return string
125+
*/
126+
private function getHitsCacheKey()
127+
{
128+
return $this->key.self::CACHE_KEY_HITS;
129+
}
130+
131+
/**
132+
* @return string
133+
*/
134+
private function getTimeCacheKey()
135+
{
136+
return $this->key.self::CACHE_KEY_TIME;
119137
}
120138
}

src/Throttle/Throttler/LeakyBucketThrottler.php

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@
3131

3232
final class LeakyBucketThrottler implements RetriableThrottlerInterface
3333
{
34-
const TIME_CACHE_KEY = ':time';
35-
const TOKEN_CACHE_KEY = ':tokens';
34+
const CACHE_KEY_TIME = ':time';
35+
const CACHE_KEY_TOKEN = ':tokens';
3636

3737
/**
3838
* @var CacheAdapterInterface
@@ -114,7 +114,7 @@ public function hit()
114114
$this->setUsedCapacity($tokenCount + 1);
115115

116116
if (0 < $wait = $this->getWaitTime($tokenCount)) {
117-
$this->timeProvider->usleep(1e3 * $wait);
117+
$this->timeProvider->usleep(self::MILLISECOND_TO_MICROSECOND_MULTIPLIER * $wait);
118118
}
119119

120120
return $wait;
@@ -134,13 +134,14 @@ public function clear()
134134
public function count()
135135
{
136136
try {
137-
$timeSinceLastRequest = 1e3 * ($this->timeProvider->now() - $this->cache->get($this->key.self::TIME_CACHE_KEY));
137+
$cachedTime = $this->cache->get($this->getTimeCacheKey());
138+
$timeSinceLastRequest = self::SECOND_TO_MILLISECOND_MULTIPLIER * ($this->timeProvider->now() - $cachedTime);
138139

139140
if ($timeSinceLastRequest > $this->timeLimit) {
140141
return 0;
141142
}
142143

143-
$lastTokenCount = $this->cache->get($this->key.self::TOKEN_CACHE_KEY);
144+
$lastTokenCount = $this->cache->get($this->getTokenCacheKey());
144145
} catch (ItemNotFoundException $exception) {
145146
$this->clear(); //Clear the bucket
146147

@@ -206,7 +207,23 @@ private function getWaitTime($tokenCount)
206207
*/
207208
private function setUsedCapacity($tokens)
208209
{
209-
$this->cache->set($this->key.self::TOKEN_CACHE_KEY, $tokens, $this->cacheTtl);
210-
$this->cache->set($this->key.self::TIME_CACHE_KEY, $this->timeProvider->now(), $this->cacheTtl);
210+
$this->cache->set($this->getTokenCacheKey(), $tokens, $this->cacheTtl);
211+
$this->cache->set($this->getTimeCacheKey(), $this->timeProvider->now(), $this->cacheTtl);
212+
}
213+
214+
/**
215+
* @return string
216+
*/
217+
private function getTokenCacheKey()
218+
{
219+
return $this->key.self::CACHE_KEY_TOKEN;
220+
}
221+
222+
/**
223+
* @return string
224+
*/
225+
private function getTimeCacheKey()
226+
{
227+
return $this->key.self::CACHE_KEY_TIME;
211228
}
212229
}

src/Throttle/Throttler/MovingWindowThrottler.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,14 @@ public function getRetryTimeout()
7878
// Then return the time remaining for that timestamp to expire
7979
foreach ($this->hitCountMapping as $timestamp => $hitCount) {
8080
if ($this->hitLimit > $totalHitCount -= $hitCount) {
81-
return 1e3 * max(0, $this->timeLimit - ((int) ceil($this->timeProvider->now()) - $timestamp));
81+
return self::SECOND_TO_MILLISECOND_MULTIPLIER * max(
82+
0,
83+
$this->timeLimit - ((int) ceil($this->timeProvider->now()) - $timestamp)
84+
);
8285
}
8386
}
8487

85-
return 1e3 * $this->timeLimit;
88+
return self::SECOND_TO_MILLISECOND_MULTIPLIER * $this->timeLimit;
8689
}
8790

8891
/**

src/Throttle/Throttler/RetrialQueueThrottler.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ public function access()
6666
public function hit()
6767
{
6868
if (0 !== $waitTime = $this->internalThrottler->getRetryTimeout()) {
69-
$this->timeProvider->usleep(1e3 * $waitTime);
69+
$this->timeProvider->usleep(self::MILLISECOND_TO_MICROSECOND_MULTIPLIER * $waitTime);
7070
}
7171

7272
return $this->internalThrottler->hit();

src/Throttle/Throttler/ThrottlerInterface.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@
2727

2828
interface ThrottlerInterface
2929
{
30+
const SECOND_TO_MILLISECOND_MULTIPLIER = 1000;
31+
const MILLISECOND_TO_MICROSECOND_MULTIPLIER = 1000;
32+
3033
/**
3134
* Access the resource and return status
3235
*

tests/Functional/LeakyBucketTest.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use Sunspikes\Ratelimit\Throttle\Factory\TimeAwareThrottlerFactory;
1010
use Sunspikes\Ratelimit\Throttle\Hydrator\HydratorFactory;
1111
use Sunspikes\Ratelimit\Throttle\Settings\LeakyBucketSettings;
12+
use Sunspikes\Ratelimit\Throttle\Throttler\ThrottlerInterface;
1213
use Sunspikes\Ratelimit\Time\TimeAdapterInterface;
1314

1415
class LeakyBucketTest extends AbstractThrottlerTestCase
@@ -35,7 +36,9 @@ protected function setUp()
3536
public function testThrottleAccess()
3637
{
3738
$expectedWaitTime = self::TIME_LIMIT / (self::TOKEN_LIMIT - $this->getMaxAttempts());
38-
$this->timeAdapter->shouldReceive('usleep')->with(1e3 * $expectedWaitTime)->once();
39+
$this->timeAdapter->shouldReceive('usleep')
40+
->with(ThrottlerInterface::SECOND_TO_MILLISECOND_MULTIPLIER * $expectedWaitTime)
41+
->once();
3942

4043
parent::testThrottleAccess();
4144
}

tests/Functional/RetrialQueueTest.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use Sunspikes\Ratelimit\Throttle\Hydrator\HydratorFactory;
1111
use Sunspikes\Ratelimit\Throttle\Settings\FixedWindowSettings;
1212
use Sunspikes\Ratelimit\Throttle\Settings\RetrialQueueSettings;
13+
use Sunspikes\Ratelimit\Throttle\Throttler\ThrottlerInterface;
1314
use Sunspikes\Ratelimit\Time\TimeAdapterInterface;
1415

1516
class RetrialQueueTest extends AbstractThrottlerTestCase
@@ -34,7 +35,12 @@ protected function setUp()
3435

3536
public function testThrottleAccess()
3637
{
37-
$this->timeAdapter->shouldReceive('usleep')->with(1e6 * self::TIME_LIMIT)->once();
38+
$this->timeAdapter->shouldReceive('usleep')
39+
->with(
40+
ThrottlerInterface::SECOND_TO_MILLISECOND_MULTIPLIER *
41+
ThrottlerInterface::MILLISECOND_TO_MICROSECOND_MULTIPLIER *
42+
self::TIME_LIMIT
43+
)->once();
3844

3945
parent::testThrottleAccess();
4046
}

tests/Throttle/Throttler/ElasticWindowThrottlerTest.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use Mockery as M;
66
use Sunspikes\Ratelimit\Cache\Adapter\CacheAdapterInterface;
77
use Sunspikes\Ratelimit\Throttle\Throttler\ElasticWindowThrottler;
8+
use Sunspikes\Ratelimit\Throttle\Throttler\ThrottlerInterface;
89

910
class ElasticWindowThrottlerTest extends \PHPUnit_Framework_TestCase
1011
{
@@ -75,6 +76,9 @@ public function testGetRetryTimeout()
7576
$this->throttler->hit();
7677
$this->throttler->hit();
7778

78-
$this->assertEquals(1e3 * self::TTL, $this->throttler->getRetryTimeout());
79+
$this->assertEquals(
80+
ThrottlerInterface::SECOND_TO_MILLISECOND_MULTIPLIER * self::TTL,
81+
$this->throttler->getRetryTimeout()
82+
);
7983
}
8084
}

tests/Throttle/Throttler/FixedWindowThrottlerTest.php

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,20 @@
44

55
use Mockery as M;
66
use Sunspikes\Ratelimit\Throttle\Throttler\FixedWindowThrottler;
7+
use Sunspikes\Ratelimit\Throttle\Throttler\ThrottlerInterface;
78

89
class FixedWindowThrottlerTest extends AbstractWindowThrottlerTest
910
{
1011
public function testAccess()
1112
{
1213
$this->cacheAdapter
1314
->shouldReceive('set')
14-
->with('key'.FixedWindowThrottler::HITS_CACHE_KEY, 1, self::CACHE_TTL)
15+
->with('key'.FixedWindowThrottler::CACHE_KEY_HITS, 1, self::CACHE_TTL)
1516
->once();
1617

1718
$this->cacheAdapter
1819
->shouldReceive('set')
19-
->with('key'.FixedWindowThrottler::TIME_CACHE_KEY, self::TIME_LIMIT + 2, self::CACHE_TTL)
20+
->with('key'.FixedWindowThrottler::CACHE_KEY_TIME, self::TIME_LIMIT + 2, self::CACHE_TTL)
2021
->once();
2122

2223
parent::testAccess();
@@ -28,12 +29,12 @@ public function testClear()
2829

2930
$this->cacheAdapter
3031
->shouldReceive('set')
31-
->with('key'.FixedWindowThrottler::TIME_CACHE_KEY, self::INITIAL_TIME + 3, self::CACHE_TTL)
32+
->with('key'.FixedWindowThrottler::CACHE_KEY_TIME, self::INITIAL_TIME + 3, self::CACHE_TTL)
3233
->once();
3334

3435
$this->cacheAdapter
3536
->shouldReceive('set')
36-
->with('key'.FixedWindowThrottler::HITS_CACHE_KEY, 0, self::CACHE_TTL)
37+
->with('key'.FixedWindowThrottler::CACHE_KEY_HITS, 0, self::CACHE_TTL)
3738
->once();
3839

3940
$this->throttler->clear();
@@ -46,7 +47,7 @@ public function testCountWithLessTimePassedThanLimit()
4647

4748
$this->cacheAdapter
4849
->shouldReceive('get')
49-
->with('key'.FixedWindowThrottler::HITS_CACHE_KEY)
50+
->with('key'.FixedWindowThrottler::CACHE_KEY_HITS)
5051
->andReturn(self::HIT_LIMIT / 3);
5152

5253
$this->assertEquals(self::HIT_LIMIT / 3, $this->throttler->count());
@@ -66,10 +67,13 @@ public function testGetRetryTimeoutPostLimit()
6667
$this->mockTimePassed(self::TIME_LIMIT / 2);
6768
$this->cacheAdapter
6869
->shouldReceive('get')
69-
->with('key'.FixedWindowThrottler::HITS_CACHE_KEY)
70+
->with('key'.FixedWindowThrottler::CACHE_KEY_HITS)
7071
->andReturn(self::HIT_LIMIT + 1);
7172

72-
$this->assertEquals(5e2 * self::TIME_LIMIT, $this->throttler->getRetryTimeout());
73+
$this->assertEquals(
74+
ThrottlerInterface::SECOND_TO_MILLISECOND_MULTIPLIER / 2 * self::TIME_LIMIT,
75+
$this->throttler->getRetryTimeout()
76+
);
7377
}
7478

7579
/**
@@ -96,7 +100,7 @@ protected function mockTimePassed($timeDiff)
96100

97101
$this->cacheAdapter
98102
->shouldReceive('get')
99-
->with('key'.FixedWindowThrottler::TIME_CACHE_KEY)
103+
->with('key'.FixedWindowThrottler::CACHE_KEY_TIME)
100104
->andReturn(self::INITIAL_TIME);
101105
}
102106
}

0 commit comments

Comments
 (0)