Framework Thinking - Axon - User Location Key Value Store
Here is solutions for User Location Key Value Store.
1. Understand the problem
-
We need to design a key-value store where:
-
Key = userId
-
Value = user’s location
-
-
We must support operations like:
-
put(userId, location)
-
get(userId)
-
-
Possibly:
-
Updating locations
-
Querying users by location
-
Handling large scale
-
So the core problem is:
- Design an efficient way to store, retrieve, and update user locations using a key-value model.
2. Clarify constraints, asks 4 - 5 questions including edge cases.
- Scale
-
How many users?
-
How many location data points do we want to keep per user?
-
How frequently do these location objects get sent up?
-
Do we want to optimize for runtime? Or memory usage?
-
Can I assume timestamps are monotonically increasing for a device?
- Reads vs Writes?
- Is this read-heavy (tracking users) or write-heavy (live GPS updates)?
- Single machine or distributed?
- Do we need historical locations or only the latest?
- Do we need reverse lookup?
- Example: “Find all users in New York”
Edge cases to consider
-
Timestamp provided is earlier than the first location → we should return “” because no such timestamp exist
-
If the user_id doesn’t exist → return “” or throw an exception, doesn’t matter
-
If they did binary search, does their “mid” calculation support overflow? [minor] i.e., (hi+lo)//2 can cause overflow if hi+lo » max int
-
Boundary conditions with binary search
-
input such that timestamp > the max timestamp in the store
-
input such that timestamp matches the very first location
-
-
Which timestamp to use? The client timestamp or the server timestamp? Timestamps coming in from different timezones ?
-
What if the officer uses multiple devices that have different clocks/timestamps?
3. Explore examples.
set(1, t=5, lat=2, lon=5)
set(1, t=10, lat=3, lon=6)
set(2, t=4, lat=0, lon=0)
get(1, 8) → (3,6)
get(1, 4) → (2,5)
get(1, 11) → None
get(2, 1) → (0,0)
4. Brainstorm 2 - 3 solutions, naive solution first and optimize later. Explain the key idea of each solution.
-
Set: O(1).
-
Get: O(logN).
class Location:
def __init__(self, timestamp, lat, lon):
self.timestamp = timestamp
self.lat = lat
self.lon = lon
def __repr__(self):
return f"({self.lat}, {self.lon}) @ t={self.timestamp}"
class LocationStore:
def __init__(self):
self.store = {} # user_id -> List[Location]
def set(self, user_id, timestamp, lat, lon):
if user_id not in self.store:
self.store[user_id] = []
self.store[user_id].append(Location(timestamp, lat, lon))
# assumes timestamps are inserted in sorted order
def get(self, user_id, query_time):
if user_id not in self.store:
return None
locations = self.store[user_id]
n = len(locations)
# Binary Search
left, right = 0, n - 1
closest = None
min_diff = float("inf")
while left <= right:
mid = left + (right - left) // 2
loc = locations[mid]
diff = abs(loc.timestamp - query_time)
if diff < min_diff:
min_diff = diff
closest = loc
if loc.timestamp == query_time:
return loc
elif loc.timestamp < query_time:
left = mid + 1
else:
right = mid - 1
return closest
store = LocationStore()
store.set(1, 1, 1, 4)
store.set(1, 5, 2, 5)
store.set(1, 10, 3, 6)
print(store.get(1, 8)) # (3,6) at t=10
print(store.get(1, 4)) # (2,5) at t=5
print(store.get(1, 1)) # (1,4) at t=1
print(store.get(1, 0)) # (1,4) at t=1
print(store.get(1, 20)) # (3,6) at t=10
print(store.get(99, 5)) # None
# assert (store.get(1, 1).timestamp == 1)
# assert (store.get(1, 0).timestamp == 1)
# assert (store.get(1, 3).timestamp == 4)
# assert (store.get(1, 4).timestamp == 4)
# assert (store.get(1, 5).timestamp == 4)
# assert (store.get(1, 6).timestamp == 4)
# assert (store.get(1, 8).timestamp == 10)
# assert (store.get(1, 15).timestamp == 10)