@@ -124,20 +124,74 @@ Configuration
124124The following example creates two different rate limiters for an API service, to
125125enforce different levels of service (free or paid):
126126
127- .. code-block :: yaml
128-
129- # config/packages/rate_limiter.yaml
130- framework :
131- rate_limiter :
132- anonymous_api :
133- # use 'sliding_window' if you prefer that policy
134- policy : ' fixed_window'
135- limit : 100
136- interval : ' 60 minutes'
137- authenticated_api :
138- policy : ' token_bucket'
139- limit : 5000
140- rate : { interval: '15 minutes', amount: 500 }
127+ .. configuration-block ::
128+
129+ .. code-block :: yaml
130+
131+ # config/packages/rate_limiter.yaml
132+ framework :
133+ rate_limiter :
134+ anonymous_api :
135+ # use 'sliding_window' if you prefer that policy
136+ policy : ' fixed_window'
137+ limit : 100
138+ interval : ' 60 minutes'
139+ authenticated_api :
140+ policy : ' token_bucket'
141+ limit : 5000
142+ rate : { interval: '15 minutes', amount: 500 }
143+
144+ .. code-block :: xml
145+
146+ <!-- config/packages/rate_limiter.xml -->
147+ <?xml version =" 1.0" encoding =" UTF-8" ?>
148+ <container xmlns =" http://symfony.com/schema/dic/services"
149+ xmlns : xsi =" http://www.w3.org/2001/XMLSchema-instance"
150+ xmlns : framework =" http://symfony.com/schema/dic/symfony"
151+ xsi : schemaLocation =" http://symfony.com/schema/dic/services
152+ https://symfony.com/schema/dic/services/services-1.0.xsd
153+ http://symfony.com/schema/dic/symfony
154+ https://symfony.com/schema/dic/symfony/symfony-1.0.xsd" >
155+
156+ <framework : config >
157+ <framework : rate-limiter >
158+ <!-- policy: use 'sliding_window' if you prefer that policy -->
159+ <framework : limiter name =" anonymous_api"
160+ policy =" fixed_window"
161+ limit =" 100"
162+ interval =" 60 minutes"
163+ />
164+
165+ <framework : limiter name =" authenticated_api"
166+ policy =" token_bucket"
167+ limit =" 5000"
168+ >
169+ <framework : rate interval =" 15 minutes"
170+ amount =" 500"
171+ />
172+ </framework : limiter >
173+ </framework : rate-limiter >
174+ </framework : config >
175+ </container >
176+
177+ .. code-block :: php
178+
179+ // config/packages/rate_limiter.php
180+ $container->loadFromExtension('framework', [
181+ rate_limiter' => [
182+ 'anonymous_api' => [
183+ // use 'sliding_window' if you prefer that policy
184+ 'policy' => 'fixed_window',
185+ 'limit' => 100,
186+ 'interval' => '60 minutes',
187+ ],
188+ 'authenticated_api' => [
189+ 'policy' => 'token_bucket',
190+ 'limit' => 5000,
191+ 'rate' => [ 'interval' => '15 minutes', 'amount' => 500 ],
192+ ],
193+ ],
194+ ]);
141195
142196 .. note ::
143197
@@ -302,27 +356,146 @@ the :class:`Symfony\\Component\\RateLimiter\\Reservation` object returned by the
302356 }
303357 }
304358
305- Rate Limiter Storage and Locking
306- --------------------------------
307-
308- Rate limiters use the default cache and locking mechanisms defined in your
309- Symfony application. If you prefer to change that, use the ``lock_factory `` and
310- ``storage_service `` options:
311-
312- .. code-block :: yaml
313-
314- # config/packages/rate_limiter.yaml
315- framework :
316- rate_limiter :
317- anonymous_api_limiter :
318- # ...
319- # the value is the name of any cache pool defined in your application
320- cache_pool : ' app.redis_cache'
321- # or define a service implementing StorageInterface to use a different
322- # mechanism to store the limiter information
323- storage_service : ' App\RateLimiter\CustomRedisStorage'
324- # the value is the name of any lock defined in your application
325- lock_factory : ' app.rate_limiter_lock'
359+ Storing Rate Limiter State: Caching
360+ -----------------------------------
361+
362+ All rate limiter policies require to store the state of the rate limiter
363+ (e.g. how many hits were already made in the current time window). This
364+ state is stored by default using the :doc: `Cache component </cache >`.
365+
366+ The default cache pool used by all limiters is ``cache.rate_limiter ``. You
367+ can modify this cache pool by :ref: `defining a "rate_limiter" pool <cache-create-pools >`.
368+
369+ You can also override the pool for a specific limiter using the ``cache_pool ``
370+ option:
371+
372+ .. configuration-block ::
373+
374+ .. code-block :: yaml
375+
376+ # config/packages/rate_limiter.yaml
377+ framework :
378+ rate_limiter :
379+ anonymous_api :
380+ # ...
381+
382+ # use the "cache.anonymous_rate_limiter" cache pool
383+ cache_pool : ' cache.anonymous_rate_limiter'
384+
385+ .. code-block :: xml
386+
387+ <!-- config/packages/rate_limiter.xml -->
388+ <?xml version =" 1.0" encoding =" UTF-8" ?>
389+ <container xmlns =" http://symfony.com/schema/dic/services"
390+ xmlns : xsi =" http://www.w3.org/2001/XMLSchema-instance"
391+ xmlns : framework =" http://symfony.com/schema/dic/symfony"
392+ xsi : schemaLocation =" http://symfony.com/schema/dic/services
393+ https://symfony.com/schema/dic/services/services-1.0.xsd
394+ http://symfony.com/schema/dic/symfony
395+ https://symfony.com/schema/dic/symfony/symfony-1.0.xsd" >
396+
397+ <framework : config >
398+ <framework : rate-limiter >
399+ <!-- cache-pool: use the "cache.anonymous_rate_limiter" cache pool -->
400+ <framework : limiter name =" anonymous_api"
401+ policy =" fixed_window"
402+ limit =" 100"
403+ interval =" 60 minutes"
404+ cache-pool =" cache.anonymous_rate_limiter"
405+ />
406+
407+ <!-- ... ->
408+ </framework:rate-limiter>
409+ </framework:config>
410+ </container>
411+
412+ .. code-block:: php
413+
414+ // config/packages/rate_limiter.php
415+ $container->loadFromExtension('framework', [
416+ rate_limiter' => [
417+ 'anonymous_api' => [
418+ // ...
419+
420+ // use the "cache.anonymous_rate_limiter" cache pool
421+ 'cache_pool' => 'cache.anonymous_rate_limiter',
422+ ],
423+ ],
424+ ]);
425+
426+ .. note::
427+
428+ Instead of using the Cache component, you can also implement a custom
429+ storage. Create a PHP class that implements the
430+ :class:`Symfony\\Component\\RateLimiter\\Storage\\StorageInterface` and
431+ set the ``storage_service`` setting of each limiter to the service ID
432+ of this class.
433+
434+ Using Locks to Prevent Race Conditions
435+ --------------------------------------
436+
437+ Rate limiting can be affected by race conditions, if the same limiter is
438+ applied to simultaneous requests (e.g. 3 servers of the same client call
439+ the same API). To prevent these race conditions, the rate limiting
440+ operations are protected using :doc:`locks </lock>`.
441+
442+ By default, the global lock (configured by ``framework.lock``) is used. You
443+ can use a specific :ref:`named lock <lock-named-locks>` via the
444+ ``lock_factory`` option:
445+
446+ .. configuration-block::
447+
448+ .. code-block:: yaml
449+
450+ # config/packages/rate_limiter.yaml
451+ framework:
452+ rate_limiter:
453+ anonymous_api:
454+ # ...
455+
456+ # use the "lock.rate_limiter.factory" for this limiter
457+ lock_factory: 'lock.rate_limiter.factory'
458+
459+ .. code-block:: xml
460+
461+ <!-- config/packages/rate_limiter.xml -->
462+ <?xml version =" 1.0" encoding =" UTF-8" ?>
463+ <container xmlns =" http://symfony.com/schema/dic/services"
464+ xmlns : xsi =" http://www.w3.org/2001/XMLSchema-instance"
465+ xmlns : framework =" http://symfony.com/schema/dic/symfony"
466+ xsi : schemaLocation =" http://symfony.com/schema/dic/services
467+ https://symfony.com/schema/dic/services/services-1.0.xsd
468+ http://symfony.com/schema/dic/symfony
469+ https://symfony.com/schema/dic/symfony/symfony-1.0.xsd" >
470+
471+ <framework : config >
472+ <framework : rate-limiter >
473+ <!-- limiter-factory: use the "lock.rate_limiter.factory" for this limiter -->
474+ <framework : limiter name =" anonymous_api"
475+ policy =" fixed_window"
476+ limit =" 100"
477+ interval =" 60 minutes"
478+ lock-factory =" lock.rate_limiter.factory"
479+ />
480+
481+ <!-- ... -->
482+ </framework : rate-limiter >
483+ </framework : config >
484+ </container >
485+
486+ .. code-block :: php
487+
488+ // config/packages/rate_limiter.php
489+ $container->loadFromExtension('framework', [
490+ rate_limiter' => [
491+ 'anonymous_api' => [
492+ // ...
493+
494+ // use the "lock.rate_limiter.factory" for this limiter
495+ 'lock_factory' => 'lock.rate_limiter.factory',
496+ ],
497+ ],
498+ ]);
326499
327500 .. _`DoS attacks` : https://cheatsheetseries.owasp.org/cheatsheets/Denial_of_Service_Cheat_Sheet.html
328501.. _`Apache mod_ratelimit` : https://httpd.apache.org/docs/current/mod/mod_ratelimit.html
0 commit comments