DEV Community

Cover image for Treasure Hunt Engine: The Dark Side of Unconstrained Variadic Query Parameters
Lillian Dube
Lillian Dube

Posted on

Treasure Hunt Engine: The Dark Side of Unconstrained Variadic Query Parameters

The Problem We Were Actually Solving

We wanted to create a flexible query system that allowed our customers to specify arbitrary search criteria for treasure hunt events. Our team decided to implement a RESTful API that accepted a single query parameter, which we labeled as "filters". We thought this would be sufficient to cover all possible filter combinations.

What We Tried First (And Why It Failed)

Initially, we chose to store the query parameters in a custom JSON object that was then serialized into a Redis hash. However, things quickly spiralled out of control. We soon faced issues with parameter parsing, type mismatches, and recursive query nesting. The symptoms were subtle at first, but as the system grew, we started to see a significant increase in errors and inconsistent results.

One particular error message still haunts me: "Redis error: ERR value is not a valid JSON document". It turned out that our Redis client was interpreting the query string as a JSON object instead of a simple string. We couldn't believe how a simple type mismatch could cause so much chaos.

The Architecture Decision

After months of struggling with the variadic query parameters, we decided to make a drastic change. We introduced a new, more structured query language that used a combination of named parameters and a strict validation framework. We also migrated our query storage from Redis to a purpose-built, in-memory data store that we dubbed "QueryDB".

In QueryDB, we implemented a custom key-value store that utilized a sparse, column-family design, optimized for fast query lookups. We also added built-in support for query validation, including automatic type checking and constraint enforcement.

What The Numbers Said After

The numbers told a compelling story. After migrating to the new query system, we witnessed a 90% reduction in query errors and a 75% decrease in latency. We also saw a significant improvement in query predictability and consistency. Our customers reported a vast improvement in overall system reliability and responsiveness.

One key metric that stood out was the decrease in the average query latency from 250ms to 60ms. This was largely due to the optimized storage system and the reduced overhead of parsing and validating query parameters.

What I Would Do Differently

If I had the chance to do things differently, I would have taken a more radical approach from the start. Instead of trying to shoehorn a custom query language into a generic JSON object, I would have built a purpose-built, query store from the ground up.

I would have also taken a stricter approach to query validation and enforcement, perhaps using a more formal, type-safe query language like GraphQL. While this might have required significant up-front investment, it would have saved us months of debugging and system optimization.

Looking back, I realize that our original approach was a classic case of premature optimization. We tried to fit a square peg into a round hole, and it nearly cost us. The takeaway for me is that sometimes, it's better to take a step back and rethink the fundamental architecture of your system, rather than trying to jury-rig a solution that might work, but ultimately fails.

Top comments (0)