FastAPI endpoint blocked until a message has been consumed with aio-pika - fastapi

I am using a FastAPI endpoint to do some sort of long polling and also aiopika to wait messages that a client is publishing to rabbitmq connection/channel/exchange/queue.
My question: When I make a request to the endpoint it is blocked waiting for a message to arrive but as you can see in the below snipped if I put a print() instruction before it starts consuming it is not shown until a message arrives. Why this happens?
#router.post("/long_polling")
async def long_polling(request):
device_id = "1234"
print("THIS IS NOT PRINTED UNTIL A MESSAGE IS PUBLISHED!!!")
connection = await aio_pika.connect("amqp://user:pwd#rabbitmq:5672/")
async with connection:
channel = await connection.channel()
exchange = await channel.declare_exchange("exchange", aio_pika.ExchangeType.TOPIC)
queue = await channel.declare_queue("")
await queue.bind(exchange, routing_key="{}.outgoing".format(device_id))
print("NOT PRINTED UNTIL A MESSAGE ARRIVE ALTHOUGH BEFORE CONSUMING")
# START CONSUMING
async with queue.iterator() as queue_iter:
async for message in queue_iter:
async with message.process():
print(message.body)
return
When I published a message, the endpoint returns properly, so just to know why the previous instructions are not executed until a new message arrives.

Related

FastAPI as backend for Telethon

I'm trying to make api auth with telethon work. I'm sending request to endpoint where telegram client is initialized and trying to send code request to telegram. But there is input() and I didn't find any solution to pass code as variable
#router.get('/code')
async def send_code_request(phone: str):
client = get_telegram_client(phone)
await client.start(phone)
return {'msg': 'code sent'}
I found easier solution, but there is one con - when authorizing via session sign_in() method is requiring to execute send_code_request() method first so there is will be 2 same code messages
async def get_telegram_client(session: str = None) -> TelegramClient:
return TelegramClient(
StringSession(session),
api_id=settings.TELEGRAM_API_ID,
api_hash=settings.TELEGRAM_API_HASH
)
#router.post('/code')
async def send_authorizarion_code(payload: TelegramSendCode):
client = await get_telegram_client()
await client.connect()
try:
await client.send_code_request(payload.phone)
except FloodWaitError as e:
return {
'FloodWaitError': {
'phone_number': e.request.phone_number,
'seconds': e.seconds
}}
else:
return {
'msg': 'code sent',
'session': client.session.save()
}
#router.post('/auth')
async def authorize(payload: TelegramAuth):
client = await get_telegram_client(payload.session)
await client.connect()
await client.send_code_request(payload.phone)
await client.sign_in(code=payload.code, phone=payload.phone)
return {'msg': 'signed in'}
I'm assuming you're using .start() for that.
.start() accepts a callback that is by default input() you can pass your own input like so.
client.start(code_callback=your_callback) and your callback should should return the code.
This can all be found here in the start docs

Best way to use httpx async client and tenacity?

I'm getting fairly different results with two different implementations.
Here is implementation 1
request_semaphore = asyncio.Semaphore(5)
async def _send_async_request(client: AsyncClient, method, auth, url, body):
async with request_semaphore:
try:
async for attempt in AsyncRetrying(stop=stop_after_attempt(3), wait=wait_fixed(1)):
with attempt:
response = await client.request(method=method, url=url, auth=auth, json=body)
response.raise_for_status()
return response
except RetryError as e:
pass
And here is implementation 2:
request_semaphore = asyncio.Semaphore(5)
#retry(stop=stop_after_attempt(3), wait=wait_fixed(1))
async def _send_single_async_request(self, client: AsyncClient, method, auth, url, body):
async with request_semaphore:
response = await client.request(method=method, url=url, auth=auth, json=body)
response.raise_for_status()
return response
async def _send_async_request(self, client: AsyncClient, method, auth, url, body):
try:
await self._send_single_async_request(client, method, auth, request)
except RetryError as e:
pass
I'm testing it against a stable REST API. Here are the bench marks:
100 successful POST requests:
Implementation 1: 0:59 mins
Implementation 2: 0:57 mins
100 failed POST requests:
Implementation 1: 3:26 mins
Implementation 2: 2:09 mins
These results are consistent. Can anyone help me understand why my first implementation is slower than my second?
edit: FYI, here's how i'm calling the above functions (the above funcs actually receive a request tuple with url and body, edited it for clarity)
async def _prepare_async_requests(method, auth, requests):
async with AsyncClient() as client:
task_list = [self._send_async_request(client, method, auth, request) for request in requests]
return [await task for task in asyncio.as_completed(task_list)]
def send_async_requests(auth, method, requests):
loop = asyncio.get_event_loop()
responses = loop.run_until_complete(self._prepare_async_requests(method, auth, requests))
return responses

