PracHub
QuestionsCoachesLearningGuidesInterview Prep

Quick Overview

This question evaluates understanding of time-based resource accounting, design of data structures for expiring balances, atomic update semantics for concurrent operations, and algorithmic time/space complexity within the Coding & Algorithms domain.

  • Medium
  • OpenAI
  • Coding & Algorithms
  • Software Engineer

Implement an expiring GPU credits ledger

Company: OpenAI

Role: Software Engineer

Category: Coding & Algorithms

Difficulty: Medium

Interview Round: Technical Screen

Implement an expiring GPU credits ledger for multiple users with three operations: 1) add_credit(user_id, amount, expiry_time): add a lot of credits that expires at expiry_time. 2) subtract(user_id, amount, timestamp): atomically deduct amount at timestamp by consuming unexpired lots in order of earliest expiry first; lots with expiry_time <= timestamp are expired and cannot be used; if total unexpired balance < amount, the operation must fail and make no changes. 3) get_balance(user_id, timestamp): return the total unexpired balance at timestamp and a breakdown by remaining lots (amount, expiry_time). Assumptions: time is integer seconds; amounts are non-negative integers; partial consumption of a lot is allowed; exact expiry boundary uses the rule "valid iff timestamp < expiry_time". Tasks: implement the three functions; include tests for (a) single lot add and spend, (b) spending across multiple lots with different expiries, (c) attempts to spend after a lot expires, (d) insufficient-balance failure leaving state unchanged, (e) edge case at expiry boundary. Follow up: analyze time/space complexity; discuss how to optimize for many operations (e.g., data structures to achieve O(log n) per update) and how to persist/restore state.

Quick Answer: This question evaluates understanding of time-based resource accounting, design of data structures for expiring balances, atomic update semantics for concurrent operations, and algorithmic time/space complexity within the Coding & Algorithms domain.

Implement an **expiring GPU credits ledger** that processes a stream of operations for multiple users and reports the results of every query. ## What to implement Implement: ```python def solution(operations): ... ``` `operations` is a list of operation tuples, processed **in the given order**. Each credit a user adds creates a **separate lot** with its own expiry time. A lot is **valid (unexpired) at a timestamp `t` if and only if `t < expiry_time`** (so a lot with `expiry_time <= t` is expired at `t`). Return a list containing the result of every **non-`add`** operation, in the order the operations were processed. `add` operations produce no output. ## Operations Each tuple's first element is its type: - **`('add', user_id, amount, expiry_time)`** — Add a new lot of `amount` credits to `user_id` that expires at `expiry_time`. Produces no result. (An `add` with `amount <= 0` adds nothing.) - **`('subtract', user_id, amount, timestamp)`** — Atomically deduct `amount` credits from `user_id` using only lots that are unexpired at `timestamp`, spending from the lot with the **earliest `expiry_time` first**. **Partial consumption of a lot is allowed** (a lot may be left with a reduced amount). Returns **`True`** on success or **`False`** on failure: - If the total of `user_id`'s unexpired credits is less than `amount`, the operation **fails**: return `False` and leave the ledger **unchanged** (no lots are modified). - Otherwise it succeeds: deduct exactly `amount` earliest-expiry-first and return `True`. - A subtract of `amount == 0` always **succeeds** (returns `True`) and changes nothing, even for a user with no credits. - **`('balance', user_id, timestamp)`** — Report `user_id`'s credits that are unexpired at `timestamp`. Returns a tuple `(total, breakdown)` where: - `total` is the sum of all unexpired lot amounts. - `breakdown` is the list of remaining unexpired lots as `(amount, expiry_time)` pairs, **sorted by `expiry_time`** (ascending). A user with no unexpired credits has balance `(0, [])`. ## Notes - Expired lots never count toward a balance, can never be spent, and should be treated as gone once their `expiry_time` has been reached at the timestamp of a query. - Users are independent; an operation on one `user_id` never affects another. ## Constraints - `1 <= len(operations) <= 20000` - `0 <= amount <= 10^9` - `0 <= expiry_time, timestamp <= 10^9` - Operations are processed in the given order. - A lot is usable exactly when `timestamp < expiry_time`. - The `timestamp` values of the **non-`add` operations** (`subtract` and `balance`) are **non-decreasing** in processing order (time only moves forward across queries). `add` operations carry no timestamp and may specify any `expiry_time`. ## Follow-up Analyze the time and space complexity of your approach. Discuss how to optimize toward **O(log n)** updates and queries for heavy workloads, and describe how you would persist and restore the ledger state.

