async def get_response(self, request_id) -> Awaitable: channel_name = QUEUE_PREFIX + request_id logger.debug('SUBSCRIBE %s', channel_name) channel = (await self.pool.subscribe(channel_name))[0] async def get_message(): wait_coro = asyncio.ensure_future(channel.wait_message()) timeout_coro = asyncio.ensure_future( asyncio.sleep(self.request_timeout)) done = set() try: done, running = await asyncio.wait( [wait_coro, timeout_coro], return_when=asyncio.FIRST_COMPLETED) except Exception as error: # pragma: no cover logger.error('Error while waiting for message:', error) raise exceptions.RadicalException( f'Error while waiting for message: {str(error)}') else: if timeout_coro in done: raise exceptions.TimeoutException( 'Timeout while waiting for response.') message = await channel.get() return message finally: timeout_coro.cancel() try: await self.pool.unsubscribe(channel_name) except Exception as error: # pragma: no cover raise exceptions.TransportException(str(error)) channel.close() return get_message()
async def start(self): self.pool = await aioredis.create_redis_pool( self.transport_url, loop=self.loop, minsize=4 # At lest 2: one for BLPOP, other for PUBLISH ) logger.debug('Redis transport started')
async def call(self, queue_name, method, *args, **kwargs): logger.debug('Calling %s from %s (nowait mode)', method, queue_name) radical_request = RadicalRequest(signature=Signature(method=method, args=args, kwargs=kwargs), reply_to=None) data = self.serializer.encode_request(radical_request) await self.transport.send_to(queue_name, data)
async def get_next_request(self): source_name = QUEUE_PREFIX + self.queue_name try: result = await self.pool.execute('blpop', source_name, 1) if result is not None: logger.debug('BLPOP %s', source_name) return result[1] except Exception as error: logger.error(f'ERROR: {repr(error)}, retrying in 1 second') await asyncio.sleep(1)
async def send_to(self, queue_name, message): source_name = QUEUE_PREFIX + queue_name async with self.pool.acquire() as conn: async with conn.cursor() as cursor: logger.debug(f'Sending request to {queue_name}') try: await self._lock(cursor) await cursor.execute( f'INSERT INTO {source_name}(data) VALUES(%s)', [message]) finally: await self._unlock(cursor)
async def get_message(): async with self.pool.acquire() as conn: async with conn.cursor() as cursor: logger.debug(f'Waiting for response to {request_id}') try: await cursor.execute(f'LISTEN {channel_name}') msg = await asyncio.wait_for(conn.notifies.get(), self.request_timeout) return msg.payload except asyncio.TimeoutError: raise exceptions.TimeoutException( 'Timeout while waiting for response.') finally: await cursor.execute(f'UNLISTEN {channel_name}')
async def call_wait(self, queue_name, method, *args, **kwargs): logger.debug('Calling %s from %s (wait mode)', method, queue_name) message_id = uuid.uuid1().hex response_coroutine = await self.transport.get_response(message_id) response_future = asyncio.ensure_future(response_coroutine, loop=self.loop) radical_request = RadicalRequest(signature=Signature(method=method, args=args, kwargs=kwargs), reply_to=message_id) data = self.serializer.encode_request(radical_request) await self.transport.send_to(queue_name, data) response = await response_future # TODO: Add timeout radical_response = self.serializer.decode_response(response) if radical_response.error: raise RadicalException(radical_response.error) return radical_response.result
async def get_next_request(self): result = None async with self.pool.acquire() as conn: async with conn.cursor() as cursor: try: await self._lock(cursor) await cursor.execute( f'SELECT * FROM {self.request_table} LIMIT 1') row = await cursor.fetchone() if row: logger.debug('Received new request in queue table.') id_, data = row await cursor.execute( f'DELETE FROM {self.request_table} WHERE id = {id_}' ) return bytes(data) finally: await self._unlock(cursor) await asyncio.sleep(1) return result
async def start(self): self.closed.clear() self.pool = await aiopg.create_pool(host=self.urlinfo.hostname, port=self.urlinfo.port or 5432, database=self.urlinfo.path[1:], user=self.urlinfo.username, password=self.urlinfo.password) async with self.pool.acquire() as conn: async with conn.cursor() as cursor: await self._lock(cursor) await cursor.execute( 'SELECT COUNT(*) FROM pg_tables WHERE tablename = %s', [self.request_table]) count, = await cursor.fetchone() if not count: logger.debug('Creating queue table.') await cursor.execute( f'CREATE TABLE {self.request_table}(id serial, data bytea)' ) await self._unlock(cursor) logger.debug('Postgres transport started')
async def send_to(self, queue_name, message): source_name = QUEUE_PREFIX + queue_name logger.debug('LPUSH %s', source_name) await self.pool.execute('lpush', source_name, message)
async def reply_to(self, request_id, message): source_name = QUEUE_PREFIX + request_id logger.debug('PUBLISH %s', source_name) await self.pool.execute('publish', source_name, message)
async def reply_to(self, request_id, message): source_name = QUEUE_PREFIX + request_id async with self.pool.acquire() as conn: async with conn.cursor() as cursor: logger.debug(f'NOTIFY {source_name}') await cursor.execute(f'NOTIFY {source_name}, %s', [message])