Tutor Billing Ledger With Idempotent Invoice Cutoffs
RipplingSoftware Engineer · Technical ScreenLast updated
Implement an in-memory ledger for hourly tutors that logs sessions, reports total and outstanding amounts in O(1), and invoices work up to a cutoff without ever billing the same minute twice. Tests stateful design, exact arithmetic, and handling of partial and late-logged work.
An online tutoring marketplace pays tutors by the hour for time spent in live sessions. Build an in-memory TutorLedger with:
class TutorLedger:
def add_tutor(self, tutor_id: str, cents_per_hour: int) -> None: ...
def log_session(self, tutor_id: str, start: int, end: int) -> None: ...
def invoice_through(self, t: int): ... # returns amount newly invoiced
def total_billed(self): ... # all logged work, invoiced or not
def outstanding(self): ... # logged work not yet invoiced
- Times are integer seconds. A session's value is
rate * (end - start) / 3600. invoice_through(t)settles all logged work that happened at or before timet. If a session straddlest, the portion beforetis settled now and the rest later.- Calling
invoice_throughagain with the same or an earliertmust never settle anything twice. - A session may be logged after an invoice whose cutoff is later than the session's start ("late entry"). That work must not be lost: it is settled by the next
invoice_throughcall, whatever its argument. - The two getters are called far more often than anything else and must be O(1).
- Amounts must be exact; round to whole cents only for display.
Example
add_tutor("ana", 4500) # $45.00/h
add_tutor("ben", 3000) # $30.00/h
log_session("ana", 0, 3600) # $45.00
log_session("ben", 1800, 9000) # $60.00
total_billed(), outstanding() -> $105.00, $105.00
invoice_through(5400) -> $75.00 (ana 1h + ben 1h)
outstanding() -> $30.00
invoice_through(5400) -> $0.00
invoice_through(3000) -> $0.00
log_session("ana", 3600, 4000) # late entry, $5.00
total_billed(), outstanding() -> $110.00, $35.00
invoice_through(100) -> $5.00
Constraints
- Up to 10^5 tutors and 10^6 sessions. Single process, single thread.
0 <= start <= end, rates are non-negative integers in cents per hour.- Unknown tutor IDs and
end < startshould raise.
Hints
Hint 1
Keep running totals for 'everything logged' and 'everything invoiced'; the getters are then a subtraction.
Hint 2
The effective cutoff is the maximum cutoff ever seen, not the latest argument. Store it as a watermark.
Hint 3
For each session, remember how far into it you have already paid. Fully paid sessions can be dropped from the working set.
Hint 4
rate_cents * seconds is an exact integer. Divide by 3600 only when reporting.
Follow-up questions
- How would you support per-tutor outstanding balances and per-tutor invoices efficiently?
- Rates change over time with effective dates. How does the data model change?
- Make it safe for concurrent `log_session` and `invoice_through` calls from multiple threads.
- If this were backed by a database, how would you make `invoice_through` idempotent across process crashes?
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.