Skip to content

Commit 566c016

Browse files
authored
Authorization Renewal Jitter (#310)
If a lot of WebSocket connections were severed at the same time, they'll all reconnect and reauthorize around the same time. Although clients may (and should!) implement some jitter on their side, it won't be very large because commonly real-time features need to be available and working. The problem is, once that first domino of a thundering herd falls, subsequent dominoes keep falling, precisely in `authorization_renewal_period` intervals. This PR allows one to add (actually remove) some jitter (in seconds), which will spread the authorizations around. Those are typically less time-sensitive than that first reconnection & reauthorization, so they can use a larger jitter.
1 parent f17d7ae commit 566c016

File tree

3 files changed

+16
-1
lines changed

3 files changed

+16
-1
lines changed

README.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,14 @@ e.g.:
428428
"error": "Unauthorized."
429429
}
430430
431+
432+
To avoid a thundering herd problem where a lot of authorizations happened at
433+
the same time *and they continue renewing the authorization at the same time,
434+
too*, add ``authorization_renewal_jitter`` as a number of seconds. This will
435+
schedule the upcoming authorization renewal for
436+
``authorization_renewal_period`` seconds from now *minus* anywhere between 0
437+
and ``authorization_renewal_jitter`` seconds.
438+
431439
Before subscribe
432440
~~~~~~~~~~~~~~~~
433441

example_config.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@
4242
# Fields returned by the authorizer callback that are passed to all
4343
# subsequent service callbacks.
4444
'authorizer_fields': ['capabilities'],
45+
# Periodically reauthorize subscriptions and add some jitter to avoid
46+
# thundering herds.
47+
'authorization_renewal_period': 300,
48+
'authorization_renewal_jitter': 30,
4549
# If this service requires extra fields to fulfill a subscription,
4650
# you may provide them here. They are passed to all URL callbacks.
4751
'extra_fields': ['organization_id'],

socketshark/subscription.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,13 +152,16 @@ async def authorize_subscription(self):
152152

153153
async def periodic_authorizer(self):
154154
period = self.service_config['authorization_renewal_period']
155+
jitter = self.service_config.get('authorization_renewal_jitter', 0)
155156
self.session.trace_log.debug(
156157
'initializing periodic authorizer',
157158
subscription=self.name,
158159
period=period,
160+
jitter=jitter,
159161
)
160162
while True:
161-
await asyncio.sleep(period)
163+
sleep_duration = period - (random.random() * jitter)
164+
await asyncio.sleep(sleep_duration)
162165
try:
163166
self.session.log.debug(
164167
'verifying authorization', subscription=self.name

0 commit comments

Comments
 (0)