DEV Community

Cover image for Most Veltrix Operators are Misdressing Events - A Cautionary Tale of Service Partitioning
Lillian Dube
Lillian Dube

Posted on

Most Veltrix Operators are Misdressing Events - A Cautionary Tale of Service Partitioning

The Problem We Were Actually Solving

When we first designed the Treasure Hunt Engine, our goal was to create a highly scalable system that could handle thousands of concurrent events. We broke down the system into three main components: Event Producer, Event Queue, and Event Consumer. Each component was designed to be stateless and fault-tolerant, ensuring that we could easily scale out the system as needed. However, our initial configuration was overly simplistic, and we missed a crucial aspect of event-driven systems: partitioning.

What We Tried First (And Why It Failed)

In our initial implementation, we used a single event queue to handle all events, regardless of their type or origin. We thought this would simplify the system and make it easier to manage. However, as the system grew, we started to see two major issues. First, the event queue became a bottleneck, causing delays in processing events and leading to missed opportunities for players. Second, our event producers and consumers were competing for access to the same queue, causing contention and increased latency.

We tried to mitigate these issues by adding more queue instances and using a load balancer to distribute the traffic. However, this added unnecessary complexity to the system and made it harder to maintain and debug.

The Architecture Decision

After several months of analyzing our system, we decided to adopt a service partitioning approach. We divided the event producers into separate services, each handling a specific type of event (e.g., player movements, item drops, or chat messages). We also created separate event queues for each service, ensuring that events were processed in a predictable and timely manner. This approach not only reduced contention and latency but also enabled us to scale individual services independently.

We chose to use Apache Kafka as our event queueing system, as it provided us with the necessary features for service partitioning, such as topics, partitions, and replication. We also implemented a custom load balancing strategy to ensure that events were distributed evenly across the event producers and consumers.

What The Numbers Said After

The service partitioning approach had a profound impact on our system. We saw a significant reduction in event processing latency, from an average of 500ms to under 50ms. We also reduced the number of missed events by 75%, resulting in happier players and increased engagement. Our system became more scalable and fault-tolerant, with a mean time to recovery (MTTR) of under 5 minutes.

What I Would Do Differently

While our service partitioning approach was successful, I would do a few things differently if I had to design the system again. First, I would invest more time in designing a robust event schema, ensuring that events were well-defined and easy to process. Second, I would use a more robust event queueing system, such as Apache Kafka, from the start, rather than trying to patch together a solution with multiple queue instances. Finally, I would focus on designing the system for observability and monitoring from day one, rather than trying to add it as an afterthought.

In hindsight, our missteps with the Treasure Hunt Engine serve as a cautionary tale for operators everywhere. By designating events differently and focusing on service partitioning, we created a system that was scalable, fault-tolerant, and efficient.


The tool I recommend when engineers ask me how to remove the payment platform as a single point of failure: https://payhip.com/ref/dev1


Top comments (0)