03 - Timeout

BRANCH: You can start from the branch first-request.

git checkout first-request

The client of our API can specify a timeout. The endpoint should always end in that timeout. If the timeout is not specified we will wait for the first successful response.

Implementing the timeout functionality

Get the timeout parameter from the request.

  1. Import required packages.

     from flask import Flask, jsonify, redirect, url_for, request
     from http import HTTPStatus
    
  2. Get the timeout from request args.

     @app.get("/api/smart")
     def smart_api_requester():
         timeout = request.args["timeout"]
         ...
    

QUESTION: Do you spot any problems?

Click on me for the answer!
  • We are missing validation. Let’s add it.
      def milliseconds_to_seconds(value: int) -> float:
          return value / 1000
        
        
      def get_and_validate_timeout()  -> None | float:
          timeout = request.args.get("timeout")
          if timeout is None:
              return timeout
        
          try:
              converted_timeout = milliseconds_to_seconds(int(timeout))
          except ValueError:
              raise BadRequest("Timeout has to be integer value.")
        
          if converted_timeout <= 0:
              raise BadRequest("Timeout has to be positive non zero value.")
        
          return converted_timeout
        
        
       @app.get("/api/smart")
       def smart_api_requester():
           timeout_seconds = get_and_validate_timeout()
    
  • Test timeout arguments.
    • http://127.0.0.1:8081/api/smart?timeout=0
    • http://127.0.0.1:8081/api/smart?timeout=nonumber
    • http://127.0.0.1:8081/api/smart?timeout=10

    The first two requests should fail with the bad request status code. The last one should pass.


End after the timeout is reached.

QUESTION: How could we implement this? We need to wait for the response and also check if the timeout is not reached.

Click on me for the answer!

We can use asynchronous features of python. This allows you to write concurrent code.

QUESTION: What is the difference between parallelism and concurrency? Is it better to use asynchronous framework instead of multiprocessing or threading?

Click on me for the answer!

Parallelism consists of performing multiple operations at the same time.

Concurrency is a slightly broader term than parallelism. It suggests that multiple tasks have the ability to run in an overlapping manner. (There’s a saying that concurrency does not imply parallelism.)

Threads consume more memory because they need to have their own stack. We also need to secure the thread safety. The same thing goes with multiprocessing. Multiprocessing works well if we have multiple CPUs and our processes are independent - we don’t need to provide thread safety.

On the other hand, async is ideal if we have many I/O operations, e.g. waiting for server to respond, multiple writes into the file and so on.

Source: Async IO python


So, let’s create an async program.

  1. First we need to install flask with async, so our server can be run asynchronously.
    pip install flask[async]==2.2.2
    
  2. After that, we need to import required packages.
    import asyncio
    from aiohttp import ClientSession
    
  3. Create the async request.
    Success: TypeAlias = bool
          
          
    async def fetch() -> tuple[Success, dict]:
        async with ClientSession() as session:
            try:
                async with session.get(BLOOMREACH_SERVER) as response:
                    if response.status == HTTPStatus.OK:
                        try:
                            result_data = await response.json()
                        except ContentTypeError:
                            return False, {}
          
                        return True, result_data
                    else:
                        return False, {}
            except Exception:
                # Catch any session error (e.g. timeout)
                return False, {}
    
  4. Call async request from our route.
    @app.get("/api/smart")
    async def smart_api_requester():
        timeout_seconds = get_and_validate_timeout()
        try:
            success, result = await asyncio.wait_for(fetch(), timeout=timeout_seconds)
        except asyncio.TimeoutError:
            raise RequestTimeout()
          
        return jsonify(success=success, response=result)
    

results matching ""

    No results matching ""