Dart Http Package request timeout

While using http package in my flutter app, I encountered a slight issue. I am testing on localhost and when I post something in database using http.post, it doesn't return response for default time (i.e. 60s I think) when server is not running. And when I start the apache and mysql services within the timeout, it posts the data in the server . Is there any way to reduce the timeout of the http requests in dart http package? Or is there any alternative solution?
This is for http package
final response = await http.post(Url).timeout(Duration(seconds: 5));
And this is for Dio package (recommend to test this package)
BaseOptions options = new BaseOptions(
baseUrl: baseUrl,
connectTimeout: 10000, //10 seconds
receiveTimeout: 10000,
);
Dio dio = new Dio(options);
Response<dynamic> response = await dio.post(url, data: mapData);
You have two options availabe.
Reduce the timeout on the HttpClient
final client = new HttpClient();
client.connectionTimeout = const Duration(seconds: 10);
This will apply to all request made by the same client. If the request exceeds this timeout, a SocketException is thrown.
Set a per request timeout
You can set a timeout on any Future using the Future.timeout method.
try {
..
final request = await client.get(...);
final response = await request.close().timeout(const Duration(seconds: 10));
// more code
} on TimeoutException catch (e) {
// handle timeout
}

Trying to respond to Slack depending on response other API

I'm have difficulties of responding to Slack using Node Red. The response should wait on the response of another REST call.
I guess I need something with async await? But I can't figure out how to create the flow.
This is how the process should work
sent an interactive message to Slack
User clicks on a button (decline or Approve)
message is sent to Slack that request is being processed in other system
the request is transformed and posted to an other API /other system
When the response is received, then it should send the response back to Slack.
Can someone help me on this? (I'm trying to learn Node Red)
The nodejs that does exactly what I'm intent to do:
app.post('/review', urlencodedParser, async (req,res) => {
try{res.send(`Contract is currently being processed`)
var request = JSON.parse(req.body.payload);
var slackmessage = await SlackContractReviewContent(request);
var response = await sendContractReview(slackmessage.entity, slackmessage.status);
var Slackresponse = await sendSlackDelayedMessage(slackmessage,response);
console.log(Slackresponse);
res.end()
} catch (err) {
console.log(`Could not be processed!`);
res.end();
};

Meteor async function for validateLoginAttempt

I wan't to use googles captcha as an security messure in my app.
The process:
Request public response from google on client (Success)
Save public response to an collection (Success)
On server validateLoginAttempt should send my secret and client aclaimed public keys for validation (Success)
If and only if 3 successfull return true otherwise false. (Fails)
The problem I am facing is in 4
I need to execute validateLoginAttempt as async, As soon as I write:
Accounts.validateLoginAttempt(async() => {
await myCallToRemoteValidation
})
However
Accounts.validateLoginAttempt(() => {...})
does process the callback, but too late since node will have already progressed. I already use async await in other parts of the code, and it works but not with accounts. How can I solve this?
Reference: http://docs.meteor.com/api/accounts-multi.html#AccountsServer-validateLoginAttempt

Resources