Anshul Goyal
Anshul Goyal

Reputation: 139

Catching exceptions in aws lambda python functions

I am just getting into aws lambda functions and have written a function that fetches some data from a dynamodb table. This is the function:

import boto3
from boto3.dynamodb.conditions import Key, Attr
import botocore.exceptions
import logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)

dynamodb=boto3.resource('dynamodb')
appointmentsTable = dynamodb.Table('Appointments')

class NotFoundError(Exception):
    pass

def lambda_handler(event, context):
    try:
        logger.info(f'event: {event}')
        
        bookedAppointments = fetchAppointments(event)
        
        logger.info(f'Response: {bookedAppointments}')
        
        return sendResponse(True , 200 , 'Appointments found' , bookedAppointments)
    except NotFoundError:
        return sendResponse(True , 400, 'No booked appointments Found' , [])
    except Exception as error:
        return sendResponse(False , 500 , 'Error in fetch booked appointments' , str(error))
    
def sendResponse(success , statusCode , message , responseData):
    return {
        'success' : success,
        'statusCode' : statusCode,
        'message': message,
        'responseData' : responseData
    }

def fetchAppointments(event):
    
    consId = event.get('consId')
    
    try:
        bookedAppointments = appointmentsTable.query(
            IndexName = 'consId-index',
            KeyConditionExpression = Key('consId').eq(consId),
            FilterExpression = 'booked=:b',
            ExpressionAttributeValues = {
                ':b' : True
            }
        )
    except botocore.exceptions.ClientError as error:
        logger.exception(f'Error in fetchAppointments function: {error}')
        raise error
    
    if bookedAppointments.get('Items') == []:
        raise NotFoundError
    
    sortedResult = sortResult(bookedAppointments.get('Items'))
    
    return sortedResult
    
def sortResult(listTobeSorted):
    return sorted(listTobeSorted , key=lambda k: (k['appointmentDate'] , k['appointmentSlot']))

I know wrapping all of your code logic in a try-catch block is bad practise , so is there a better way to handle any exceptions that may occur in the fetchAppointments function?

Upvotes: 0

Views: 4462

Answers (2)

samtoddler
samtoddler

Reputation: 9625

Code quality is certainly a good thing we don't have to start from scratch IMO, In my experience what I learned is

it should work first and then it should look beautiful

But in Cloud, there is more to it, it should be optimized in terms of cost as well.

So there you already started on the right path already by handling the exception

If you don't handle the exception and let the function fail, by default lambda would try to execute it 3 times

So you would be charged for those executions. You can customize this behaviour

As far as handling the exception goes @Jens explained it real good.

Upvotes: 1

Jens
Jens

Reputation: 21500

There are different ways to solve issues like this, but the underlying truth is: it always depends on what you want to do. So the following advice might be ok for this specific case, but might not be ok for other uses cases. So take the following with a grain of salt.

That said, the first thing to analyse is the different outputs of your fetchAppointments() function:

  1. One or more appointments found
  2. No appointment found
  3. Some application error/exception

Coming to your question: should you use exception handling for this? Yes, but I would not use it as much as you did.

I would only use it for the "proper" exception in this case and the rest should be covered by "normal" application logic.

Consider the following simplified version of your handler:

def lambda_handler(event, context):
    try:
        appointments = fetchAppointments(event)
    except Exception as error:
        return sendResponse(False, 500, 'Error in fetch booked appointments', str(error))

    if not appointments:
        return sendResponse(True, 400, 'No booked appointments Found', [])
        
    return sendResponse(True, 200, 'Appointments found', appointments)

As you can see, only the "real" application exception is handled with exception handling and the rest just uses "normal" logic to figure out, if there are appointments or not.

Upvotes: 3

Related Questions