Skip to content
This repository has been archived by the owner on Apr 26, 2024. It is now read-only.

Commit

Permalink
Add linearizer on user ID to push rule PUT/DELETE requests (#16052)
Browse files Browse the repository at this point in the history
See: #16053

Signed off by Nick @ Beeper (@Fizzadar)
  • Loading branch information
Fizzadar committed Aug 11, 2023
1 parent 7f4b413 commit 614efc4
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 6 deletions.
1 change: 1 addition & 0 deletions changelog.d/16052.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix long-standing bug where concurrent requests to change a user's push rules could cause a deadlock. Contributed by Nick @ Beeper (@fizzadar).
28 changes: 22 additions & 6 deletions synapse/rest/client/push_rule.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
from synapse.rest.client._base import client_patterns
from synapse.storage.push_rule import InconsistentRuleException, RuleNotFoundException
from synapse.types import JsonDict
from synapse.util.async_helpers import Linearizer

if TYPE_CHECKING:
from synapse.server import HomeServer
Expand All @@ -53,26 +54,32 @@ def __init__(self, hs: "HomeServer"):
self.notifier = hs.get_notifier()
self._is_worker = hs.config.worker.worker_app is not None
self._push_rules_handler = hs.get_push_rules_handler()
self._push_rule_linearizer = Linearizer(name="push_rules")

async def on_PUT(self, request: SynapseRequest, path: str) -> Tuple[int, JsonDict]:
if self._is_worker:
raise Exception("Cannot handle PUT /push_rules on worker")

requester = await self.auth.get_user_by_req(request)
user_id = requester.user.to_string()

async with self._push_rule_linearizer.queue(user_id):
return await self.handle_put(request, path, user_id)

async def handle_put(
self, request: SynapseRequest, path: str, user_id: str
) -> Tuple[int, JsonDict]:
spec = _rule_spec_from_path(path.split("/"))
try:
priority_class = _priority_class_from_spec(spec)
except InvalidRuleException as e:
raise SynapseError(400, str(e))

requester = await self.auth.get_user_by_req(request)

if "/" in spec.rule_id or "\\" in spec.rule_id:
raise SynapseError(400, "rule_id may not contain slashes")

content = parse_json_value_from_request(request)

user_id = requester.user.to_string()

if spec.attr:
try:
await self._push_rules_handler.set_rule_attr(user_id, spec, content)
Expand Down Expand Up @@ -126,11 +133,20 @@ async def on_DELETE(
if self._is_worker:
raise Exception("Cannot handle DELETE /push_rules on worker")

spec = _rule_spec_from_path(path.split("/"))

requester = await self.auth.get_user_by_req(request)
user_id = requester.user.to_string()

async with self._push_rule_linearizer.queue(user_id):
return await self.handle_delete(request, path, user_id)

async def handle_delete(
self,
request: SynapseRequest,
path: str,
user_id: str,
) -> Tuple[int, JsonDict]:
spec = _rule_spec_from_path(path.split("/"))

namespaced_rule_id = f"global/{spec.template}/{spec.rule_id}"

try:
Expand Down

0 comments on commit 614efc4

Please sign in to comment.