Implement Event Filtering and Queue Routing
AmazonSoftware Engineer · Technical ScreenLast updated
Implement an in-memory event routing engine that matches nested JSON events against subscriber filter policies and routes them in subscription order.
Problem Statement
Design and implement an in-memory event routing system, EventRouter, modeled after notification systems like Amazon SNS. Subscribers register interest in events using a destination queue identifier and a filter policy. When an event is published, the router evaluates the event against all registered policies and returns a list of target queue_ids in the order they were subscribed.
Requirements
Implement the EventRouter class with the following methods:
-
subscribe(queue_id: str, filter_policy: dict) -> None:- Registers a queue with a given filter policy.
- If the
queue_idwas already registered, update its filter policy without changing its position in subscription order.
-
route(event: dict) -> list[str]:- Evaluates the
eventagainst all active subscriptions. - Returns the
queue_ids of all matching subscriptions, preserving original subscription order.
- Evaluates the
Filter Policy Specification
A filter policy is a dictionary where keys are field paths and values are conditions. For an event to match a policy, all field conditions in the policy must be satisfied (logical AND).
- Field Paths: Keys can use dot-notation to access nested JSON fields (e.g.,
"user.address.city"). If a field path does not exist in the event, the condition fails. - Conditions: A condition on a field can be one of:
- Exact Value List (logical OR): A list of primitive values (
str,int,float,bool). The field matches if its value equals any item in the list. - Numeric Comparison: A dict with one or more operators from
"$gt","$gte","$lt","$lte". The event value must be numeric (intorfloat) and satisfy all specified operators for that field. - Prefix Match: A dict
{"$prefix": "value"}. The event value must be a string starting with the prefix.
- Exact Value List (logical OR): A list of primitive values (
If there is a type mismatch during comparison (e.g., performing a numeric comparison on a string value), the condition evaluates to False.
Example
router = EventRouter()
# Queue 1: Matches orders in US or CA with total >= 100
router.subscribe("queue-1", {
"customer.country": ["US", "CA"],
"order.total": {"$gte": 100}
})
# Queue 2: Matches payment events starting with 'txn_'
router.subscribe("queue-2", {
"event_id": {"$prefix": "txn_"}
})
event_1 = {
"event_id": "txn_98721",
"customer": {"country": "US"},
"order": {"total": 150.50}
}
print(router.route(event_1)) # Output: ["queue-1", "queue-2"]
event_2 = {
"event_id": "audit_001",
"customer": {"country": "UK"},
"order": {"total": 200}
}
print(router.route(event_2)) # Output: []
Constraints
- Number of subscriptions:
N <= 1,000 - Number of fields in an event:
<= 50 - Max nesting depth of an event:
<= 5 - String lengths:
<= 100characters - Types are standard JSON:
dict,list,str,int,float,bool,None
Hints
Hint 1
Break down the nested field lookup into a helper function that splits the dot-delimited path and navigates through nested dictionaries safely.
Hint 2
Handle type checking explicitly (e.g., ensuring a field is numeric before evaluating numeric operators, and checking that booleans are not treated as integers in Python).
Hint 3
Maintain insertion order of queue IDs using an ordered structure such as an `OrderedDict` or a combination of a list and a dictionary.
Follow-up questions
- How would you optimize `route()` if there are tens of thousands of subscribers, so you do not evaluate every filter sequentially?
- How would you handle array fields in events (e.g., checking if any element in `items[].category` matches a condition)?
- In a distributed system, how would you ensure filter updates and routing remain consistent without introducing high lock contention?
Solution walkthrough is for members
Get the full walkthrough with reference code and complexity analysis, plus Kitty Coach at 300 messages a day. Hints, the code runner and community answers stay free.