Building the TravelTimer.
When I first heard of the idea of letting players know when they arrived at a location automatically instead of letting the next command handle it, I just shrugged it off as something we wouldn't implement due to my refusal of adding timers to the bot.
Sure, we could use the regular timers that's in the language we use, but going with that approach would mean more memory usage and the need to keep track of all the timers within the bot itself, even throughout restarts and downtime, and frankly, that's just not something I want to do.
A while later, I was experimenting with PUB/SUB paradigms using Redis in order to have an external process, independent of the bot that would still be able to communicate with the bots processes in order to fetch information at set intervals, in this case, data about the shards for our shard dashboard.
After implementing this, I thought to myself that I could use the same system to execute events from a database, since I've done that before. As I was starting to experiment with it, I quickly discovered that it wouldn't be suitable for the bot or the way I had implemented it in the first place.
Discovering this, I started looking around for alternatives to Redis, since at this point I wanted to have an event loop in the bot, and our Redis configuration just didn't match the bill.
Well, what's on the bill?
On the bill, Peter, were a couple of features:
It had to be free
As I'm running this project mainly out of my own pockets, I can't afford software licenses for several thousands of dollars a year, so we'd need software we could deploy ourselves.
It had to be fast.
I'm talking Lightning McQueen fast, Sonic fast, so fast that the second you tell it to do something, it does it.
It had to be able to delay messages.
In order to make our work easier, and relieve the headaches and broken keyboards, we'd need something we could tell to delay sending a message for a specific amount of time.
It had to be easy to setup.
Minimal configuration out-of-the-box functionality was needed since I don't have the time or patience to sit and read through hundreds upon hundreds of pages of documentation, it had to be easy to setup.
It had to be easy to use.
As we don't want our codebase to consist of thousands of lines of code just to set a single thing up, we needed it to be easy to use, and provide libraries for our language of choice.
That sure sounds fancy and all, but did you find anything?
Oh I did Peter, haven't you been paying attention to the latest updates?
After searching around and testing around, I found RabbitMQ, a message broker that is just that - a message broker.
...
Where Redis is a in-memory data structure store, used as a database, cache and message broker, RabbitMQ is a pure message broker, meaning that's its only task.
With RabbitMQ, it filled all our criteria.
It was free.
We could set it up and have it fully functional in minutes.
It was fast.
In 2014, it hit over a million messages per second on Google Cloud Engine.
It had to be able to delay messages.
The authors of RabbitMQ provides a plugin for delaying messages that can be installed extremely easily.
It had to be easy to use.
RabbitMQ has several libraries available for our language of choice, and they're even used in the official documentation!
It had to be easy to setup.
It's a one-click configuration deal when you're in the dashboard of the server.
What's a message broker anyways?
Think of it as a postal service.
You're going to send a letter to your friend, you write the letter, put it in an envelope, write your friends address on the envelope, put a stamp on and put it in the mail, and a couple days later, it shows up in your friends mailbox, he opens it and reads the contents, a badly drawn comic of Garfield.
Simple, no?
Well, it's not that simple. See, your friend lives on the other end of the globe, and there's no direct route there.
While you can take a plane to him with no effort, your mail service can't, since they have to keep their costs down.
As such, they have their staff pick your letter up from the mailbox, take it back to their office, put it into the sorting facilities, it gets sorted to its next stop, where it goes through the same process of sorting and going further down the line until it reaches your friend, often through multiple carriers, boats and flights.
In RabbitMQ, it's the same, you send a message with a destination, and the server handles it based on your configuration of exchanges with ease and speed.
That's cool, but how did you implement it?
Well Peter, let me tell you.
As I wanted a system where I could add new events with ease, I started working on that, and I made it modular, meaning that I could just add the file and code, and the bot would load it automatically.
As there are two types of clients, a producer and a consumer, I needed to make classes for both of them.
Only problem with that is that they both would contain the exact same code, say for a few lines related to sending the messages and handling the incoming ones.
So, I made a class that both consumers and producers could use.
Great, but wasn't there an issue when you released it?
Oh, so you read the changelogs now?
You're right, there was issues with it. It didn't work as I thought.
While testing, I only test on a single shard, whereas the bot has 64 at the time of writing, and as such when it worked fine in testing, I thought that it would work fine in production.
...
But it didn't.
See, the way I had implemented the system is that I would be able to send a message from any shard destined to any other shard, and RabbitMQ would broadcast it to every consumer, that's not what happened.
What I didn't know is that the way RabbitMQ works is that it does one message at a time, and each consumer pulls it individually, and then deleting the message once processed.
As such, no message destined for a specific shard ever reached that shard.
How did you fix it?
In order to fix it, I had to do more testing.
Seeing as I didn't need the ability to message a specific shard, since the ping would be on the shard the command was executed, I could just set the consumer and producer to be specifically for that shard.
After reading through the docs and testing again, I figured out that this solution would work, and it did, meaning that the TravelTimer was ready for re-release.
So, what should you take away from this?
Read the docs.
They're there for a reason, and it's to document how the thing you're using works, and how it's supposed to be used.
Test. Test. Test.
Always test before deploying.
If it seems to work fine, something is wrong, you need to break it to see where it fails, get your mindset in as a user.
As always, thanks for reading.
And yes Peter, you can go send that bad Garfield drawing now.