Move additional tasks to the background worker (#8458)
							parent
							
								
									8dbf62fada
								
							
						
					
					
						commit
						e4f72ddc44
					
				|  | @ -0,0 +1 @@ | |||
| Allow running background tasks in a separate worker process. | ||||
|  | @ -127,6 +127,7 @@ from synapse.rest.health import HealthResource | |||
| from synapse.rest.key.v2 import KeyApiV2Resource | ||||
| from synapse.server import HomeServer, cache_in_self | ||||
| from synapse.storage.databases.main.censor_events import CensorEventsStore | ||||
| from synapse.storage.databases.main.client_ips import ClientIpWorkerStore | ||||
| from synapse.storage.databases.main.media_repository import MediaRepositoryStore | ||||
| from synapse.storage.databases.main.metrics import ServerMetricsStore | ||||
| from synapse.storage.databases.main.monthly_active_users import ( | ||||
|  | @ -135,6 +136,7 @@ from synapse.storage.databases.main.monthly_active_users import ( | |||
| from synapse.storage.databases.main.presence import UserPresenceState | ||||
| from synapse.storage.databases.main.search import SearchWorkerStore | ||||
| from synapse.storage.databases.main.stats import StatsStore | ||||
| from synapse.storage.databases.main.transactions import TransactionWorkerStore | ||||
| from synapse.storage.databases.main.ui_auth import UIAuthWorkerStore | ||||
| from synapse.storage.databases.main.user_directory import UserDirectoryStore | ||||
| from synapse.types import ReadReceipt | ||||
|  | @ -466,6 +468,7 @@ class GenericWorkerSlavedStore( | |||
|     SlavedAccountDataStore, | ||||
|     SlavedPusherStore, | ||||
|     CensorEventsStore, | ||||
|     ClientIpWorkerStore, | ||||
|     SlavedEventStore, | ||||
|     SlavedKeyStore, | ||||
|     RoomStore, | ||||
|  | @ -481,6 +484,7 @@ class GenericWorkerSlavedStore( | |||
|     MediaRepositoryStore, | ||||
|     ServerMetricsStore, | ||||
|     SearchWorkerStore, | ||||
|     TransactionWorkerStore, | ||||
|     BaseSlavedStore, | ||||
| ): | ||||
|     pass | ||||
|  |  | |||
|  | @ -11,7 +11,6 @@ | |||
| #  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| #  See the License for the specific language governing permissions and | ||||
| #  limitations under the License. | ||||
| 
 | ||||
| import logging | ||||
| import math | ||||
| import resource | ||||
|  | @ -19,7 +18,10 @@ import sys | |||
| 
 | ||||
| from prometheus_client import Gauge | ||||
| 
 | ||||
| from synapse.metrics.background_process_metrics import run_as_background_process | ||||
| from synapse.metrics.background_process_metrics import ( | ||||
|     run_as_background_process, | ||||
|     wrap_as_background_process, | ||||
| ) | ||||
| 
 | ||||
| logger = logging.getLogger("synapse.app.homeserver") | ||||
| 
 | ||||
|  | @ -41,6 +43,7 @@ registered_reserved_users_mau_gauge = Gauge( | |||
| ) | ||||
| 
 | ||||
| 
 | ||||
| @wrap_as_background_process("phone_stats_home") | ||||
| async def phone_stats_home(hs, stats, stats_process=_stats_process): | ||||
|     logger.info("Gathering stats for reporting") | ||||
|     now = int(hs.get_clock().time()) | ||||
|  | @ -143,20 +146,10 @@ def start_phone_stats_home(hs): | |||
|             (int(hs.get_clock().time()), resource.getrusage(resource.RUSAGE_SELF)) | ||||
|         ) | ||||
| 
 | ||||
|     def start_phone_stats_home(): | ||||
|         return run_as_background_process( | ||||
|             "phone_stats_home", phone_stats_home, hs, stats | ||||
|         ) | ||||
| 
 | ||||
|     def generate_user_daily_visit_stats(): | ||||
|         return run_as_background_process( | ||||
|             "generate_user_daily_visits", hs.get_datastore().generate_user_daily_visits | ||||
|         ) | ||||
| 
 | ||||
|     # Rather than update on per session basis, batch up the requests. | ||||
|     # If you increase the loop period, the accuracy of user_daily_visits | ||||
|     # table will decrease | ||||
|     clock.looping_call(generate_user_daily_visit_stats, 5 * 60 * 1000) | ||||
|     clock.looping_call(hs.get_datastore().generate_user_daily_visits, 5 * 60 * 1000) | ||||
| 
 | ||||
|     # monthly active user limiting functionality | ||||
|     def reap_monthly_active_users(): | ||||
|  | @ -167,6 +160,7 @@ def start_phone_stats_home(hs): | |||
|     clock.looping_call(reap_monthly_active_users, 1000 * 60 * 60) | ||||
|     reap_monthly_active_users() | ||||
| 
 | ||||
|     @wrap_as_background_process("generate_monthly_active_users") | ||||
|     async def generate_monthly_active_users(): | ||||
|         current_mau_count = 0 | ||||
|         current_mau_count_by_service = {} | ||||
|  | @ -186,19 +180,14 @@ def start_phone_stats_home(hs): | |||
|         registered_reserved_users_mau_gauge.set(float(len(reserved_users))) | ||||
|         max_mau_gauge.set(float(hs.config.max_mau_value)) | ||||
| 
 | ||||
|     def start_generate_monthly_active_users(): | ||||
|         return run_as_background_process( | ||||
|             "generate_monthly_active_users", generate_monthly_active_users | ||||
|         ) | ||||
| 
 | ||||
|     if hs.config.limit_usage_by_mau or hs.config.mau_stats_only: | ||||
|         start_generate_monthly_active_users() | ||||
|         clock.looping_call(start_generate_monthly_active_users, 5 * 60 * 1000) | ||||
|         generate_monthly_active_users() | ||||
|         clock.looping_call(generate_monthly_active_users, 5 * 60 * 1000) | ||||
|     # End of monthly active user settings | ||||
| 
 | ||||
|     if hs.config.report_stats: | ||||
|         logger.info("Scheduling stats reporting for 3 hour intervals") | ||||
|         clock.looping_call(start_phone_stats_home, 3 * 60 * 60 * 1000) | ||||
|         clock.looping_call(phone_stats_home, 3 * 60 * 60 * 1000, hs, stats) | ||||
| 
 | ||||
|         # We need to defer this init for the cases that we daemonize | ||||
|         # otherwise the process ID we get is that of the non-daemon process | ||||
|  | @ -206,4 +195,4 @@ def start_phone_stats_home(hs): | |||
| 
 | ||||
|         # We wait 5 minutes to send the first set of stats as the server can | ||||
|         # be quite busy the first few minutes | ||||
|         clock.call_later(5 * 60, start_phone_stats_home) | ||||
|         clock.call_later(5 * 60, phone_stats_home, hs, stats) | ||||
|  |  | |||
|  | @ -351,7 +351,63 @@ class ClientIpBackgroundUpdateStore(SQLBaseStore): | |||
|         return updated | ||||
| 
 | ||||
| 
 | ||||
| class ClientIpStore(ClientIpBackgroundUpdateStore): | ||||
| class ClientIpWorkerStore(ClientIpBackgroundUpdateStore): | ||||
|     def __init__(self, database: DatabasePool, db_conn, hs): | ||||
|         super().__init__(database, db_conn, hs) | ||||
| 
 | ||||
|         self.user_ips_max_age = hs.config.user_ips_max_age | ||||
| 
 | ||||
|         if hs.config.run_background_tasks and self.user_ips_max_age: | ||||
|             self._clock.looping_call(self._prune_old_user_ips, 5 * 1000) | ||||
| 
 | ||||
|     @wrap_as_background_process("prune_old_user_ips") | ||||
|     async def _prune_old_user_ips(self): | ||||
|         """Removes entries in user IPs older than the configured period. | ||||
|         """ | ||||
| 
 | ||||
|         if self.user_ips_max_age is None: | ||||
|             # Nothing to do | ||||
|             return | ||||
| 
 | ||||
|         if not await self.db_pool.updates.has_completed_background_update( | ||||
|             "devices_last_seen" | ||||
|         ): | ||||
|             # Only start pruning if we have finished populating the devices | ||||
|             # last seen info. | ||||
|             return | ||||
| 
 | ||||
|         # We do a slightly funky SQL delete to ensure we don't try and delete | ||||
|         # too much at once (as the table may be very large from before we | ||||
|         # started pruning). | ||||
|         # | ||||
|         # This works by finding the max last_seen that is less than the given | ||||
|         # time, but has no more than N rows before it, deleting all rows with | ||||
|         # a lesser last_seen time. (We COALESCE so that the sub-SELECT always | ||||
|         # returns exactly one row). | ||||
|         sql = """ | ||||
|             DELETE FROM user_ips | ||||
|             WHERE last_seen <= ( | ||||
|                 SELECT COALESCE(MAX(last_seen), -1) | ||||
|                 FROM ( | ||||
|                     SELECT last_seen FROM user_ips | ||||
|                     WHERE last_seen <= ? | ||||
|                     ORDER BY last_seen ASC | ||||
|                     LIMIT 5000 | ||||
|                 ) AS u | ||||
|             ) | ||||
|         """ | ||||
| 
 | ||||
|         timestamp = self.clock.time_msec() - self.user_ips_max_age | ||||
| 
 | ||||
|         def _prune_old_user_ips_txn(txn): | ||||
|             txn.execute(sql, (timestamp,)) | ||||
| 
 | ||||
|         await self.db_pool.runInteraction( | ||||
|             "_prune_old_user_ips", _prune_old_user_ips_txn | ||||
|         ) | ||||
| 
 | ||||
| 
 | ||||
| class ClientIpStore(ClientIpWorkerStore): | ||||
|     def __init__(self, database: DatabasePool, db_conn, hs): | ||||
| 
 | ||||
|         self.client_ip_last_seen = Cache( | ||||
|  | @ -360,8 +416,6 @@ class ClientIpStore(ClientIpBackgroundUpdateStore): | |||
| 
 | ||||
|         super().__init__(database, db_conn, hs) | ||||
| 
 | ||||
|         self.user_ips_max_age = hs.config.user_ips_max_age | ||||
| 
 | ||||
|         # (user_id, access_token, ip,) -> (user_agent, device_id, last_seen) | ||||
|         self._batch_row_update = {} | ||||
| 
 | ||||
|  | @ -372,9 +426,6 @@ class ClientIpStore(ClientIpBackgroundUpdateStore): | |||
|             "before", "shutdown", self._update_client_ips_batch | ||||
|         ) | ||||
| 
 | ||||
|         if self.user_ips_max_age: | ||||
|             self._clock.looping_call(self._prune_old_user_ips, 5 * 1000) | ||||
| 
 | ||||
|     async def insert_client_ip( | ||||
|         self, user_id, access_token, ip, user_agent, device_id, now=None | ||||
|     ): | ||||
|  | @ -525,49 +576,3 @@ class ClientIpStore(ClientIpBackgroundUpdateStore): | |||
|             } | ||||
|             for (access_token, ip), (user_agent, last_seen) in results.items() | ||||
|         ] | ||||
| 
 | ||||
|     @wrap_as_background_process("prune_old_user_ips") | ||||
|     async def _prune_old_user_ips(self): | ||||
|         """Removes entries in user IPs older than the configured period. | ||||
|         """ | ||||
| 
 | ||||
|         if self.user_ips_max_age is None: | ||||
|             # Nothing to do | ||||
|             return | ||||
| 
 | ||||
|         if not await self.db_pool.updates.has_completed_background_update( | ||||
|             "devices_last_seen" | ||||
|         ): | ||||
|             # Only start pruning if we have finished populating the devices | ||||
|             # last seen info. | ||||
|             return | ||||
| 
 | ||||
|         # We do a slightly funky SQL delete to ensure we don't try and delete | ||||
|         # too much at once (as the table may be very large from before we | ||||
|         # started pruning). | ||||
|         # | ||||
|         # This works by finding the max last_seen that is less than the given | ||||
|         # time, but has no more than N rows before it, deleting all rows with | ||||
|         # a lesser last_seen time. (We COALESCE so that the sub-SELECT always | ||||
|         # returns exactly one row). | ||||
|         sql = """ | ||||
|             DELETE FROM user_ips | ||||
|             WHERE last_seen <= ( | ||||
|                 SELECT COALESCE(MAX(last_seen), -1) | ||||
|                 FROM ( | ||||
|                     SELECT last_seen FROM user_ips | ||||
|                     WHERE last_seen <= ? | ||||
|                     ORDER BY last_seen ASC | ||||
|                     LIMIT 5000 | ||||
|                 ) AS u | ||||
|             ) | ||||
|         """ | ||||
| 
 | ||||
|         timestamp = self.clock.time_msec() - self.user_ips_max_age | ||||
| 
 | ||||
|         def _prune_old_user_ips_txn(txn): | ||||
|             txn.execute(sql, (timestamp,)) | ||||
| 
 | ||||
|         await self.db_pool.runInteraction( | ||||
|             "_prune_old_user_ips", _prune_old_user_ips_txn | ||||
|         ) | ||||
|  |  | |||
|  | @ -18,7 +18,7 @@ import time | |||
| from typing import Dict | ||||
| 
 | ||||
| from synapse.metrics import GaugeBucketCollector | ||||
| from synapse.metrics.background_process_metrics import run_as_background_process | ||||
| from synapse.metrics.background_process_metrics import wrap_as_background_process | ||||
| from synapse.storage._base import SQLBaseStore | ||||
| from synapse.storage.database import DatabasePool | ||||
| from synapse.storage.databases.main.event_push_actions import ( | ||||
|  | @ -57,18 +57,13 @@ class ServerMetricsStore(EventPushActionsWorkerStore, SQLBaseStore): | |||
|         super().__init__(database, db_conn, hs) | ||||
| 
 | ||||
|         # Read the extrems every 60 minutes | ||||
|         def read_forward_extremities(): | ||||
|             # run as a background process to make sure that the database transactions | ||||
|             # have a logcontext to report to | ||||
|             return run_as_background_process( | ||||
|                 "read_forward_extremities", self._read_forward_extremities | ||||
|             ) | ||||
| 
 | ||||
|         hs.get_clock().looping_call(read_forward_extremities, 60 * 60 * 1000) | ||||
|         if hs.config.run_background_tasks: | ||||
|             self._clock.looping_call(self._read_forward_extremities, 60 * 60 * 1000) | ||||
| 
 | ||||
|         # Used in _generate_user_daily_visits to keep track of progress | ||||
|         self._last_user_visit_update = self._get_start_of_day() | ||||
| 
 | ||||
|     @wrap_as_background_process("read_forward_extremities") | ||||
|     async def _read_forward_extremities(self): | ||||
|         def fetch(txn): | ||||
|             txn.execute( | ||||
|  | @ -274,6 +269,7 @@ class ServerMetricsStore(EventPushActionsWorkerStore, SQLBaseStore): | |||
|         today_start = calendar.timegm((now.tm_year, now.tm_mon, now.tm_mday, 0, 0, 0)) | ||||
|         return today_start * 1000 | ||||
| 
 | ||||
|     @wrap_as_background_process("generate_user_daily_visits") | ||||
|     async def generate_user_daily_visits(self) -> None: | ||||
|         """ | ||||
|         Generates daily visit data for use in cohort/ retention analysis | ||||
|  |  | |||
|  | @ -14,14 +14,16 @@ | |||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| # See the License for the specific language governing permissions and | ||||
| # limitations under the License. | ||||
| 
 | ||||
| import logging | ||||
| import re | ||||
| from typing import Any, Dict, List, Optional, Tuple | ||||
| 
 | ||||
| from synapse.api.constants import UserTypes | ||||
| from synapse.api.errors import Codes, StoreError, SynapseError, ThreepidValidationError | ||||
| from synapse.metrics.background_process_metrics import run_as_background_process | ||||
| from synapse.metrics.background_process_metrics import ( | ||||
|     run_as_background_process, | ||||
|     wrap_as_background_process, | ||||
| ) | ||||
| from synapse.storage._base import SQLBaseStore | ||||
| from synapse.storage.database import DatabasePool | ||||
| from synapse.storage.types import Cursor | ||||
|  | @ -48,6 +50,21 @@ class RegistrationWorkerStore(SQLBaseStore): | |||
|             database.engine, find_max_generated_user_id_localpart, "user_id_seq", | ||||
|         ) | ||||
| 
 | ||||
|         self._account_validity = hs.config.account_validity | ||||
|         if hs.config.run_background_tasks and self._account_validity.enabled: | ||||
|             self._clock.call_later( | ||||
|                 0.0, | ||||
|                 run_as_background_process, | ||||
|                 "account_validity_set_expiration_dates", | ||||
|                 self._set_expiration_date_when_missing, | ||||
|             ) | ||||
| 
 | ||||
|         # Create a background job for culling expired 3PID validity tokens | ||||
|         if hs.config.run_background_tasks: | ||||
|             self.clock.looping_call( | ||||
|                 self.cull_expired_threepid_validation_tokens, THIRTY_MINUTES_IN_MS | ||||
|             ) | ||||
| 
 | ||||
|     @cached() | ||||
|     async def get_user_by_id(self, user_id: str) -> Optional[Dict[str, Any]]: | ||||
|         return await self.db_pool.simple_select_one( | ||||
|  | @ -778,6 +795,78 @@ class RegistrationWorkerStore(SQLBaseStore): | |||
|             "delete_threepid_session", delete_threepid_session_txn | ||||
|         ) | ||||
| 
 | ||||
|     @wrap_as_background_process("cull_expired_threepid_validation_tokens") | ||||
|     async def cull_expired_threepid_validation_tokens(self) -> None: | ||||
|         """Remove threepid validation tokens with expiry dates that have passed""" | ||||
| 
 | ||||
|         def cull_expired_threepid_validation_tokens_txn(txn, ts): | ||||
|             sql = """ | ||||
|             DELETE FROM threepid_validation_token WHERE | ||||
|             expires < ? | ||||
|             """ | ||||
|             txn.execute(sql, (ts,)) | ||||
| 
 | ||||
|         await self.db_pool.runInteraction( | ||||
|             "cull_expired_threepid_validation_tokens", | ||||
|             cull_expired_threepid_validation_tokens_txn, | ||||
|             self.clock.time_msec(), | ||||
|         ) | ||||
| 
 | ||||
|     async def _set_expiration_date_when_missing(self): | ||||
|         """ | ||||
|         Retrieves the list of registered users that don't have an expiration date, and | ||||
|         adds an expiration date for each of them. | ||||
|         """ | ||||
| 
 | ||||
|         def select_users_with_no_expiration_date_txn(txn): | ||||
|             """Retrieves the list of registered users with no expiration date from the | ||||
|             database, filtering out deactivated users. | ||||
|             """ | ||||
|             sql = ( | ||||
|                 "SELECT users.name FROM users" | ||||
|                 " LEFT JOIN account_validity ON (users.name = account_validity.user_id)" | ||||
|                 " WHERE account_validity.user_id is NULL AND users.deactivated = 0;" | ||||
|             ) | ||||
|             txn.execute(sql, []) | ||||
| 
 | ||||
|             res = self.db_pool.cursor_to_dict(txn) | ||||
|             if res: | ||||
|                 for user in res: | ||||
|                     self.set_expiration_date_for_user_txn( | ||||
|                         txn, user["name"], use_delta=True | ||||
|                     ) | ||||
| 
 | ||||
|         await self.db_pool.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, 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 - d ; now + period] range, d being a | ||||
|                 delta equal to 10% of the validity period. | ||||
|         """ | ||||
|         now_ms = self._clock.time_msec() | ||||
|         expiration_ts = now_ms + self._account_validity.period | ||||
| 
 | ||||
|         if use_delta: | ||||
|             expiration_ts = self.rand.randrange( | ||||
|                 expiration_ts - self._account_validity.startup_job_max_delta, | ||||
|                 expiration_ts, | ||||
|             ) | ||||
| 
 | ||||
|         self.db_pool.simple_upsert_txn( | ||||
|             txn, | ||||
|             "account_validity", | ||||
|             keyvalues={"user_id": user_id}, | ||||
|             values={"expiration_ts_ms": expiration_ts, "email_sent": False}, | ||||
|         ) | ||||
| 
 | ||||
| 
 | ||||
| class RegistrationBackgroundUpdateStore(RegistrationWorkerStore): | ||||
|     def __init__(self, database: DatabasePool, db_conn, hs): | ||||
|  | @ -911,28 +1000,8 @@ class RegistrationStore(RegistrationBackgroundUpdateStore): | |||
|     def __init__(self, database: DatabasePool, db_conn, hs): | ||||
|         super().__init__(database, db_conn, hs) | ||||
| 
 | ||||
|         self._account_validity = hs.config.account_validity | ||||
|         self._ignore_unknown_session_error = hs.config.request_token_inhibit_3pid_errors | ||||
| 
 | ||||
|         if self._account_validity.enabled: | ||||
|             self._clock.call_later( | ||||
|                 0.0, | ||||
|                 run_as_background_process, | ||||
|                 "account_validity_set_expiration_dates", | ||||
|                 self._set_expiration_date_when_missing, | ||||
|             ) | ||||
| 
 | ||||
|         # Create a background job for culling expired 3PID validity tokens | ||||
|         def start_cull(): | ||||
|             # run as a background process to make sure that the database transactions | ||||
|             # have a logcontext to report to | ||||
|             return run_as_background_process( | ||||
|                 "cull_expired_threepid_validation_tokens", | ||||
|                 self.cull_expired_threepid_validation_tokens, | ||||
|             ) | ||||
| 
 | ||||
|         hs.get_clock().looping_call(start_cull, THIRTY_MINUTES_IN_MS) | ||||
| 
 | ||||
|     async def add_access_token_to_user( | ||||
|         self, | ||||
|         user_id: str, | ||||
|  | @ -1477,22 +1546,6 @@ class RegistrationStore(RegistrationBackgroundUpdateStore): | |||
|             start_or_continue_validation_session_txn, | ||||
|         ) | ||||
| 
 | ||||
|     async def cull_expired_threepid_validation_tokens(self) -> None: | ||||
|         """Remove threepid validation tokens with expiry dates that have passed""" | ||||
| 
 | ||||
|         def cull_expired_threepid_validation_tokens_txn(txn, ts): | ||||
|             sql = """ | ||||
|             DELETE FROM threepid_validation_token WHERE | ||||
|             expires < ? | ||||
|             """ | ||||
|             txn.execute(sql, (ts,)) | ||||
| 
 | ||||
|         await self.db_pool.runInteraction( | ||||
|             "cull_expired_threepid_validation_tokens", | ||||
|             cull_expired_threepid_validation_tokens_txn, | ||||
|             self.clock.time_msec(), | ||||
|         ) | ||||
| 
 | ||||
|     async def set_user_deactivated_status( | ||||
|         self, user_id: str, deactivated: bool | ||||
|     ) -> None: | ||||
|  | @ -1522,61 +1575,6 @@ class RegistrationStore(RegistrationBackgroundUpdateStore): | |||
|         ) | ||||
|         txn.call_after(self.is_guest.invalidate, (user_id,)) | ||||
| 
 | ||||
|     async def _set_expiration_date_when_missing(self): | ||||
|         """ | ||||
|         Retrieves the list of registered users that don't have an expiration date, and | ||||
|         adds an expiration date for each of them. | ||||
|         """ | ||||
| 
 | ||||
|         def select_users_with_no_expiration_date_txn(txn): | ||||
|             """Retrieves the list of registered users with no expiration date from the | ||||
|             database, filtering out deactivated users. | ||||
|             """ | ||||
|             sql = ( | ||||
|                 "SELECT users.name FROM users" | ||||
|                 " LEFT JOIN account_validity ON (users.name = account_validity.user_id)" | ||||
|                 " WHERE account_validity.user_id is NULL AND users.deactivated = 0;" | ||||
|             ) | ||||
|             txn.execute(sql, []) | ||||
| 
 | ||||
|             res = self.db_pool.cursor_to_dict(txn) | ||||
|             if res: | ||||
|                 for user in res: | ||||
|                     self.set_expiration_date_for_user_txn( | ||||
|                         txn, user["name"], use_delta=True | ||||
|                     ) | ||||
| 
 | ||||
|         await self.db_pool.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, 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 - d ; now + period] range, d being a | ||||
|                 delta equal to 10% of the validity period. | ||||
|         """ | ||||
|         now_ms = self._clock.time_msec() | ||||
|         expiration_ts = now_ms + self._account_validity.period | ||||
| 
 | ||||
|         if use_delta: | ||||
|             expiration_ts = self.rand.randrange( | ||||
|                 expiration_ts - self._account_validity.startup_job_max_delta, | ||||
|                 expiration_ts, | ||||
|             ) | ||||
| 
 | ||||
|         self.db_pool.simple_upsert_txn( | ||||
|             txn, | ||||
|             "account_validity", | ||||
|             keyvalues={"user_id": user_id}, | ||||
|             values={"expiration_ts_ms": expiration_ts, "email_sent": False}, | ||||
|         ) | ||||
| 
 | ||||
| 
 | ||||
| def find_max_generated_user_id_localpart(cur: Cursor) -> int: | ||||
|     """ | ||||
|  |  | |||
|  | @ -61,7 +61,10 @@ class RoomMemberWorkerStore(EventsWorkerStore): | |||
|         self._check_safe_current_state_events_membership_updated_txn(txn) | ||||
|         txn.close() | ||||
| 
 | ||||
|         if self.hs.config.metrics_flags.known_servers: | ||||
|         if ( | ||||
|             self.hs.config.run_background_tasks | ||||
|             and self.hs.config.metrics_flags.known_servers | ||||
|         ): | ||||
|             self._known_servers_count = 1 | ||||
|             self.hs.get_clock().looping_call( | ||||
|                 run_as_background_process, | ||||
|  |  | |||
|  | @ -19,7 +19,7 @@ from typing import Iterable, List, Optional, Tuple | |||
| 
 | ||||
| from canonicaljson import encode_canonical_json | ||||
| 
 | ||||
| from synapse.metrics.background_process_metrics import run_as_background_process | ||||
| from synapse.metrics.background_process_metrics import wrap_as_background_process | ||||
| from synapse.storage._base import SQLBaseStore, db_to_json | ||||
| from synapse.storage.database import DatabasePool, LoggingTransaction | ||||
| from synapse.storage.engines import PostgresEngine, Sqlite3Engine | ||||
|  | @ -43,15 +43,33 @@ _UpdateTransactionRow = namedtuple( | |||
| SENTINEL = object() | ||||
| 
 | ||||
| 
 | ||||
| class TransactionStore(SQLBaseStore): | ||||
| class TransactionWorkerStore(SQLBaseStore): | ||||
|     def __init__(self, database: DatabasePool, db_conn, hs): | ||||
|         super().__init__(database, db_conn, hs) | ||||
| 
 | ||||
|         if hs.config.run_background_tasks: | ||||
|             self._clock.looping_call(self._cleanup_transactions, 30 * 60 * 1000) | ||||
| 
 | ||||
|     @wrap_as_background_process("cleanup_transactions") | ||||
|     async def _cleanup_transactions(self) -> None: | ||||
|         now = self._clock.time_msec() | ||||
|         month_ago = now - 30 * 24 * 60 * 60 * 1000 | ||||
| 
 | ||||
|         def _cleanup_transactions_txn(txn): | ||||
|             txn.execute("DELETE FROM received_transactions WHERE ts < ?", (month_ago,)) | ||||
| 
 | ||||
|         await self.db_pool.runInteraction( | ||||
|             "_cleanup_transactions", _cleanup_transactions_txn | ||||
|         ) | ||||
| 
 | ||||
| 
 | ||||
| class TransactionStore(TransactionWorkerStore): | ||||
|     """A collection of queries for handling PDUs. | ||||
|     """ | ||||
| 
 | ||||
|     def __init__(self, database: DatabasePool, db_conn, hs): | ||||
|         super().__init__(database, db_conn, hs) | ||||
| 
 | ||||
|         self._clock.looping_call(self._start_cleanup_transactions, 30 * 60 * 1000) | ||||
| 
 | ||||
|         self._destination_retry_cache = ExpiringCache( | ||||
|             cache_name="get_destination_retry_timings", | ||||
|             clock=self._clock, | ||||
|  | @ -266,22 +284,6 @@ class TransactionStore(SQLBaseStore): | |||
|                 }, | ||||
|             ) | ||||
| 
 | ||||
|     def _start_cleanup_transactions(self): | ||||
|         return run_as_background_process( | ||||
|             "cleanup_transactions", self._cleanup_transactions | ||||
|         ) | ||||
| 
 | ||||
|     async def _cleanup_transactions(self) -> None: | ||||
|         now = self._clock.time_msec() | ||||
|         month_ago = now - 30 * 24 * 60 * 60 * 1000 | ||||
| 
 | ||||
|         def _cleanup_transactions_txn(txn): | ||||
|             txn.execute("DELETE FROM received_transactions WHERE ts < ?", (month_ago,)) | ||||
| 
 | ||||
|         await self.db_pool.runInteraction( | ||||
|             "_cleanup_transactions", _cleanup_transactions_txn | ||||
|         ) | ||||
| 
 | ||||
|     async def store_destination_rooms_entries( | ||||
|         self, destinations: Iterable[str], room_id: str, stream_ordering: int, | ||||
|     ) -> None: | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 Patrick Cloke
						Patrick Cloke