Skip to content

Commit c9cbb2c

Browse files
authored
Merge pull request #47 from epandurski/master
Look for a DEACTIVATED marker file
2 parents b285c3f + cad708a commit c9cbb2c

File tree

5 files changed

+89
-0
lines changed

5 files changed

+89
-0
lines changed

development.env

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ APP_ASSOCIATED_LOGGERS=swpt_pythonlib.flask_signalbus.signalbus_cli
2525
APP_SSL_HANDSHAKE_TIMEOUT=5
2626
APP_MAX_CACHED_PEERS=5000
2727
APP_PEERS_CACHE_SECONDS=600
28+
APP_PEERS_CHECK_SECONDS=3600
2829
APP_FILE_READ_THREADS=5
2930
APP_RMQ_CONNECTION_TIMEOUT_SECONDS=10
3031
APP_RMQ_CONFIRMATION_TIMEOUT_SECONDS=20

swpt_stomp/peer_data.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,13 @@ async def _get_peer_data(
413413
except FileNotFoundError: # pragma: nocover
414414
return None
415415

416+
try:
417+
await self._read_file(f"{dir}/DEACTIVATED")
418+
except FileNotFoundError:
419+
pass
420+
else: # pragma: nocover
421+
return None
422+
416423
try:
417424
# Peers that do not have a file with the name "ACTIVE" in their
418425
# corresponding directories are considered inactive. The

swpt_stomp/rmq.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ def _NO_TM(m: "RmqMessage") -> Message:
3131
)
3232

3333

34+
async def _NEVER() -> bool:
35+
return False
36+
37+
3438
APP_RMQ_CONNECTION_TIMEOUT_SECONDS = float(
3539
os.environ.get(
3640
"APP_RMQ_CONNECTION_TIMEOUT_SECONDS",
@@ -124,6 +128,7 @@ async def publish_to_exchange(
124128
confirmation_timeout: float = APP_RMQ_CONFIRMATION_TIMEOUT_SECONDS,
125129
connection_timeout: float = APP_RMQ_CONNECTION_TIMEOUT_SECONDS,
126130
channel: Optional[AbstractChannel] = None,
131+
is_peer_deactivated: Callable[[], Awaitable[bool]] = _NEVER,
127132
) -> None:
128133
"""Publishes messages to a RabbitMQ exchange.
129134
@@ -156,6 +161,7 @@ async def publish_messages(ch: AbstractChannel) -> None:
156161
exchange_name=exchange_name,
157162
preprocess_message=preprocess_message,
158163
confirmation_timeout=confirmation_timeout,
164+
is_peer_deactivated=is_peer_deactivated,
159165
)
160166

161167
try:
@@ -299,6 +305,7 @@ async def _publish_to_exchange(
299305
exchange_name: str,
300306
preprocess_message: Callable[[Message], Awaitable[RmqMessage]],
301307
confirmation_timeout: float,
308+
is_peer_deactivated: Callable[[], Awaitable[bool]],
302309
) -> None:
303310
exchange = await channel.get_exchange(exchange_name, ensure=False)
304311
deliveries: deque[_Delivery] = deque()
@@ -343,6 +350,9 @@ async def deliver_message(message: Message) -> None:
343350

344351
async def publish_messages() -> None:
345352
while message := await recv_queue.get():
353+
if await is_peer_deactivated():
354+
raise ServerError("The peer has been deactivated.")
355+
346356
delivery = _Delivery(message.id)
347357
mark_as_confirmed = partial(on_confirmation, delivery)
348358

swpt_stomp/server.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@
3535

3636
import logging
3737
import sys
38+
import time
39+
import random
3840
import os
3941
import os.path
4042
import asyncio
@@ -69,6 +71,9 @@
6971
APP_MAX_CONNECTIONS_PER_PEER = int(
7072
os.environ.get("APP_MAX_CONNECTIONS_PER_PEER", "10")
7173
)
74+
APP_PEERS_CHECK_SECONDS = float(
75+
os.environ.get("APP_PEERS_CHECK_SECONDS", "3600")
76+
)
7277

7378
_EXCHANGE_NAMES = {
7479
NodeType.AA: "accounts_in",
@@ -129,6 +134,20 @@ async def publish(transport: asyncio.Transport) -> None:
129134
)
130135
raise
131136
else:
137+
next_peer_check_at = (
138+
time.time() + random.random() * APP_PEERS_CHECK_SECONDS
139+
)
140+
141+
async def is_peer_deactivated() -> bool:
142+
nonlocal next_peer_check_at
143+
now = time.time()
144+
if now < next_peer_check_at:
145+
return False
146+
else: # pragma: no cover
147+
next_peer_check_at = now + APP_PEERS_CHECK_SECONDS
148+
peer_data = await db.get_peer_data(peer_serial_number)
149+
return peer_data is None
150+
132151
with _allowed_peer_connection(peer_data.node_id):
133152
await publish_to_exchange(
134153
send_queue,
@@ -139,6 +158,7 @@ async def publish(transport: asyncio.Transport) -> None:
139158
preprocess_message, owner_node_data, peer_data
140159
),
141160
channel=channel,
161+
is_peer_deactivated=is_peer_deactivated,
142162
)
143163

144164
return StompServer(

tests/test_rmq.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,3 +223,54 @@ async def preprocess_message(m):
223223
assert isinstance(m, ServerError)
224224
assert m.error_message == "Test error"
225225
assert send_queue.empty()
226+
227+
228+
@pytest.mark.asyncio
229+
async def test_publish_deactivated_peer(rmq_url):
230+
await ready_queue(rmq_url)
231+
loop = asyncio.get_running_loop()
232+
send_queue = asyncio.Queue(5)
233+
recv_queue = WatermarkQueue(5)
234+
235+
message = Message(
236+
id="1",
237+
type="TestMessage",
238+
body=bytearray(b"Test message"),
239+
content_type="text/plain",
240+
)
241+
await recv_queue.put(message)
242+
243+
async def preprocess_message(m):
244+
return RmqMessage(
245+
id=m.id,
246+
body=bytes(m.body),
247+
headers={
248+
"message-type": m.type,
249+
"debtor-id": 1,
250+
"creditor-id": 2,
251+
"coordinator-id": 3,
252+
},
253+
type=m.type,
254+
content_type=m.content_type,
255+
routing_key="test_stomp",
256+
)
257+
258+
async def always() -> bool:
259+
return True
260+
261+
publish_task = loop.create_task(
262+
publish_to_exchange(
263+
send_queue,
264+
recv_queue,
265+
url=rmq_url,
266+
exchange_name="",
267+
preprocess_message=preprocess_message,
268+
is_peer_deactivated=always,
269+
)
270+
)
271+
272+
await asyncio.wait_for(publish_task, 10.0)
273+
m = await send_queue.get()
274+
assert isinstance(m, ServerError)
275+
assert m.error_message == "The peer has been deactivated."
276+
assert send_queue.empty()

0 commit comments

Comments
 (0)