def test_mirror_worker_rate_limiting(self, mock_mirror_email: MagicMock, mock_warn: MagicMock) -> None: fake_client = self.FakeClient() realm = get_realm('zulip') RateLimitedRealmMirror(realm).clear_history() stream = get_stream('Denmark', realm) stream_to_address = encode_email_address(stream) data = [ dict( message='\xf3test', time=time.time(), rcpt_to=stream_to_address ) ] * 5 for element in data: fake_client.queue.append(('email_mirror', element)) with simulated_queue_client(lambda: fake_client): start_time = time.time() with patch('time.time', return_value=start_time): worker = queue_processors.MirrorWorker() worker.setup() worker.start() # Of the first 5 messages, only 2 should be processed # (the rest being rate-limited): self.assertEqual(mock_mirror_email.call_count, 2) # If a new message is sent into the stream mirror, it will get rejected: fake_client.queue.append(('email_mirror', data[0])) worker.start() self.assertEqual(mock_mirror_email.call_count, 2) # However, missed message emails don't get rate limited: with self.settings(EMAIL_GATEWAY_PATTERN="*****@*****.**"): address = 'mm' + ('x' * 32) + '@example.com' event = dict( message='\xf3test', time=time.time(), rcpt_to=address ) fake_client.queue.append(('email_mirror', event)) worker.start() self.assertEqual(mock_mirror_email.call_count, 3) # After some times passes, emails get accepted again: with patch('time.time', return_value=(start_time + 11.0)): fake_client.queue.append(('email_mirror', data[0])) worker.start() self.assertEqual(mock_mirror_email.call_count, 4) # If RateLimiterLockingException is thrown, we rate-limit the new message: with patch('zerver.lib.rate_limiter.RedisRateLimiterBackend.incr_ratelimit', side_effect=RateLimiterLockingException): fake_client.queue.append(('email_mirror', data[0])) worker.start() self.assertEqual(mock_mirror_email.call_count, 4) mock_warn.assert_called_with( "Deadlock trying to incr_ratelimit for %s", "RateLimitedRealmMirror:%s" % (realm.string_id,), )
def test_mirror_worker_rate_limiting(self, mock_mirror_email: MagicMock) -> None: fake_client = self.FakeClient() realm = get_realm("zulip") RateLimitedRealmMirror(realm).clear_history() stream = get_stream("Denmark", realm) stream_to_address = encode_email_address(stream) data = [ dict( msg_base64=base64.b64encode(b"\xf3test").decode(), time=time.time(), rcpt_to=stream_to_address, ), ] * 5 for element in data: fake_client.enqueue("email_mirror", element) with simulated_queue_client(lambda: fake_client), self.assertLogs( "zerver.worker.queue_processors", level="WARNING" ) as warn_logs: start_time = time.time() with patch("time.time", return_value=start_time): worker = queue_processors.MirrorWorker() worker.setup() worker.start() # Of the first 5 messages, only 2 should be processed # (the rest being rate-limited): self.assertEqual(mock_mirror_email.call_count, 2) # If a new message is sent into the stream mirror, it will get rejected: fake_client.enqueue("email_mirror", data[0]) worker.start() self.assertEqual(mock_mirror_email.call_count, 2) # However, message notification emails don't get rate limited: with self.settings(EMAIL_GATEWAY_PATTERN="*****@*****.**"): address = "mm" + ("x" * 32) + "@example.com" event = dict( msg_base64=base64.b64encode(b"\xf3test").decode(), time=time.time(), rcpt_to=address, ) fake_client.enqueue("email_mirror", event) worker.start() self.assertEqual(mock_mirror_email.call_count, 3) # After some times passes, emails get accepted again: with patch("time.time", return_value=(start_time + 11.0)): fake_client.enqueue("email_mirror", data[0]) worker.start() self.assertEqual(mock_mirror_email.call_count, 4) # If RateLimiterLockingException is thrown, we rate-limit the new message: with patch( "zerver.lib.rate_limiter.RedisRateLimiterBackend.incr_ratelimit", side_effect=RateLimiterLockingException, ): with self.assertLogs("zerver.lib.rate_limiter", "WARNING") as mock_warn: fake_client.enqueue("email_mirror", data[0]) worker.start() self.assertEqual(mock_mirror_email.call_count, 4) self.assertEqual( mock_warn.output, [ "WARNING:zerver.lib.rate_limiter:Deadlock trying to incr_ratelimit for RateLimitedRealmMirror:zulip" ], ) self.assertEqual( warn_logs.output, [ "WARNING:zerver.worker.queue_processors:MirrorWorker: Rejecting an email from: None to realm: Zulip Dev - rate limited." ] * 5, )