By using AWS re:Post, you agree to the AWS re:Post Terms of Use

How to Implement timers in AWS? and perform some action when AWS lex session timed out.

1

I'm working on an application that handles customer interactions coming from a chat widget using AWS Connect. Here’s the flow:

  1. A customer sends a message via chat widget, which is received by our app.
  2. The message is initially routed to an Amazon Lex bot using recognize text api .
  3. If the bot cannot handle the customer’s query, the conversation is routed to a human agent using Amazon Connect APIs.
  4. The customer and agent then interact until the chat is ended by the agent.

Currently, we use Redis to manage session states:

  • When a first message lands on my app, a session is created in Redis and a conversation got created in my database.
  • When the agent ends the chat i received the (web-socket) chat-end event on my app and then i remove the session from Redis, and change the conversation-state in our database from active to ended.

Problem: However, there’s an issue when the customer’s query is handled entirely by the Lex bot or if the customer leaves the conversation before it’s routed to an agent. In such cases:

  • The session remains in Redis even though the customer is no longer interacting.
  • The conversationState in our database stays as active, which is not accurate if the customer has left or the query is resolved at the bot stage.

Goal: I want to implement a stateless timer-based approach to automatically:

  1. Remove the session from Redis.
  2. Update the conversationState in the database to ended when there has been no message exchange between the customer and the bot for a specific period (e.g., 5 minutes).

Questions:

  • What will be the best approach to handle this situation as i don't get any event when the bot session timed out?
  • What would be the best approach to implement a stateless timer to handle this in a scalable way?
  • Are there specific AWS services or patterns that would be ideal for managing such timer-based actions?
  • How can I ensure that this process is stateless and scalable, considering we are using Redis and handling potentially many concurrent conversations?

Any advice or examples of similar implementations would be greatly appreciated!

1 Answer
0

Hey @Subhan,

You are not alone in wanting to solve this problem with something out of the box.

Option 1: EventBridge

The most immediate solution would be to see if there is any EventBridge event you could subscribe to, to clean up your Lex session automatically. This may be a tad difficult though. There may not be an associated EventBridge event, and if there is, it may not contain the necessary details to properly clean up your Lex session, without forcing you to maintain an external database for your clean up Lambda Function to reference.

Option 2: (most-probably) Manual Timer

Code-wise, my team needed similar functionality. We wanted to know if a Lambda Function was long running, though this technology can be applied to any sort of place code can be ran. It's implemented in Python, but the idea is the same across languages.

Here's the code (a tad formatted for your sake) that could get the job done, to do something when a time limit has been reached; in your case, instead of throwing an error, you could clean up your Lex session:

from signal import signal, SIGALRM, alarm
from functools import wraps
from datetime import datetime
from pytz import timezone

DEFAULT_LIMIT_IN_MINUTES = 14
DEFAULT_LIMIT_IN_SECONDS = DEFAULT_LIMIT_IN_MINUTES * 60


def now(code='<time zone code here>') -> datetime:
    return datetime.now(timezone(code))


def time_limit(start_time, limit_in_seconds=DEFAULT_LIMIT_IN_SECONDS):
    """
    - Enforces a time limit, in seconds, to a function (default of {DEFAULT_LIMIT_IN_SECONDS} seconds).
    - If the time limit is hit, an `Exception` is thrown.
    - If the time limit isn't hit, the return value of the wrapped function is maintained.

    Example Usage:

    from datetime import datetime
    from pytz import timezone

    start_time = datetime.now(timezone('<time zone code here>'))
    limit_in_seconds = 30

    @time_limit(start_time, limit_in_seconds)
    def process(record):
        print(f'Processing {record}...')
        sleep(60)

    records = [
        'a', # Will be cut off at the limit, 30 seconds, with an exception.
        'b', # Will be cut off right away.
        'c' # Will be cut off right away.
    ]

    for record in records:
        process(record)

    :param start_time: The start time the limit should be compared against (e.g. Lambda Function start time to be shared across records of an invocation).
    :param limit_in_seconds: The limit in seconds when the function should be stopped (default of {DEFAULT_LIMIT_IN_SECONDS} seconds).
    :return: The return value of the wrapped function, if the time limit isn't hit.
    """
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            def throw():
                raise Exception(f'Exceeded time limit of {limit_in_seconds} seconds.')

            # When the time limit is hit, the signal calls this function.
            def handler(_, __):
                throw()

            # Skips the signaling if the time limit was already hit in a previous invocation.
            elapsed_seconds = int((now() - start_time).total_seconds())
            seconds_remaining = limit_in_seconds - elapsed_seconds
            if seconds_remaining <= 0:
                throw()

            signal(SIGALRM, handler)
            alarm(seconds_remaining)
            try:
                result = func(*args, **kwargs)
            finally:
                cancel = 0
                alarm(cancel)

            return result
        return wrapper
    return decorator

I hope this helps. Let me know if you need any more assistance, Chase

profile picture
answered 20 days ago

You are not logged in. Log in to post an answer.

A good answer clearly answers the question and provides constructive feedback and encourages professional growth in the question asker.

Guidelines for Answering Questions