Katkat

Implement Hierarchical Region Access Overrides

PinterestSoftware Engineer · Technical Screen

Last updated

Design and implement a regional access control system that handles time-based access overrides on a static tree hierarchy with out-of-order updates.

Problem Statement

Geographic regions are modeled as a static tree hierarchy where each region has at most one parent (e.g., "GLOBAL" -> "NA" -> "US" -> "CA"). Access permissions (True for granted, False for denied) set at any node inherit down to all of its descendants.

Over time, administrators can set access overrides at any node with an associated integer timestamp. An override takes effect at its given timestamp and persists indefinitely unless overridden. Because log events may be delivered out of order, override records can arrive in any order of their timestamps.

When checking whether access is granted to a region at a query timestamp t:

  1. Consider all overrides on the path from the root down to the target region with an override timestamp <= t.
  2. The override with the strictly largest timestamp determines the access permission.
  3. If two overrides on the path have the exact same timestamp, the one closest to the target region (deepest in the tree) takes precedence.
  4. If no overrides on the path occurred at or before t, access defaults to False.

Implement the RegionAccessManager class:

class RegionAccessManager:
    def __init__(self, hierarchy: dict[str, str | None]):
        """
        Initializes the hierarchy where hierarchy[region] = parent_region.
        Root regions have None as their parent.
        """
        pass

    def set_override(self, region: str, allowed: bool, timestamp: int) -> None:
        """
        Records an access override for the specified region starting at timestamp.
        """
        pass

    def has_access(self, region: str, timestamp: int) -> bool:
        """
        Returns whether access is permitted for region at query timestamp.
        """
        pass

Examples

Example 1

hierarchy = {
    "GLOBAL": None,
    "NA": "GLOBAL",
    "US": "NA",
    "CA": "US"
}
manager = RegionAccessManager(hierarchy)

manager.set_override("GLOBAL", True, 10)
manager.set_override("US", False, 20)

# Query at t=15 for "CA":
# Path: GLOBAL (t=10, True)
manager.has_access("CA", 15)  # Returns True

# Query at t=25 for "CA":
# Path: GLOBAL (t=10, True), US (t=20, False)
# Max timestamp is 20 (US), which is False
manager.has_access("CA", 25)  # Returns False

# Out-of-order override set at "NA" with timestamp 30
manager.set_override("NA", True, 30)

# Query at t=35 for "CA":
# Path: GLOBAL (10, True), NA (30, True), US (20, False)
# Max timestamp is 30 (NA), which is True
manager.has_access("CA", 35)  # Returns True

Constraints

  • Number of regions N <= 10,000
  • Maximum tree depth D <= 50
  • Total calls to set_override and has_access <= 50,000
  • 1 <= timestamp <= 10^9
  • All region names are non-empty strings of alphanumeric characters.
  • The hierarchy is guaranteed to form a valid forest/tree (no cycles).

Hints

Hint 1

Since the tree hierarchy is static and depth D is small (<= 50), traversing from a region up to the root on each query takes O(D) steps.

Hint 2

For each region, maintain its overrides in chronological order so you can use binary search to quickly find the latest override with timestamp <= t.

Hint 3

During a query, inspect every ancestor up to the root, find the latest effective override at each node using binary search, and select the one with the maximum timestamp (breaking ties by node depth).

Follow-up questions

  • How would you optimize `has_access` if the tree depth D could be as large as N (e.g., up to 100,000 nodes in a degenerate line)?
  • What concurrency or thread-safety primitives would you introduce if reads and writes happen simultaneously across multiple threads?
  • If overrides also had an expiration timestamp (e.g., an interval `[start_time, end_time)`), how would your data structure and query logic change?