Katkat

Design a Driver Payroll System

RipplingSoftware Engineer · Technical Screen

Last updated

Design an in-memory driver payroll system that records time-stamped earnings and supports efficient range queries for both individual drivers and company-wide aggregates.

Problem Statement

Design an in-memory payroll tracking system for a ride-sharing company. The system tracks earnings generated by drivers over time and allows querying individual driver earnings as well as total company payroll across specified time windows.

Implement the DriverPayroll class:

  • __init__(): Initializes the payroll system.
  • record_earning(driver_id: str, timestamp: int, amount: int) -> None: Records an earning amount (in cents) for driver_id at a given timestamp. Calls to record_earning are guaranteed to arrive in non-decreasing order of timestamp globally.
  • get_driver_earnings(driver_id: str, start_time: int, end_time: int) -> int: Returns the total earnings for driver_id between start_time and end_time, inclusive. If the driver has no earnings in this range or does not exist, return 0.
  • get_total_payroll(start_time: int, end_time: int) -> int: Returns the sum of earnings across all drivers between start_time and end_time, inclusive.

Example

payroll = DriverPayroll()
payroll.record_earning("driver_1", 10, 500)   # $5.00 for driver_1 at t=10
payroll.record_earning("driver_2", 15, 750)   # $7.50 for driver_2 at t=15
payroll.record_earning("driver_1", 20, 300)   # $3.00 for driver_1 at t=20
payroll.record_earning("driver_1", 25, 200)   # $2.00 for driver_1 at t=25

# Driver 1 earnings between t=10 and t=20 (500 + 300)
assert payroll.get_driver_earnings("driver_1", 10, 20) == 800

# Total payroll between t=12 and t=22 (750 + 300)
assert payroll.get_total_payroll(12, 22) == 1050

# Query with no earnings
assert payroll.get_driver_earnings("driver_2", 16, 30) == 0
assert payroll.get_driver_earnings("driver_3", 0, 100) == 0

Constraints

  • 1 <= len(driver_id) <= 32
  • 1 <= timestamp <= 10^9
  • 1 <= amount <= 10^6
  • 0 <= start_time <= end_time <= 10^9
  • At most 2 * 10^5 total calls across all methods.
  • Multiple earnings may be recorded at the exact same timestamp (for the same or different drivers).

Edge Cases to Consider

  • Querying a driver_id that was never recorded.
  • Time windows that cover no events or encompass all recorded events.
  • Multiple earnings occurring at the exact same start_time or end_time.

Hints

Hint 1

Because earnings arrive in chronological order, appending values to sorted lists allows you to use binary search for range queries.

Hint 2

Pairing timestamps with cumulative prefix sums allows you to compute range totals in O(log N) time rather than O(N).

Hint 3

Maintain both per-driver time series and a global time series to answer driver-specific and company-wide queries with the same prefix sum technique.

Follow-up questions

  • How would you handle out-of-order earnings (e.g., late tips or delayed network syncs)?
  • How would you efficiently implement a method `get_top_k_drivers(start_time: int, end_time: int, k: int) -> list[str]`?
  • If memory becomes a constraint, how would you evict or archive older data while still answering historical aggregate queries accurately?