13
13
14
14
use Symfony \Component \Lock \LockInterface ;
15
15
use Symfony \Component \Lock \NoLock ;
16
- use Symfony \Component \RateLimiter \Exception \ReserveNotSupportedException ;
16
+ use Symfony \Component \RateLimiter \Exception \MaxWaitDurationExceededException ;
17
17
use Symfony \Component \RateLimiter \LimiterInterface ;
18
18
use Symfony \Component \RateLimiter \RateLimit ;
19
19
use Symfony \Component \RateLimiter \Reservation ;
@@ -49,11 +49,10 @@ public function __construct(string $id, int $limit, \DateInterval $interval, Sto
49
49
50
50
public function reserve (int $ tokens = 1 , float $ maxTime = null ): Reservation
51
51
{
52
- throw new ReserveNotSupportedException (__CLASS__ );
53
- }
52
+ if ($ tokens > $ this ->limit ) {
53
+ throw new \InvalidArgumentException (sprintf ('Cannot reserve more tokens (%d) than the size of the rate limiter (%d). ' , $ tokens , $ this ->limit ));
54
+ }
54
55
55
- public function consume (int $ tokens = 1 ): RateLimit
56
- {
57
56
$ this ->lock ->acquire (true );
58
57
59
58
try {
@@ -64,22 +63,43 @@ public function consume(int $tokens = 1): RateLimit
64
63
$ window = SlidingWindow::createFromPreviousWindow ($ window , $ this ->interval );
65
64
}
66
65
66
+ $ now = microtime (true );
67
67
$ hitCount = $ window ->getHitCount ();
68
68
$ availableTokens = $ this ->getAvailableTokens ($ hitCount );
69
- if ($ availableTokens < $ tokens ) {
70
- return new RateLimit ($ availableTokens , $ window ->getRetryAfter (), false , $ this ->limit );
71
- }
69
+ if ($ availableTokens >= $ tokens ) {
70
+ $ window ->add ($ tokens );
72
71
73
- $ window ->add ($ tokens );
72
+ $ reservation = new Reservation ($ now , new RateLimit ($ this ->getAvailableTokens ($ window ->getHitCount ()), \DateTimeImmutable::createFromFormat ('U ' , floor ($ now )), true , $ this ->limit ));
73
+ } else {
74
+ $ waitDuration = $ window ->calculateTimeForTokens ($ this ->limit , max (1 , $ tokens ));
75
+
76
+ if (null !== $ maxTime && $ waitDuration > $ maxTime ) {
77
+ // process needs to wait longer than set interval
78
+ throw new MaxWaitDurationExceededException (sprintf ('The rate limiter wait time ("%d" seconds) is longer than the provided maximum time ("%d" seconds). ' , $ waitDuration , $ maxTime ), new RateLimit ($ this ->getAvailableTokens ($ window ->getHitCount ()), \DateTimeImmutable::createFromFormat ('U ' , floor ($ now + $ waitDuration )), false , $ this ->limit ));
79
+ }
80
+
81
+ $ window ->add ($ tokens );
82
+
83
+ $ reservation = new Reservation ($ now + $ waitDuration , new RateLimit ($ t
8000
his ->getAvailableTokens ($ window ->getHitCount ()), \DateTimeImmutable::createFromFormat ('U ' , floor ($ now + $ waitDuration )), false , $ this ->limit ));
84
+ }
74
85
75
86
if (0 < $ tokens ) {
76
87
$ this ->storage ->save ($ window );
77
88
}
78
-
79
- return new RateLimit ($ this ->getAvailableTokens ($ window ->getHitCount ()), $ window ->getRetryAfter (), true , $ this ->limit );
80
89
} finally {
81
90
$ this ->lock ->release ();
82
91
}
92
+
93
+ return $ reservation ;
94
+ }
95
+
96
+ public function consume (int $ tokens = 1 ): RateLimit
97
+ {
98
+ try {
99
+ return $ this ->reserve ($ tokens , 0 )->getRateLimit ();
100
+ } catch (MaxWaitDurationExceededException $ e ) {
101
+ return $ e ->getRateLimit ();
102
+ }
83
103
}
84
104
85
105
private function getAvailableTokens (int $ hitCount ): int
0 commit comments