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
|
@ -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.
|
* `enabled`: whether to use Redis support. Defaults to false.
|
||||||
* `host` and `port`: Optional host and port to use to connect to redis. Defaults to
|
* `host` and `port`: Optional host and port to use to connect to redis. Defaults to
|
||||||
localhost and 6379
|
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.
|
* `password`: Optional password if configured on the Redis instance.
|
||||||
* `dbid`: Optional redis dbid if needs to connect to specific redis logical db.
|
* `dbid`: Optional redis dbid if needs to connect to specific redis logical db.
|
||||||
* `use_tls`: Whether to use tls connection. Defaults to false.
|
* `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.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:
|
Example configuration:
|
||||||
```yaml
|
```yaml
|
||||||
redis:
|
redis:
|
||||||
|
|
|
@ -61,6 +61,9 @@ def lazyConnection(
|
||||||
# most methods to it via ConnectionHandler.__getattr__.
|
# most methods to it via ConnectionHandler.__getattr__.
|
||||||
class ConnectionHandler(RedisProtocol):
|
class ConnectionHandler(RedisProtocol):
|
||||||
def disconnect(self) -> "Deferred[None]": ...
|
def disconnect(self) -> "Deferred[None]": ...
|
||||||
|
def __repr__(self) -> str: ...
|
||||||
|
|
||||||
|
class UnixConnectionHandler(ConnectionHandler): ...
|
||||||
|
|
||||||
class RedisFactory(protocol.ReconnectingClientFactory):
|
class RedisFactory(protocol.ReconnectingClientFactory):
|
||||||
continueTrying: bool
|
continueTrying: bool
|
||||||
|
|
|
@ -33,6 +33,7 @@ class RedisConfig(Config):
|
||||||
|
|
||||||
self.redis_host = redis_config.get("host", "localhost")
|
self.redis_host = redis_config.get("host", "localhost")
|
||||||
self.redis_port = redis_config.get("port", 6379)
|
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_dbid = redis_config.get("dbid", None)
|
||||||
self.redis_password = redis_config.get("password")
|
self.redis_password = redis_config.get("password")
|
||||||
|
|
||||||
|
|
|
@ -352,7 +352,15 @@ class ReplicationCommandHandler:
|
||||||
|
|
||||||
reactor = hs.get_reactor()
|
reactor = hs.get_reactor()
|
||||||
redis_config = hs.config.redis
|
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)
|
ssl_context_factory = ClientContextFactory(hs.config.redis)
|
||||||
reactor.connectSSL(
|
reactor.connectSSL(
|
||||||
redis_config.redis_host,
|
redis_config.redis_host,
|
||||||
|
|
|
@ -17,7 +17,12 @@ from inspect import isawaitable
|
||||||
from typing import TYPE_CHECKING, Any, Generic, List, Optional, Type, TypeVar, cast
|
from typing import TYPE_CHECKING, Any, Generic, List, Optional, Type, TypeVar, cast
|
||||||
|
|
||||||
import attr
|
import attr
|
||||||
import txredisapi
|
from txredisapi import (
|
||||||
|
ConnectionHandler,
|
||||||
|
RedisFactory,
|
||||||
|
SubscriberProtocol,
|
||||||
|
UnixConnectionHandler,
|
||||||
|
)
|
||||||
from zope.interface import implementer
|
from zope.interface import implementer
|
||||||
|
|
||||||
from twisted.internet.address import IPv4Address, IPv6Address
|
from twisted.internet.address import IPv4Address, IPv6Address
|
||||||
|
@ -68,7 +73,7 @@ class ConstantProperty(Generic[T, V]):
|
||||||
|
|
||||||
|
|
||||||
@implementer(IReplicationConnection)
|
@implementer(IReplicationConnection)
|
||||||
class RedisSubscriber(txredisapi.SubscriberProtocol):
|
class RedisSubscriber(SubscriberProtocol):
|
||||||
"""Connection to redis subscribed to replication stream.
|
"""Connection to redis subscribed to replication stream.
|
||||||
|
|
||||||
This class fulfils two functions:
|
This class fulfils two functions:
|
||||||
|
@ -95,7 +100,7 @@ class RedisSubscriber(txredisapi.SubscriberProtocol):
|
||||||
synapse_handler: "ReplicationCommandHandler"
|
synapse_handler: "ReplicationCommandHandler"
|
||||||
synapse_stream_prefix: str
|
synapse_stream_prefix: str
|
||||||
synapse_channel_names: List[str]
|
synapse_channel_names: List[str]
|
||||||
synapse_outbound_redis_connection: txredisapi.ConnectionHandler
|
synapse_outbound_redis_connection: ConnectionHandler
|
||||||
|
|
||||||
def __init__(self, *args: Any, **kwargs: Any):
|
def __init__(self, *args: Any, **kwargs: Any):
|
||||||
super().__init__(*args, **kwargs)
|
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
|
"""A subclass of RedisFactory that periodically sends pings to ensure that
|
||||||
we detect dead connections.
|
we detect dead connections.
|
||||||
"""
|
"""
|
||||||
|
@ -245,7 +250,7 @@ class SynapseRedisFactory(txredisapi.RedisFactory):
|
||||||
dbid: Optional[int],
|
dbid: Optional[int],
|
||||||
poolsize: int,
|
poolsize: int,
|
||||||
isLazy: bool = False,
|
isLazy: bool = False,
|
||||||
handler: Type = txredisapi.ConnectionHandler,
|
handler: Type = ConnectionHandler,
|
||||||
charset: str = "utf-8",
|
charset: str = "utf-8",
|
||||||
password: Optional[str] = None,
|
password: Optional[str] = None,
|
||||||
replyTimeout: int = 30,
|
replyTimeout: int = 30,
|
||||||
|
@ -326,7 +331,7 @@ class RedisDirectTcpReplicationClientFactory(SynapseRedisFactory):
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
hs: "HomeServer",
|
hs: "HomeServer",
|
||||||
outbound_redis_connection: txredisapi.ConnectionHandler,
|
outbound_redis_connection: ConnectionHandler,
|
||||||
channel_names: List[str],
|
channel_names: List[str],
|
||||||
):
|
):
|
||||||
super().__init__(
|
super().__init__(
|
||||||
|
@ -368,7 +373,7 @@ def lazyConnection(
|
||||||
reconnect: bool = True,
|
reconnect: bool = True,
|
||||||
password: Optional[str] = None,
|
password: Optional[str] = None,
|
||||||
replyTimeout: int = 30,
|
replyTimeout: int = 30,
|
||||||
) -> txredisapi.ConnectionHandler:
|
) -> ConnectionHandler:
|
||||||
"""Creates a connection to Redis that is lazily set up and reconnects if the
|
"""Creates a connection to Redis that is lazily set up and reconnects if the
|
||||||
connections is lost.
|
connections is lost.
|
||||||
"""
|
"""
|
||||||
|
@ -380,7 +385,7 @@ def lazyConnection(
|
||||||
dbid=dbid,
|
dbid=dbid,
|
||||||
poolsize=1,
|
poolsize=1,
|
||||||
isLazy=True,
|
isLazy=True,
|
||||||
handler=txredisapi.ConnectionHandler,
|
handler=ConnectionHandler,
|
||||||
password=password,
|
password=password,
|
||||||
replyTimeout=replyTimeout,
|
replyTimeout=replyTimeout,
|
||||||
)
|
)
|
||||||
|
@ -408,3 +413,44 @@ def lazyConnection(
|
||||||
)
|
)
|
||||||
|
|
||||||
return factory.handler
|
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
|
# We only want to import redis module if we're using it, as we have
|
||||||
# `txredisapi` as an optional dependency.
|
# `txredisapi` as an optional dependency.
|
||||||
from synapse.replication.tcp.redis import lazyConnection
|
from synapse.replication.tcp.redis import lazyConnection, lazyUnixConnection
|
||||||
|
|
||||||
logger.info(
|
if self.config.redis.redis_path is None:
|
||||||
"Connecting to redis (host=%r port=%r) for external cache",
|
logger.info(
|
||||||
self.config.redis.redis_host,
|
"Connecting to redis (host=%r port=%r) for external cache",
|
||||||
self.config.redis.redis_port,
|
self.config.redis.redis_host,
|
||||||
)
|
self.config.redis.redis_port,
|
||||||
|
)
|
||||||
|
|
||||||
return lazyConnection(
|
return lazyConnection(
|
||||||
hs=self,
|
hs=self,
|
||||||
host=self.config.redis.redis_host,
|
host=self.config.redis.redis_host,
|
||||||
port=self.config.redis.redis_port,
|
port=self.config.redis.redis_port,
|
||||||
dbid=self.config.redis.redis_dbid,
|
dbid=self.config.redis.redis_dbid,
|
||||||
password=self.config.redis.redis_password,
|
password=self.config.redis.redis_password,
|
||||||
reconnect=True,
|
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:
|
def should_send_federation(self) -> bool:
|
||||||
"Should this server be sending federation traffic directly?"
|
"Should this server be sending federation traffic directly?"
|
||||||
|
|
Loading…
Reference in New Issue