Serverless Ping Pong

on
nov 15, 2018
in
AWS

Ping-pong is an interesting game. It is a game where two players hit a lightweight ball back and forth across a table using small rackets. The game takes place on a hard table divided by a net. When you look at the game as an observer you see some interesting things. The players act only when the ball is on their side of the table. Only then a player swings the racket and the ball starts to travel the other way. When you are working in IT, you start to recognize other things as well. There is an asynchronous process in the game. The players react on ball and act on it. Also, the ball has travels from left to right, and from right to left. Another interesting thing is that players only swing when the ball can be hit, but in between the players don’t do anything else. Lets try to model this process in a serverless way.

The ping pong model

To model the game Serverless, we can use AWS Lambda as act as players. For the ball, we can take a message. For the playing table we can take two message queue, one for the ‘left-to-right’ motion and one for the ‘right-to-left’ motion and AWS SNS is a perfect fit. For the rackets we can use the act of ‘publishing a message’. We also have to have a way to start the game, which will be by ‘serving the ball’. AWS CloudWatch events is perfect for this job. It will send a message to the lambda to ‘start serving’. Of course, you only serve one time, so we have to deal with that. To define the logic simple Python scripts suffice combined with the Python SDK for Python. The game will be deployed by means of Sceptre to AWS defining the desired state configuration of the serverless infrastructure using AWS CloudFormation templates. I think this can actually work!

The players

For the players we take two Lambdas that execute simple Python scripts. One Lambda is called ‘ping’ that starts the game, and the other one is called ‘pong’ that reacts by ‘hitting’ the ball back.

Ping Actor:

# remember the serve
started = False

def handler(event, ctx) -> None:
    global started
    if event.get('detail-type'): # cloudwatch event
        if not started:
            started = True
            # hit the ball
            publish(ctx.invoked_function_arn, 'ping-topic', 'ping')
    else: # other event
        # hit the ball
        publish(ctx.invoked_function_arn, 'ping-topic', 'ping')

Pong Actor:

def handler(event, ctx):
    # hit the ball
    publish(ctx.invoked_function_arn, 'pong-topic', 'pong')

The playing table

We can model the table by using two Amazon SNS queues that will receive the messages from one actor and route the stream of messages to the other actor. The queues must have permissions to invoke the lambdas.

  PingTopic:
    Type: AWS::SNS::Topic
    Properties:
      TopicName: ping-topic
      Subscription:
      - Endpoint: !GetAtt PongFunction.Arn
        Protocol: lambda

  PongTopic:
    Type: AWS::SNS::Topic
    Properties:
      TopicName: pong-topic
      Subscription:
      - Endpoint: !GetAtt PingFunction.Arn
        Protocol: lambda

  InvokePingPermission:
    Type: AWS::Lambda::Permission
    Properties:
      FunctionName: !GetAtt PingFunction.Arn
      Action: lambda:invokeFunction
      Principal: sns.amazonaws.com
      SourceArn: !Ref PongTopic

  InvokePongPermission:
    Type: AWS::Lambda::Permission
    Properties:
      FunctionName: !GetAtt PongFunction.Arn
      Action: lambda:invokeFunction
      Principal: sns.amazonaws.com
      SourceArn: !Ref PingTopic

Start serving

To actually start the game, a player must start serving. The Ping actor already has the logic setup for that. A CloudWatch event will trigger the Ping actor to start the game. CloudWatch must have permission to invoke the lambda.

  CloudWatchEventsRule:
    Type: AWS::Events::Rule
    Properties:
      ScheduleExpression: rate(1 minute)
      State: ENABLED
      Targets:
      - Arn: !GetAtt PingFunction.Arn
        Id: scheduled-event

  InvokeLambdaPermission:
    Type: AWS::Lambda::Permission
    Properties:
      FunctionName: !GetAtt PingFunction.Arn
      Action: lambda:invokeFunction
      Principal: events.amazonaws.com
      SourceArn: !GetAtt CloudWatchEventsRule.Arn

Example

The example project shows how to configure a project to create a serverless ping-pong game. The example can be deployed with make deploy and removed with make delete.

Conclusion

A game like ping-pong is asynchronous by nature. Players play the game by reacting on events that are happening. Upon an event, players only have to do simple things like hitting the ball back. Serverless architectures are a perfect fit for asynchronous processes. By using message queues, asynchronous message passing and serverless compute, real life processes can be modeled in an event-driven way. Event Driven Architectures (EDA), like the ping-pong game we just created make efficient use of data centre resources and are the are a core architectural pattern and essential for serverless cloud computing. Next time we’ll look at how we can apply what we have learned from the ping-pong to and abstract some integration patterns from it!

Share this article: Tweet this post / Post on LinkedIn