summary refs log tree commit diff
path: root/synapse/storage/_base.py
diff options
context:
space:
mode:
authorBrendan Abolivier <babolivier@matrix.org>2019-05-28 16:47:42 +0100
committerBrendan Abolivier <babolivier@matrix.org>2019-05-28 16:52:45 +0100
commit52839886d664576831462e033b88e5aba4c019e3 (patch)
tree33f49429d93c882115ea75a3fc1a82c68051b822 /synapse/storage/_base.py
parentChangelog (diff)
downloadsynapse-52839886d664576831462e033b88e5aba4c019e3.tar.xz
Allow configuring a range for the account validity startup job
When enabling the account validity feature, Synapse will look at startup for registered account without an expiration date, and will set one equals to 'now + validity_period' for them. On large servers, it can mean that a large number of users will have the same expiration date, which means that they will all be sent a renewal email at the same time, which isn't ideal.
In order to mitigate this, this PR allows server admins to define a 'max_delta' so that the expiration date is a random value in the [now + validity_period ; now + validity_period + max_delta] range. This allows renewal emails to be progressively sent over a configured period instead of being sent all in one big batch.
Diffstat (limited to 'synapse/storage/_base.py')
-rw-r--r--synapse/storage/_base.py23
1 files changed, 21 insertions, 2 deletions
diff --git a/synapse/storage/_base.py b/synapse/storage/_base.py
index fa6839ceca..40802fd3dc 100644
--- a/synapse/storage/_base.py
+++ b/synapse/storage/_base.py
@@ -16,6 +16,7 @@
 # limitations under the License.
 import itertools
 import logging
+import random
 import sys
 import threading
 import time
@@ -247,6 +248,8 @@ class SQLBaseStore(object):
                 self._check_safe_to_upsert,
             )
 
+        self.rand = random.SystemRandom()
+
         if self._account_validity.enabled:
             self._clock.call_later(
                 0.0,
@@ -308,21 +311,37 @@ class SQLBaseStore(object):
             res = self.cursor_to_dict(txn)
             if res:
                 for user in res:
-                    self.set_expiration_date_for_user_txn(txn, user["name"])
+                    self.set_expiration_date_for_user_txn(
+                        txn,
+                        user["name"],
+                        use_delta=True,
+                    )
 
         yield self.runInteraction(
             "get_users_with_no_expiration_date",
             select_users_with_no_expiration_date_txn,
         )
 
-    def set_expiration_date_for_user_txn(self, txn, user_id):
+    def set_expiration_date_for_user_txn(self, txn, user_id, use_delta=False):
         """Sets an expiration date to the account with the given user ID.
 
         Args:
              user_id (str): User ID to set an expiration date for.
+             use_delta (bool): If set to False, the expiration date for the user will be
+                now + validity period. If set to True, this expiration date will be a
+                random value in the [now + period; now + period + max_delta] range,
+                max_delta being the configured value for the size of the range, unless
+                delta is 0, in which case it sets it to now + period.
         """
         now_ms = self._clock.time_msec()
         expiration_ts = now_ms + self._account_validity.period
+
+        if use_delta and self._account_validity.startup_job_max_delta:
+            expiration_ts = self.rand.randrange(
+                expiration_ts,
+                expiration_ts + self._account_validity.startup_job_max_delta,
+            )
+
         self._simple_insert_txn(
             txn,
             "account_validity",