async def call_rpc_local(self, api_name: str, name: str, kwargs: dict): api = registry.get(api_name) start_time = time.time() try: result = await api.call(name, kwargs) except (CancelledError, SuddenDeathException): raise except Exception as e: logger.warning(L("⚡ Error while executing {}.{}. Took {}", Bold(api_name), Bold(name), human_time(time.time() - start_time))) return e else: logger.info(L("⚡ Executed {}.{} in {}", Bold(api_name), Bold(name), human_time(time.time() - start_time))) return result
async def call_rpc_remote(self, api_name: str, name: str, kwargs: dict, options: dict): rpc_message = RpcMessage(api_name=api_name, procedure_name=name, kwargs=kwargs) return_path = self.result_transport.get_return_path(rpc_message) rpc_message.return_path = return_path options = options or {} timeout = options.get('timeout', 5) logger.info("➡ Calling remote RPC ".format(rpc_message)) start_time = time.time() # TODO: It is possible that the RPC will be called before we start waiting for the response. This is bad. future = asyncio.gather( self.receive_result(rpc_message, return_path, options=options), self.rpc_transport.call_rpc(rpc_message, options=options), ) await plugin_hook('before_rpc_call', rpc_message=rpc_message, bus_client=self) try: result_message, _ = await asyncio.wait_for(future, timeout=timeout) except asyncio.TimeoutError: future.cancel() # TODO: Include description of possible causes and how to increase the timeout. # TODO: Remove RPC from queue. Perhaps add a RpcBackend.cancel() method. Optional, # as not all backends will support it. No point processing calls which have timed out. raise LightbusTimeout('Timeout when calling RPC {} after {} seconds'.format( rpc_message.canonical_name, timeout )) from None await plugin_hook('after_rpc_call', rpc_message=rpc_message, result_message=result_message, bus_client=self) if not result_message.error: logger.info(L("⚡ Remote call of {} completed in {}", Bold(rpc_message.canonical_name), human_time(time.time() - start_time))) else: logger.warning( L("⚡ Server error during remote call of {}. Took {}: {}", Bold(rpc_message.canonical_name), human_time(time.time() - start_time), result_message.result, ), ) raise LightbusServerError('Error while calling {}: {}\nRemote stack trace:\n{}'.format( rpc_message.canonical_name, result_message.result, result_message.trace, )) return result_message.result
async def receive_result(self, rpc_message: RpcMessage, return_path: str, options: dict) -> ResultMessage: logger.info(L("⌛ Awaiting Redis result for RPC message: {}", Bold(rpc_message))) redis_key = self._parse_return_path(return_path) pool = await self.get_redis_pool() with await pool as redis: start_time = time.time() # TODO: Make timeout configurable _, result = await redis.blpop(redis_key, timeout=5) result_dictionary = redis_decode(result) logger.info(L( "⬅ Received Redis result in {} for RPC message {}: {}", human_time(time.time() - start_time), rpc_message, Bold(result) )) return ResultMessage.from_dict(result_dictionary)
async def call_rpc(self, rpc_message: RpcMessage, options: dict): stream = '{}:stream'.format(rpc_message.api_name) logger.debug( LBullets( L("Enqueuing message {} in Redis stream {}", Bold(rpc_message), Bold(stream)), items=rpc_message.to_dict() ) ) pool = await self.get_redis_pool() with await pool as redis: start_time = time.time() # TODO: MAXLEN await redis.xadd(stream=stream, fields=encode_message_fields(rpc_message.to_dict())) logger.info(L( "Enqueued message {} in Redis in {} stream {}", Bold(rpc_message), human_time(time.time() - start_time), Bold(stream) ))
async def send_event(self, event_message: EventMessage, options: dict): """Publish an event""" stream = '{}.{}:stream'.format(event_message.api_name, event_message.event_name) logger.debug( LBullets( L("Enqueuing event message {} in Redis stream {}", Bold(event_message), Bold(stream)), items=event_message.to_dict() ) ) pool = await self.get_redis_pool() with await pool as redis: start_time = time.time() # TODO: MAXLEN await redis.xadd(stream=stream, fields=encode_message_fields(event_message.to_dict())) logger.info(L( "Enqueued event message {} in Redis in {} stream {}", Bold(event_message), human_time(time.time() - start_time), Bold(stream) ))
async def send_result(self, rpc_message: RpcMessage, result_message: ResultMessage, return_path: str): logger.debug(L( "Sending result {} into Redis using return path {}", Bold(result_message), Bold(return_path) )) redis_key = self._parse_return_path(return_path) pool = await self.get_redis_pool() with await pool as redis: start_time = time.time() p = redis.pipeline() p.lpush(redis_key, redis_encode(result_message.to_dict())) # TODO: Make result expiry configurable p.expire(redis_key, timeout=60) await p.execute() logger.debug(L( "➡ Sent result {} into Redis in {} using return path {}", Bold(result_message), human_time(time.time() - start_time), Bold(return_path) ))