summaryrefslogtreecommitdiffstats
path: root/proxy/queue.py
diff options
context:
space:
mode:
Diffstat (limited to 'proxy/queue.py')
-rw-r--r--proxy/queue.py133
1 files changed, 133 insertions, 0 deletions
diff --git a/proxy/queue.py b/proxy/queue.py
new file mode 100644
index 0000000..cfa0f3c
--- /dev/null
+++ b/proxy/queue.py
@@ -0,0 +1,133 @@
1import asyncio
2import json
3import logging
4import os
5import traceback
6from typing import Type
7
8from ttun_server.redis import RedisConnectionPool
9from ttun_server.types import Message
10
11logger = logging.getLogger(__name__)
12
13
14class BaseProxyQueue:
15 def __init__(self, identifier: str):
16 self.identifier = identifier
17
18 @classmethod
19 async def create_for_identifier(cls, identifier: str) -> 'BaseProxyQueue':
20 raise NotImplementedError(f'Please implement create_for_identifier')
21
22 @classmethod
23 async def get_for_identifier(cls, identifier: str) -> Type['self']:
24 assert await cls.has_connection(identifier)
25 return cls(identifier)
26
27 @classmethod
28 async def has_connection(cls, identifier) -> bool:
29 raise NotImplementedError(f'Please implement has_connection')
30
31 async def enqueue(self, message: Message):
32 raise NotImplementedError(f'Please implement send_request')
33
34 async def dequeue(self) -> Message:
35 raise NotImplementedError(f'Please implement handle_requests')
36
37 async def delete(self):
38 raise NotImplementedError(f'Please implement delete')
39
40
41class MemoryProxyQueue(BaseProxyQueue):
42 connections: dict[str, asyncio.Queue] = {}
43
44 @classmethod
45 async def has_connection(cls, identifier) -> bool:
46 return identifier in cls.connections
47
48 @classmethod
49 async def create_for_identifier(cls, identifier: str) -> 'MemoryProxyQueue':
50 instance = cls(identifier)
51 cls.connections[identifier] = asyncio.Queue()
52
53 return instance
54
55 async def enqueue(self, message: Message):
56 return await self.__class__.connections[self.identifier].put(message)
57
58 async def dequeue(self) -> Message:
59 return await self.__class__.connections[self.identifier].get()
60
61 async def delete(self):
62 del self.__class__.connections[self.identifier]
63
64
65class RedisProxyQueue(BaseProxyQueue):
66 def __init__(self, identifier):
67 super().__init__(identifier)
68
69 self.pubsub = RedisConnectionPool()\
70 .get_connection()\
71 .pubsub()
72
73 self.subscription_queue = asyncio.Queue()
74
75 @classmethod
76 async def create_for_identifier(cls, identifier: str) -> 'BaseProxyQueue':
77 instance = cls(identifier)
78
79 await instance.pubsub.subscribe(f'request_{identifier}')
80 return instance
81
82 @classmethod
83 async def get_for_identifier(cls, identifier: str) -> 'RedisProxyQueue':
84 instance: 'RedisProxyQueue' = await super().get_for_identifier(identifier)
85
86 await instance.pubsub.subscribe(f'response_{identifier}')
87
88 return instance
89
90 @classmethod
91 async def has_connection(cls, identifier) -> bool:
92 logger.debug(await RedisConnectionPool.get_connection().pubsub_channels())
93 return f'request_{identifier}' in {
94 channel.decode()
95 for channel
96 in await RedisConnectionPool \
97 .get_connection() \
98 .pubsub_channels()
99 }
100
101 async def wait_for_message(self):
102 async for message in self.pubsub.listen():
103 match message['type']:
104 case 'subscribe':
105 continue
106 case _:
107 return message['data']
108
109 async def enqueue(self, message: Message):
110 await RedisConnectionPool \
111 .get_connection() \
112 .publish(f'request_{self.identifier}', json.dumps(message))
113
114 async def dequeue(self) -> Message:
115 message = await self.wait_for_message()
116 return json.loads(message)
117
118 async def delete(self):
119 await self.pubsub.unsubscribe(f'request_{self.identifier}')
120
121 await RedisConnectionPool.get_connection()\
122 .srem('connections', self.identifier)
123
124
125class ProxyQueueMeta(type):
126 def __new__(cls, name, superclasses, attributes):
127 return RedisProxyQueue \
128 if 'REDIS_URL' in os.environ \
129 else MemoryProxyQueue
130
131
132class ProxyQueue(BaseProxyQueue, metaclass=ProxyQueueMeta):
133 pass