mirror of
https://git.anonymousland.org/anonymousland/synapse.git
synced 2025-12-15 14:05:27 -05:00
Allow moving account data and receipts streams off master (#9104)
This commit is contained in:
parent
f08ef64926
commit
6633a4015a
27 changed files with 854 additions and 279 deletions
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
from synapse.http.server import JsonResource
|
||||
from synapse.replication.http import (
|
||||
account_data,
|
||||
devices,
|
||||
federation,
|
||||
login,
|
||||
|
|
@ -40,6 +41,7 @@ class ReplicationRestResource(JsonResource):
|
|||
presence.register_servlets(hs, self)
|
||||
membership.register_servlets(hs, self)
|
||||
streams.register_servlets(hs, self)
|
||||
account_data.register_servlets(hs, self)
|
||||
|
||||
# The following can't currently be instantiated on workers.
|
||||
if hs.config.worker.worker_app is None:
|
||||
|
|
|
|||
187
synapse/replication/http/account_data.py
Normal file
187
synapse/replication/http/account_data.py
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# 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
|
||||
|
||||
from synapse.http.servlet import parse_json_object_from_request
|
||||
from synapse.replication.http._base import ReplicationEndpoint
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ReplicationUserAccountDataRestServlet(ReplicationEndpoint):
|
||||
"""Add user account data on the appropriate account data worker.
|
||||
|
||||
Request format:
|
||||
|
||||
POST /_synapse/replication/add_user_account_data/:user_id/:type
|
||||
|
||||
{
|
||||
"content": { ... },
|
||||
}
|
||||
|
||||
"""
|
||||
|
||||
NAME = "add_user_account_data"
|
||||
PATH_ARGS = ("user_id", "account_data_type")
|
||||
CACHE = False
|
||||
|
||||
def __init__(self, hs):
|
||||
super().__init__(hs)
|
||||
|
||||
self.handler = hs.get_account_data_handler()
|
||||
self.clock = hs.get_clock()
|
||||
|
||||
@staticmethod
|
||||
async def _serialize_payload(user_id, account_data_type, content):
|
||||
payload = {
|
||||
"content": content,
|
||||
}
|
||||
|
||||
return payload
|
||||
|
||||
async def _handle_request(self, request, user_id, account_data_type):
|
||||
content = parse_json_object_from_request(request)
|
||||
|
||||
max_stream_id = await self.handler.add_account_data_for_user(
|
||||
user_id, account_data_type, content["content"]
|
||||
)
|
||||
|
||||
return 200, {"max_stream_id": max_stream_id}
|
||||
|
||||
|
||||
class ReplicationRoomAccountDataRestServlet(ReplicationEndpoint):
|
||||
"""Add room account data on the appropriate account data worker.
|
||||
|
||||
Request format:
|
||||
|
||||
POST /_synapse/replication/add_room_account_data/:user_id/:room_id/:account_data_type
|
||||
|
||||
{
|
||||
"content": { ... },
|
||||
}
|
||||
|
||||
"""
|
||||
|
||||
NAME = "add_room_account_data"
|
||||
PATH_ARGS = ("user_id", "room_id", "account_data_type")
|
||||
CACHE = False
|
||||
|
||||
def __init__(self, hs):
|
||||
super().__init__(hs)
|
||||
|
||||
self.handler = hs.get_account_data_handler()
|
||||
self.clock = hs.get_clock()
|
||||
|
||||
@staticmethod
|
||||
async def _serialize_payload(user_id, room_id, account_data_type, content):
|
||||
payload = {
|
||||
"content": content,
|
||||
}
|
||||
|
||||
return payload
|
||||
|
||||
async def _handle_request(self, request, user_id, room_id, account_data_type):
|
||||
content = parse_json_object_from_request(request)
|
||||
|
||||
max_stream_id = await self.handler.add_account_data_to_room(
|
||||
user_id, room_id, account_data_type, content["content"]
|
||||
)
|
||||
|
||||
return 200, {"max_stream_id": max_stream_id}
|
||||
|
||||
|
||||
class ReplicationAddTagRestServlet(ReplicationEndpoint):
|
||||
"""Add tag on the appropriate account data worker.
|
||||
|
||||
Request format:
|
||||
|
||||
POST /_synapse/replication/add_tag/:user_id/:room_id/:tag
|
||||
|
||||
{
|
||||
"content": { ... },
|
||||
}
|
||||
|
||||
"""
|
||||
|
||||
NAME = "add_tag"
|
||||
PATH_ARGS = ("user_id", "room_id", "tag")
|
||||
CACHE = False
|
||||
|
||||
def __init__(self, hs):
|
||||
super().__init__(hs)
|
||||
|
||||
self.handler = hs.get_account_data_handler()
|
||||
self.clock = hs.get_clock()
|
||||
|
||||
@staticmethod
|
||||
async def _serialize_payload(user_id, room_id, tag, content):
|
||||
payload = {
|
||||
"content": content,
|
||||
}
|
||||
|
||||
return payload
|
||||
|
||||
async def _handle_request(self, request, user_id, room_id, tag):
|
||||
content = parse_json_object_from_request(request)
|
||||
|
||||
max_stream_id = await self.handler.add_tag_to_room(
|
||||
user_id, room_id, tag, content["content"]
|
||||
)
|
||||
|
||||
return 200, {"max_stream_id": max_stream_id}
|
||||
|
||||
|
||||
class ReplicationRemoveTagRestServlet(ReplicationEndpoint):
|
||||
"""Remove tag on the appropriate account data worker.
|
||||
|
||||
Request format:
|
||||
|
||||
POST /_synapse/replication/remove_tag/:user_id/:room_id/:tag
|
||||
|
||||
{}
|
||||
|
||||
"""
|
||||
|
||||
NAME = "remove_tag"
|
||||
PATH_ARGS = (
|
||||
"user_id",
|
||||
"room_id",
|
||||
"tag",
|
||||
)
|
||||
CACHE = False
|
||||
|
||||
def __init__(self, hs):
|
||||
super().__init__(hs)
|
||||
|
||||
self.handler = hs.get_account_data_handler()
|
||||
self.clock = hs.get_clock()
|
||||
|
||||
@staticmethod
|
||||
async def _serialize_payload(user_id, room_id, tag):
|
||||
|
||||
return {}
|
||||
|
||||
async def _handle_request(self, request, user_id, room_id, tag):
|
||||
max_stream_id = await self.handler.remove_tag_from_room(user_id, room_id, tag,)
|
||||
|
||||
return 200, {"max_stream_id": max_stream_id}
|
||||
|
||||
|
||||
def register_servlets(hs, http_server):
|
||||
ReplicationUserAccountDataRestServlet(hs).register(http_server)
|
||||
ReplicationRoomAccountDataRestServlet(hs).register(http_server)
|
||||
ReplicationAddTagRestServlet(hs).register(http_server)
|
||||
ReplicationRemoveTagRestServlet(hs).register(http_server)
|
||||
|
|
@ -33,9 +33,13 @@ class BaseSlavedStore(CacheInvalidationWorkerStore):
|
|||
database,
|
||||
stream_name="caches",
|
||||
instance_name=hs.get_instance_name(),
|
||||
table="cache_invalidation_stream_by_instance",
|
||||
instance_column="instance_name",
|
||||
id_column="stream_id",
|
||||
tables=[
|
||||
(
|
||||
"cache_invalidation_stream_by_instance",
|
||||
"instance_name",
|
||||
"stream_id",
|
||||
)
|
||||
],
|
||||
sequence_name="cache_invalidation_stream_seq",
|
||||
writers=[],
|
||||
) # type: Optional[MultiWriterIdGenerator]
|
||||
|
|
|
|||
|
|
@ -15,47 +15,9 @@
|
|||
# limitations under the License.
|
||||
|
||||
from synapse.replication.slave.storage._base import BaseSlavedStore
|
||||
from synapse.replication.slave.storage._slaved_id_tracker import SlavedIdTracker
|
||||
from synapse.replication.tcp.streams import AccountDataStream, TagAccountDataStream
|
||||
from synapse.storage.database import DatabasePool
|
||||
from synapse.storage.databases.main.account_data import AccountDataWorkerStore
|
||||
from synapse.storage.databases.main.tags import TagsWorkerStore
|
||||
|
||||
|
||||
class SlavedAccountDataStore(TagsWorkerStore, AccountDataWorkerStore, BaseSlavedStore):
|
||||
def __init__(self, database: DatabasePool, db_conn, hs):
|
||||
self._account_data_id_gen = SlavedIdTracker(
|
||||
db_conn,
|
||||
"account_data",
|
||||
"stream_id",
|
||||
extra_tables=[
|
||||
("room_account_data", "stream_id"),
|
||||
("room_tags_revisions", "stream_id"),
|
||||
],
|
||||
)
|
||||
|
||||
super().__init__(database, db_conn, hs)
|
||||
|
||||
def get_max_account_data_stream_id(self):
|
||||
return self._account_data_id_gen.get_current_token()
|
||||
|
||||
def process_replication_rows(self, stream_name, instance_name, token, rows):
|
||||
if stream_name == TagAccountDataStream.NAME:
|
||||
self._account_data_id_gen.advance(instance_name, token)
|
||||
for row in rows:
|
||||
self.get_tags_for_user.invalidate((row.user_id,))
|
||||
self._account_data_stream_cache.entity_has_changed(row.user_id, token)
|
||||
elif stream_name == AccountDataStream.NAME:
|
||||
self._account_data_id_gen.advance(instance_name, token)
|
||||
for row in rows:
|
||||
if not row.room_id:
|
||||
self.get_global_account_data_by_type_for_user.invalidate(
|
||||
(row.data_type, row.user_id)
|
||||
)
|
||||
self.get_account_data_for_user.invalidate((row.user_id,))
|
||||
self.get_account_data_for_room.invalidate((row.user_id, row.room_id))
|
||||
self.get_account_data_for_room_and_type.invalidate(
|
||||
(row.user_id, row.room_id, row.data_type)
|
||||
)
|
||||
self._account_data_stream_cache.entity_has_changed(row.user_id, token)
|
||||
return super().process_replication_rows(stream_name, instance_name, token, rows)
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -14,43 +14,10 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from synapse.replication.tcp.streams import ReceiptsStream
|
||||
from synapse.storage.database import DatabasePool
|
||||
from synapse.storage.databases.main.receipts import ReceiptsWorkerStore
|
||||
|
||||
from ._base import BaseSlavedStore
|
||||
from ._slaved_id_tracker import SlavedIdTracker
|
||||
|
||||
|
||||
class SlavedReceiptsStore(ReceiptsWorkerStore, BaseSlavedStore):
|
||||
def __init__(self, database: DatabasePool, db_conn, hs):
|
||||
# We instantiate this first as the ReceiptsWorkerStore constructor
|
||||
# needs to be able to call get_max_receipt_stream_id
|
||||
self._receipts_id_gen = SlavedIdTracker(
|
||||
db_conn, "receipts_linearized", "stream_id"
|
||||
)
|
||||
|
||||
super().__init__(database, db_conn, hs)
|
||||
|
||||
def get_max_receipt_stream_id(self):
|
||||
return self._receipts_id_gen.get_current_token()
|
||||
|
||||
def invalidate_caches_for_receipt(self, room_id, receipt_type, user_id):
|
||||
self.get_receipts_for_user.invalidate((user_id, receipt_type))
|
||||
self._get_linearized_receipts_for_room.invalidate_many((room_id,))
|
||||
self.get_last_receipt_event_id_for_user.invalidate(
|
||||
(user_id, room_id, receipt_type)
|
||||
)
|
||||
self._invalidate_get_users_with_receipts_in_room(room_id, receipt_type, user_id)
|
||||
self.get_receipts_for_room.invalidate((room_id, receipt_type))
|
||||
|
||||
def process_replication_rows(self, stream_name, instance_name, token, rows):
|
||||
if stream_name == ReceiptsStream.NAME:
|
||||
self._receipts_id_gen.advance(instance_name, token)
|
||||
for row in rows:
|
||||
self.invalidate_caches_for_receipt(
|
||||
row.room_id, row.receipt_type, row.user_id
|
||||
)
|
||||
self._receipts_stream_cache.entity_has_changed(row.room_id, token)
|
||||
|
||||
return super().process_replication_rows(stream_name, instance_name, token, rows)
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -51,11 +51,14 @@ from synapse.replication.tcp.commands import (
|
|||
from synapse.replication.tcp.protocol import AbstractConnection
|
||||
from synapse.replication.tcp.streams import (
|
||||
STREAMS_MAP,
|
||||
AccountDataStream,
|
||||
BackfillStream,
|
||||
CachesStream,
|
||||
EventsStream,
|
||||
FederationStream,
|
||||
ReceiptsStream,
|
||||
Stream,
|
||||
TagAccountDataStream,
|
||||
ToDeviceStream,
|
||||
TypingStream,
|
||||
)
|
||||
|
|
@ -132,6 +135,22 @@ class ReplicationCommandHandler:
|
|||
|
||||
continue
|
||||
|
||||
if isinstance(stream, (AccountDataStream, TagAccountDataStream)):
|
||||
# Only add AccountDataStream and TagAccountDataStream as a source on the
|
||||
# instance in charge of account_data persistence.
|
||||
if hs.get_instance_name() in hs.config.worker.writers.account_data:
|
||||
self._streams_to_replicate.append(stream)
|
||||
|
||||
continue
|
||||
|
||||
if isinstance(stream, ReceiptsStream):
|
||||
# Only add ReceiptsStream as a source on the instance in charge of
|
||||
# receipts.
|
||||
if hs.get_instance_name() in hs.config.worker.writers.receipts:
|
||||
self._streams_to_replicate.append(stream)
|
||||
|
||||
continue
|
||||
|
||||
# Only add any other streams if we're on master.
|
||||
if hs.config.worker_app is not None:
|
||||
continue
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue