This article explores the performance challenges of request-to-stub matching in mock servers as the number of stubs grows, highlighting how a naive linear scan becomes a bottleneck. It reframes the problem as 'packet classification,' a well-solved challenge in networking, and introduces the Lucent bit-vector algorithm as an efficient solution. The article details Rift's implementation, showcasing significant performance improvements through multi-dimensional indexing and specialized data structures like Aho-Corasick automata for path matching.
Read original on Dev.to #architectureMock servers are essential tools in software development for testing and rapid prototyping, but their performance can degrade significantly over time. The core bottleneck lies in the request classification process: efficiently matching an incoming HTTP request to one of many configured stubs. While a simple linear scan of stubs is sufficient for small configurations, it becomes prohibitively slow as the number of stubs and the complexity of matching predicates (e.g., regex, JSONPath) increase. This linear search leads to O(N) performance, where N is the number of stubs, and can involve expensive operations like repeated body parsing.
| Engine | first match RPS | last match RPS | Performance Loss |
|---|
The article demonstrates this performance degradation with benchmarks for Mountebank and WireMock, showing a substantial drop in Requests Per Second (RPS) when the matching stub is found later in the configuration list. For instance, Mountebank's throughput drops by 84% between the first and last match in a 310-stub configuration, highlighting the impact of the linear scan.
The key insight presented is to reframe the mock server's request-to-stub matching problem as packet classification, a problem long solved in network routers and firewalls. In packet classification, a network device must match an incoming packet against hundreds or thousands of Access Control List (ACL) rules based on multi-dimensional criteria (source/destination IP, port, protocol) to determine an action. The shared characteristics are: multi-dimensional matching, a large and mostly static rule set, and a first-match-wins semantic.
Rift, the mock server discussed, employs the Lucent bit-vector algorithm (Lakshman and Stiliadis, 1998) for efficient request classification. The algorithm is based on these principles:
This approach ensures that the "first-match-wins" ordering is inherent in the bitset representation, simplifying implementation and preventing subtle precedence errors. Crucially, each dimension's index is designed to only *exclude* stubs it can *prove* do not match, meaning it can over-approximate (keep too many candidates) but never under-approximate (drop a valid match). Full predicate evaluation still occurs on the reduced candidate set, ensuring correctness.
| # | Dimension | Indexes | Structure |
|---|
Rift's index utilizes six dimensions across four request attributes. For example, path predicates are handled by three distinct dimensions: exact path matches use a hash map, path literals (startsWith, contains, endsWith) leverage an Aho-Corasick automaton for efficient multi-substring search, and path regexes use a multi-pattern automaton. Body matching uses structural hashing for deepEquals and a Quamina field automaton for specific field equality checks. This specialized indexing significantly reduces the search space, achieving near-constant time matching performance irrespective of stub count and position.