Notification Event Processing in the Serverless World

Nikola Mladenovic
commercetools tech
Published in
6 min readFeb 7, 2017

--

It’s no secret that the future will be serverless. Computing is becoming more granular. From monolithic applications, to microservices, serverless is the natural evolution. It is a good and effective way to build scalable and cost efficient components, helping you decouple the code. Those components essentially run in event triggered stateless containers, and therefore represent a good solution for the notification processing.

Handling events in a complex project can be a real challenge. In this blogpost, we are combining a BaaS (Backend-as-a-Service) with two different serverless platforms: AWS and Iron.io. In our example, we want to send a Push Notification when a reserved product is available for pickup in a store. The BaaS delivers event messages concerning the reservation, which are processed by an FaaS (Function-as-a-Service), and in our example passed onto another component.

Why Push is a Good Candidate for Serverless

For those of you who have dealt with direct integration with the Apple Push Notification service (APNs) and Google / Firebase Cloud Messaging (GCM / FCM) in the past, know it can be a bit messy to keep it as a part of your core platform’s codebase. You have to manually handle queueing, and processing events which can trigger a notification. Events usually carry additional information depending on their type. The service receiving them has to map that information into an appropriate platform specific notification payload. Finally, based on the user’s device type, the payload is delivered to the notification server. Definitely sounds like a service which should be separated from the complex system using it. And it is. All major serverless app platforms place notifications on top of their solutions list.

Choosing the Right Ecosystem

This sounds like a difficult question. But is it really? The answer mostly depends on whether you are building some mission critical component, and have to worry about reliability, where every uptime permille counts. It also depends on whether you have a specific language / framework preference, or are open to adjusting your tech stack to what the ecosystem supports. When it comes down to push notifications — they’re by definition not mission critical. We have decided to try out a couple of solutions which integrate well with commercetools, our core platform behind the mobile application in this example.

As a test example, we used the scenario where our mobile shop customer reserves certain product for the in-store pickup. For the sake of simplicity, we are expecting the user to get the confirmation notification right after placing a reservation.

The Amazon Way

We used two products from the suite of Amazon Web Services: Simple Notification Service along with AWS Lambda.

The first step is to create an SNS Topic, which will serve as a message queue for all events sent from the core platform. The best and easiest way to achieve that with commercetools is using subscriptions. Once we have the subscription for OrderCreated events in place, any time a reservation is made, a new message is delivered to the SNS Topic.

SNS provides an easy way to create a platform application, used to help you around delivering notifications to a specific platform. For an example, you can directly upload your certificates for APNS, a GCM API key, etc. No need to keep them in your project files, just use a simple GUI provided by AWS console.

AWS Console — Lambda configuration

The final step is to write the AWS Lambda function, which will be triggered by every message the Topic receives. You can currently choose among 5 runtimes, depending on the language and dependencies you plan to use. The code used in this example can be downloaded from GitHub. It consists of a Groovy script, and a couple of dependencies managed by Gradle. The task the Lambda function is performing here is constructing the APNS-specific payload from the SNS event message, which is essentially the message delivered from our core platform to the Topic. The second task is obtaining a token for the customer referred by the event message. Finally, the function uses an Apple iOS Prod application created as a part of SNS services, to send the payload to the Apple’s servers.

The Iron.io Way

Another suite of products, in this case from Iron.io, provides us with a serverless ecosystem. We used IronMQ along with IronWorker for the test. Our core platform also provides an option to subscribe and deliver messages to IronMQ.

IronWorker will be in charge of processing event messages, constructing payloads, obtaining customer’s tokens, but also used for sending out notifications directly to Apple’s servers. IronWorkers are built and distributed using Docker images. Once the Docker image of your worker is pushed to the Docker hub, you can register it with Iron. A slightly modified script and dependencies from the previous example are also available on GitHub.

Iron.io Dashboard — IronWorker

After the worker is registered with Iron, a unicast queue can be created in the Iron.io dashboard. The subscriber URL has to match the newly created worker. Now we can setup the subscription from our core platform, and have a message pushed to the IronMQ every time a customer places a reservation. The IronMQ subsequently triggers workers, passing the event message along the way.

Iron.io Dashboard — IronMQ

Performance Comparison

After trying out both solutions, we were able to notice certain startup time differences. Notifications implemented the Amazon way were received almost instantly, while the Iron.io way gave us a certain delay. A slightly outdated article from couple of years ago comparing Lambda and IronWorker services pointed out the startup time difference, but no numbers were given.

In order to quantify this difference, we tried to measure the time between the order creation (the moment when the commercetools platform returns a successful response for the create order request), and the moment when the device receives the notification.

The best result of the AWS way was slightly above 2 seconds, while the best time achieved with the Iron.io way took around 9 seconds.

It is important to note that the delay we get with the Iron.io way isn’t solely caused by the startup time difference. The execution times on the IronWorker were longer, and inconsistent — the variation between samples was a few times higher than the best time itself.

Conclusion

In many aspects both ecosystems we have tried are very similar. For those thinking about reliability, Iron.io claims to be pretty rock solid. AWS SNS also promotes redundancy and reliability. Depending on your specific use-case, you may find startup time differences more or less important.

If your language preferences do not align with runtimes available on AWS Lambda, you might opt for the IronWorker solution, providing much more freedom, due to their approach using the Docker images to package and execute tasks. As a Swift engineer, I am personally looking forward to trying out Swift code under a Docker container, and pushing it to IronWorker.

It is always important to avoid supplier lock-in. In this particular example, the JVM runtime is supported by both AWS Lambda and IronWorker, making a migration possible. An obvious difference between the two scripts is the entry point, i.e the way to consume the message sent from the queue. While SNS features come in handy to simplify the codebase, moving away from AWS would require additional effort to implement those functionalities. On the other hand, if you initially start with a language that is not properly supported by the AWS runtimes, migrating away from the Docker images supported by the IronWorker, to the Lambda, can be a challenge.

--

--