mirror of
				https://github.com/matrix-org/synapse.git
				synced 2025-10-31 12:18:24 +00:00 
			
		
		
		
	Add Unix socket support for Redis connections (#15644)
Adds a new configuration setting to connect to Redis via a Unix socket instead of over TCP. Disabled by default.
This commit is contained in:
		
							parent
							
								
									50918c4940
								
							
						
					
					
						commit
						c835befd10
					
				
							
								
								
									
										1
									
								
								changelog.d/15644.feature
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								changelog.d/15644.feature
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| Add Unix socket support for Redis connections. Contributed by Jason Little. | ||||
| @ -3979,6 +3979,8 @@ This setting has the following sub-options: | ||||
| * `enabled`: whether to use Redis support. Defaults to false. | ||||
| * `host` and `port`: Optional host and port to use to connect to redis. Defaults to | ||||
|    localhost and 6379 | ||||
| * `path`: The full path to a local Unix socket file. **If this is used, `host` and | ||||
|  `port` are ignored.** Defaults to `/tmp/redis.sock' | ||||
| * `password`: Optional password if configured on the Redis instance. | ||||
| * `dbid`: Optional redis dbid if needs to connect to specific redis logical db. | ||||
| * `use_tls`: Whether to use tls connection. Defaults to false. | ||||
| @ -3991,6 +3993,8 @@ This setting has the following sub-options: | ||||
| 
 | ||||
|   _Changed in Synapse 1.84.0: Added use\_tls, certificate\_file, private\_key\_file, ca\_file and ca\_path attributes_ | ||||
| 
 | ||||
|   _Changed in Synapse 1.85.0: Added path option to use a local Unix socket_ | ||||
| 
 | ||||
| Example configuration: | ||||
| ```yaml | ||||
| redis: | ||||
|  | ||||
| @ -61,6 +61,9 @@ def lazyConnection( | ||||
| # most methods to it via ConnectionHandler.__getattr__. | ||||
| class ConnectionHandler(RedisProtocol): | ||||
|     def disconnect(self) -> "Deferred[None]": ... | ||||
|     def __repr__(self) -> str: ... | ||||
| 
 | ||||
| class UnixConnectionHandler(ConnectionHandler): ... | ||||
| 
 | ||||
| class RedisFactory(protocol.ReconnectingClientFactory): | ||||
|     continueTrying: bool | ||||
|  | ||||
| @ -33,6 +33,7 @@ class RedisConfig(Config): | ||||
| 
 | ||||
|         self.redis_host = redis_config.get("host", "localhost") | ||||
|         self.redis_port = redis_config.get("port", 6379) | ||||
|         self.redis_path = redis_config.get("path", None) | ||||
|         self.redis_dbid = redis_config.get("dbid", None) | ||||
|         self.redis_password = redis_config.get("password") | ||||
| 
 | ||||
|  | ||||
| @ -352,7 +352,15 @@ class ReplicationCommandHandler: | ||||
| 
 | ||||
|         reactor = hs.get_reactor() | ||||
|         redis_config = hs.config.redis | ||||
|         if hs.config.redis.redis_use_tls: | ||||
|         if redis_config.redis_path is not None: | ||||
|             reactor.connectUNIX( | ||||
|                 redis_config.redis_path, | ||||
|                 self._factory, | ||||
|                 timeout=30, | ||||
|                 checkPID=False, | ||||
|             ) | ||||
| 
 | ||||
|         elif hs.config.redis.redis_use_tls: | ||||
|             ssl_context_factory = ClientContextFactory(hs.config.redis) | ||||
|             reactor.connectSSL( | ||||
|                 redis_config.redis_host, | ||||
|  | ||||
| @ -17,7 +17,12 @@ from inspect import isawaitable | ||||
| from typing import TYPE_CHECKING, Any, Generic, List, Optional, Type, TypeVar, cast | ||||
| 
 | ||||
| import attr | ||||
| import txredisapi | ||||
| from txredisapi import ( | ||||
|     ConnectionHandler, | ||||
|     RedisFactory, | ||||
|     SubscriberProtocol, | ||||
|     UnixConnectionHandler, | ||||
| ) | ||||
| from zope.interface import implementer | ||||
| 
 | ||||
| from twisted.internet.address import IPv4Address, IPv6Address | ||||
| @ -68,7 +73,7 @@ class ConstantProperty(Generic[T, V]): | ||||
| 
 | ||||
| 
 | ||||
| @implementer(IReplicationConnection) | ||||
| class RedisSubscriber(txredisapi.SubscriberProtocol): | ||||
| class RedisSubscriber(SubscriberProtocol): | ||||
|     """Connection to redis subscribed to replication stream. | ||||
| 
 | ||||
|     This class fulfils two functions: | ||||
| @ -95,7 +100,7 @@ class RedisSubscriber(txredisapi.SubscriberProtocol): | ||||
|     synapse_handler: "ReplicationCommandHandler" | ||||
|     synapse_stream_prefix: str | ||||
|     synapse_channel_names: List[str] | ||||
|     synapse_outbound_redis_connection: txredisapi.ConnectionHandler | ||||
|     synapse_outbound_redis_connection: ConnectionHandler | ||||
| 
 | ||||
|     def __init__(self, *args: Any, **kwargs: Any): | ||||
|         super().__init__(*args, **kwargs) | ||||
| @ -229,7 +234,7 @@ class RedisSubscriber(txredisapi.SubscriberProtocol): | ||||
|         ) | ||||
| 
 | ||||
| 
 | ||||
| class SynapseRedisFactory(txredisapi.RedisFactory): | ||||
| class SynapseRedisFactory(RedisFactory): | ||||
|     """A subclass of RedisFactory that periodically sends pings to ensure that | ||||
|     we detect dead connections. | ||||
|     """ | ||||
| @ -245,7 +250,7 @@ class SynapseRedisFactory(txredisapi.RedisFactory): | ||||
|         dbid: Optional[int], | ||||
|         poolsize: int, | ||||
|         isLazy: bool = False, | ||||
|         handler: Type = txredisapi.ConnectionHandler, | ||||
|         handler: Type = ConnectionHandler, | ||||
|         charset: str = "utf-8", | ||||
|         password: Optional[str] = None, | ||||
|         replyTimeout: int = 30, | ||||
| @ -326,7 +331,7 @@ class RedisDirectTcpReplicationClientFactory(SynapseRedisFactory): | ||||
|     def __init__( | ||||
|         self, | ||||
|         hs: "HomeServer", | ||||
|         outbound_redis_connection: txredisapi.ConnectionHandler, | ||||
|         outbound_redis_connection: ConnectionHandler, | ||||
|         channel_names: List[str], | ||||
|     ): | ||||
|         super().__init__( | ||||
| @ -368,7 +373,7 @@ def lazyConnection( | ||||
|     reconnect: bool = True, | ||||
|     password: Optional[str] = None, | ||||
|     replyTimeout: int = 30, | ||||
| ) -> txredisapi.ConnectionHandler: | ||||
| ) -> ConnectionHandler: | ||||
|     """Creates a connection to Redis that is lazily set up and reconnects if the | ||||
|     connections is lost. | ||||
|     """ | ||||
| @ -380,7 +385,7 @@ def lazyConnection( | ||||
|         dbid=dbid, | ||||
|         poolsize=1, | ||||
|         isLazy=True, | ||||
|         handler=txredisapi.ConnectionHandler, | ||||
|         handler=ConnectionHandler, | ||||
|         password=password, | ||||
|         replyTimeout=replyTimeout, | ||||
|     ) | ||||
| @ -408,3 +413,44 @@ def lazyConnection( | ||||
|         ) | ||||
| 
 | ||||
|     return factory.handler | ||||
| 
 | ||||
| 
 | ||||
| def lazyUnixConnection( | ||||
|     hs: "HomeServer", | ||||
|     path: str = "/tmp/redis.sock", | ||||
|     dbid: Optional[int] = None, | ||||
|     reconnect: bool = True, | ||||
|     password: Optional[str] = None, | ||||
|     replyTimeout: int = 30, | ||||
| ) -> ConnectionHandler: | ||||
|     """Creates a connection to Redis that is lazily set up and reconnects if the | ||||
|     connection is lost. | ||||
| 
 | ||||
|     Returns: | ||||
|         A subclass of ConnectionHandler, which is a UnixConnectionHandler in this case. | ||||
|     """ | ||||
| 
 | ||||
|     uuid = path | ||||
| 
 | ||||
|     factory = SynapseRedisFactory( | ||||
|         hs, | ||||
|         uuid=uuid, | ||||
|         dbid=dbid, | ||||
|         poolsize=1, | ||||
|         isLazy=True, | ||||
|         handler=UnixConnectionHandler, | ||||
|         password=password, | ||||
|         replyTimeout=replyTimeout, | ||||
|     ) | ||||
|     factory.continueTrying = reconnect | ||||
| 
 | ||||
|     reactor = hs.get_reactor() | ||||
| 
 | ||||
|     reactor.connectUNIX( | ||||
|         path, | ||||
|         factory, | ||||
|         timeout=30, | ||||
|         checkPID=False, | ||||
|     ) | ||||
| 
 | ||||
|     return factory.handler | ||||
|  | ||||
| @ -864,22 +864,36 @@ class HomeServer(metaclass=abc.ABCMeta): | ||||
| 
 | ||||
|         # We only want to import redis module if we're using it, as we have | ||||
|         # `txredisapi` as an optional dependency. | ||||
|         from synapse.replication.tcp.redis import lazyConnection | ||||
|         from synapse.replication.tcp.redis import lazyConnection, lazyUnixConnection | ||||
| 
 | ||||
|         logger.info( | ||||
|             "Connecting to redis (host=%r port=%r) for external cache", | ||||
|             self.config.redis.redis_host, | ||||
|             self.config.redis.redis_port, | ||||
|         ) | ||||
|         if self.config.redis.redis_path is None: | ||||
|             logger.info( | ||||
|                 "Connecting to redis (host=%r port=%r) for external cache", | ||||
|                 self.config.redis.redis_host, | ||||
|                 self.config.redis.redis_port, | ||||
|             ) | ||||
| 
 | ||||
|         return lazyConnection( | ||||
|             hs=self, | ||||
|             host=self.config.redis.redis_host, | ||||
|             port=self.config.redis.redis_port, | ||||
|             dbid=self.config.redis.redis_dbid, | ||||
|             password=self.config.redis.redis_password, | ||||
|             reconnect=True, | ||||
|         ) | ||||
|             return lazyConnection( | ||||
|                 hs=self, | ||||
|                 host=self.config.redis.redis_host, | ||||
|                 port=self.config.redis.redis_port, | ||||
|                 dbid=self.config.redis.redis_dbid, | ||||
|                 password=self.config.redis.redis_password, | ||||
|                 reconnect=True, | ||||
|             ) | ||||
|         else: | ||||
|             logger.info( | ||||
|                 "Connecting to redis (path=%r) for external cache", | ||||
|                 self.config.redis.redis_path, | ||||
|             ) | ||||
| 
 | ||||
|             return lazyUnixConnection( | ||||
|                 hs=self, | ||||
|                 path=self.config.redis.redis_path, | ||||
|                 dbid=self.config.redis.redis_dbid, | ||||
|                 password=self.config.redis.redis_password, | ||||
|                 reconnect=True, | ||||
|             ) | ||||
| 
 | ||||
|     def should_send_federation(self) -> bool: | ||||
|         "Should this server be sending federation traffic directly?" | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user