Elastic Beanstalk
is a great platform, it offers both a Web
tier and a Worker
tier. I recently wrote about Simple Routing one of my library that allows you to route a SQS
message to a specific endpoint on the Worker
.
While Beanstalk
works great once it’s deployed to AWS
there is no easy way to run it locally. As soon as you want to execute an end-to-end flow involving both the Web
and the Worker
you need to manually POST
requests to the Worker
using Postman which is cumbersome and error-prone.
As it core all the SQS daemon does is dequeue messages from a SQS
queue and POST
it to a specified endpoint. With this goal in mind I wrote Beanstalk Seeder.
I had the following objectives:
- Users should be able to get up and running quickly
- Meaningful logging
- Transform the
SQS
message attributes into HTTP headers (in order to support Simple Routing)
Get up and running quickly
You can get Beanstalk Seeder
from GitHub releases. Download the archive and extract it somewhere.
Beanstalk Seeder
’s configuration is detailed in the README
. All you need is a iAM
user, the Worker
URI
and the SQS
queue URI.
Meaningful logging
When running a third-party tool, it’s critical to get meaningful logging as the binary is a black-box for the end user. I use structured logging in order to make querying the log events a breeze. My logging framework of choice is Serilog.
The previous snippet highlights only a few of the Serilog
features.
The structure-capturing operator
MessageAttributeValue
and Message
are both defined in the awssdk.sqs
NuGet
package. I’m interested in logging only some of their properties, Serilog
has the ability to capture object via the structure-capturing operator.
Enrichment
Enrichment is the act of adding additional properties to events, other than the ones originating from the message template.
Serilog
supports ambient context. I’m also using the excellent Ben.Demystifier for getting nicer stack traces.
Sinks
By default, Serilog
does not log anywhere. In order to record events you’ll need to configure one or more Sink
s. In this case I’m writing to the console but they are many other Sink
s available.
Result
I hope the recorded events are descriptive enough so that an end user know what’s happening:
- First I display the settings used, this is important as they could come from the
appsettings.json
, environment variables or even the user secrets if the environment isDevelopment
. - Then using the
structure-capturing operator
I log the relevantSQS
message properties. - Instead of logging the complete HTTP request I log the content of the body and the relevant headers.
- When deleting the message, I log the
ReceiptHandle
, this is the value used to delete a message and the user can correlate it to what was displayed above. - Finally, rather than not displaying anything when there are no messages in the queue I inform the user that’s the case and how long I’ll wait before retrying.
Interestings bits
I’m using a CancellationTokenSource so that the user can stop the message pump at any time (relying on Console.CancelKeyPress).
The MessagePump class
is the only class
with some logic. I wrote some tests around the cancellation token, the transformation of SQS
message attributes into HTTP headers and the back-off when no messages are available in the queue.
Conclusion
I hope you’ll find Beanstalk Seeder as useful as I did, combined with Simple Routing it simplified and streamlined my Elastic Beanstalk
development.
I also wanted to point out that Beanstalk Seeder
is platform agnostic. It doesn’t matter if you’re developing using Node.js
, Go
or any other of the Elastic Beanstalk supported platforms, all you need to do is install the latest .NET Core runtime (available on Windows
, macOS
and Linux
).