Constraints

  • 1 <= len(operations) <= 20000
  • 0 <= amount <= 10^9
  • 0 <= expiry_time, timestamp <= 10^9
  • Operations are processed in the given order
  • A lot is usable exactly when timestamp < expiry_time

Examples

Input: ([('add', 'u1', 50, 10), ('balance', 'u1', 5), ('subtract', 'u1', 20, 6), ('balance', 'u1', 6)],)

Expected Output: [(50, [(50, 10)]), True, (30, [(30, 10)])]

Explanation: At time 5, the lot is still valid, so the balance is 50. Subtracting 20 at time 6 succeeds, leaving 30 in the same lot.

Input: ([('add', 'alice', 10, 5), ('add', 'alice', 30, 20), ('subtract', 'alice', 10, 5), ('balance', 'alice', 5)],)

Expected Output: [True, (20, [(20, 20)])]

Explanation: A lot is valid only when `timestamp < expiry_time`, so the lot expiring at 5 is already invalid at time 5. The subtraction uses the 30-credit lot expiring at 20, leaving 20.

Input: ([('add', 'u', 5, 4), ('add', 'u', 7, 10), ('subtract', 'u', 10, 4), ('balance', 'u', 4)],)

Expected Output: [False, (7, [(7, 10)])]

Explanation: At time 4, the lot with expiry 4 is expired. Only 7 valid credits remain, so subtracting 10 fails and the ledger stays unchanged.

Input: ([('add', 'bob', 10, 20), ('add', 'alice', 8, 15), ('add', 'bob', 5, 8), ('balance', 'bob', 7), ('subtract', 'bob', 6, 7), ('balance', 'bob', 7), ('balance', 'alice', 7)],)

Expected Output: [(15, [(5, 8), (10, 20)]), True, (9, [(9, 20)]), (8, [(8, 15)])]

Explanation: Bob's balance at time 7 includes both lots, sorted by expiry. Subtracting 6 spends 5 from the lot expiring at 8 and 1 from the lot expiring at 20. Alice's credits are unaffected.

Input: ([('subtract', 'ghost', 0, 3), ('balance', 'ghost', 3), ('subtract', 'ghost', 1, 3)],)

Expected Output: [True, (0, []), False]

Explanation: Subtracting 0 always succeeds, even for a user with no credits. The user's balance is 0, and subtracting 1 then fails.

Input: ([('add', 'u', 5, 10), ('add', 'u', 7, 10), ('subtract', 'u', 6, 1), ('balance', 'u', 1)],)

Expected Output: [True, (6, [(6, 10)])]

Explanation: Both lots have the same expiry, so the earlier-added lot is spent first. The first 5-credit lot is fully consumed, and 1 credit is taken from the second lot, leaving 6.

Hints

  1. Store each user's credits as separate lots instead of merging everything into one number, because expiry times matter during subtraction.
  2. For atomic subtraction, first compute whether enough unexpired balance exists before changing any lot.
Last updated: Apr 30, 2026

Loading coding console...

PracHub

Master your tech interviews with 8,000+ real questions from top companies.

Product

  • Questions
  • Learning Tracks
  • Interview Guides
  • Resources
  • Premium
  • For Universities
  • Student Access

Browse

  • By Company
  • By Role
  • By Category
  • Topic Hubs
  • SQL Questions
  • AI Coding Questions
  • Compare Platforms
  • Discord Community

Support

  • support@prachub.com
  • (916) 541-4762

Legal

  • Privacy Policy
  • Terms of Service
  • About Us

© 2026 PracHub. All rights reserved.

Related Coding Questions

  • Infection Spread Simulation with Death Threshold - OpenAI (medium)
  • Spreading Contagion on a Grid - OpenAI (medium)
  • Streaming Entropy with Numerical Stability - OpenAI (hard)
  • Implement a Distributed Rate Limiter - OpenAI (medium)
  • Compute Plant Infection Time - OpenAI (medium)