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

Commit 70259d8

Browse files
Limit AS transactions to 100 events (#8606)
* Limit AS transactions to 100 events * Update changelog.d/8606.feature Co-authored-by: Andrew Morgan <[email protected]> * Add tests * Update synapse/appservice/scheduler.py Co-authored-by: Andrew Morgan <[email protected]>
1 parent 20a67aa commit 70259d8

File tree

3 files changed

+58
-2
lines changed

3 files changed

+58
-2
lines changed

changelog.d/8606.feature

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Limit appservice transactions to 100 persistent and 100 ephemeral events.

synapse/appservice/scheduler.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,13 @@
6060
logger = logging.getLogger(__name__)
6161

6262

63+
# Maximum number of events to provide in an AS transaction.
64+
MAX_PERSISTENT_EVENTS_PER_TRANSACTION = 100
65+
66+
# Maximum number of ephemeral events to provide in an AS transaction.
67+
MAX_EPHEMERAL_EVENTS_PER_TRANSACTION = 100
68+
69+
6370
class ApplicationServiceScheduler:
6471
""" Public facing API for this module. Does the required DI to tie the
6572
components together. This also serves as the "event_pool", which in this
@@ -136,10 +143,17 @@ async def _send_request(self, service: ApplicationService):
136143
self.requests_in_flight.add(service.id)
137144
try:
138145
while True:
139-
events = self.queued_events.pop(service.id, [])
140-
ephemeral = self.queued_ephemeral.pop(service.id, [])
146+
all_events = self.queued_events.get(service.id, [])
147+
events = all_events[:MAX_PERSISTENT_EVENTS_PER_TRANSACTION]
148+
del all_events[:MAX_PERSISTENT_EVENTS_PER_TRANSACTION]
149+
150+
all_events_ephemeral = self.queued_ephemeral.get(service.id, [])
151+
ephemeral = all_events_ephemeral[:MAX_EPHEMERAL_EVENTS_PER_TRANSACTION]
152+
del all_events_ephemeral[:MAX_EPHEMERAL_EVENTS_PER_TRANSACTION]
153+
141154
if not events and not ephemeral:
142155
return
156+
143157
try:
144158
await self.txn_ctrl.send(service, events, ephemeral)
145159
except Exception:

tests/appservice/test_scheduler.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,31 @@ def do_send(x, y, z):
260260
self.txn_ctrl.send.assert_called_with(srv2, [srv_2_event2], [])
261261
self.assertEquals(3, self.txn_ctrl.send.call_count)
262262

263+
def test_send_large_txns(self):
264+
srv_1_defer = defer.Deferred()
265+
srv_2_defer = defer.Deferred()
266+
send_return_list = [srv_1_defer, srv_2_defer]
267+
268+
def do_send(x, y, z):
269+
return make_deferred_yieldable(send_return_list.pop(0))
270+
271+
self.txn_ctrl.send = Mock(side_effect=do_send)
272+
273+
service = Mock(id=4, name="service")
274+
event_list = [Mock(name="event%i" % (i + 1)) for i in range(200)]
275+
for event in event_list:
276+
self.queuer.enqueue_event(service, event)
277+
278+
# Expect the first event to be sent immediately.
279+
self.txn_ctrl.send.assert_called_with(service, [event_list[0]], [])
280+
srv_1_defer.callback(service)
281+
# Then send the next 100 events
282+
self.txn_ctrl.send.assert_called_with(service, event_list[1:101], [])
283+
srv_2_defer.callback(service)
284+
# Then the final 99 events
285+
self.txn_ctrl.send.assert_called_with(service, event_list[101:], [])
286+
self.assertEquals(3, self.txn_ctrl.send.call_count)
287+
263288
def test_send_single_ephemeral_no_queue(self):
264289
# Expect the event to be sent immediately.
265290
service = Mock(id=4, name="service")
@@ -296,3 +321,19 @@ def test_send_single_ephemeral_with_queue(self):
296321
# Expect the queued events to be sent
297322
self.txn_ctrl.send.assert_called_with(service, [], event_list_2 + event_list_3)
298323
self.assertEquals(2, self.txn_ctrl.send.call_count)
324+
325+
def test_send_large_txns_ephemeral(self):
326+
d = defer.Deferred()
327+
self.txn_ctrl.send = Mock(
328+
side_effect=lambda x, y, z: make_deferred_yieldable(d)
329+
)
330+
# Expect the event to be sent immediately.
331+
service = Mock(id=4, name="service")
332+
first_chunk = [Mock(name="event%i" % (i + 1)) for i in range(100)]
333+
second_chunk = [Mock(name="event%i" % (i + 101)) for i in range(50)]
334+
event_list = first_chunk + second_chunk
335+
self.queuer.enqueue_ephemeral(service, event_list)
336+
self.txn_ctrl.send.assert_called_once_with(service, [], first_chunk)
337+
d.callback(service)
338+
self.txn_ctrl.send.assert_called_with(service, [], second_chunk)
339+
self.assertEquals(2, self.txn_ctrl.send.call_count)

0 commit comments

Comments
 (0)