Credits
You’re building a system to track balances for the OpenAI API credit purchasing system.
API users can purchase credit grants, specified by an ID, that are active at a timestamp and that let them use the API. Unused credit grant balances expire at a certain time.
However, there’s a kink in our system: requests can arrive out of order because our system is built on a really unstable network. For example, a request to subtract credits can arrive at the system before the request to add credits, even though the request to add credits has a lower timestamp.
Your task is to implement the Credits class, which should support the following operations:
- Granting credits, subtracting credits, and getting the credit balance for a user.
- The ability to handle requests that arrive out of order.
Some guidelines/hints:
- Do NOT worry about memory or performance concerns. Write simple, working code. No fancy data structures are needed here.
- All timestamps can be represented as ints for simplicity and are unique (no two actions will happen at the same timestamp).
- Subtract from grants expiring soonest first.
Skeleton
class Credits:
def create_grant(
self,
timestamp: int,
grant_id: str,
amount: int,
expiration_timestamp: int,
) -> None:
pass
def subtract(self, timestamp: int, amount: int) -> None:
pass
def get_balance(self, timestamp: int) -> int | None:
pass
Tests
Test case 1 — basic subtraction
def test_basic_subtraction():
credits = Credits()
credits.subtract(30, amount=1)
credits.create_grant(10, grant_id="a", amount=1, expiration_timestamp=100)
assert credits.get_balance(10) == 1
assert credits.get_balance(30) == 0
assert credits.get_balance(20) == 1
# Explanation:
# t=10: +1 (a)
# t=20: 1 (a)
# t=30: 0 (a -1)
Test case 2 — expiration
def test_expiration():
credits = Credits()
credits.subtract(30, amount=1)
credits.create_grant(10, grant_id="a", amount=2, expiration_timestamp=100)
assert credits.get_balance(10) == 2
assert credits.get_balance(20) == 2
assert credits.get_balance(30) == 1
assert credits.get_balance(100) == 0
# Explanation:
# t=10: +2 (a)
# t=20: 2 (a)
# t=30: 1 (a -1)
# t=100: 0 (the remainder of a expired)
Test case 3 — subtracting from soonest-expiring grants first
def test_expiring_grants_soonest():
credits = Credits()
credits.create_grant(10, grant_id="a", amount=3, expiration_timestamp=60)
assert credits.get_balance(10) == 3
credits.create_grant(20, grant_id="b", amount=2, expiration_timestamp=40)
credits.subtract(30, amount=1)
credits.subtract(50, amount=3)
assert credits.get_balance(10) == 3
assert credits.get_balance(20) == 5
assert credits.get_balance(30) == 4
assert credits.get_balance(40) == 3
assert credits.get_balance(50) == 0
# Explanation:
# t=10: 3 (a)
# t=20: 5 (a=3, b=2)
# t=30: 4 (subtract 1 from b since it expires first → b=1, a=3)
# t=40: 3 (b expired)
# t=50: 0 (subtract 3 from a)
Test case 4 — subtract from many grants
def test_many_grants():
credits = Credits()
credits.create_grant(10, grant_id="a", amount=3, expiration_timestamp=60)
credits.create_grant(20, grant_id="b", amount=2, expiration_timestamp=80)
credits.subtract(30, amount=4)
assert credits.get_balance(10) == 3
assert credits.get_balance(20) == 5
assert credits.get_balance(30) == 1
assert credits.get_balance(70) == 1
# Explanation:
# t=10: 3 (a)
# t=20: 5 (a=3, b=2)
# t=30: 1 (subtract 3 from a, 1 from b)
# t=70: 1 (a expired; b=1)
Test case 5 — not enough credit
def test_insufficient_credit():
credits = Credits()
credits.create_grant(10, grant_id="a", amount=3, expiration_timestamp=60)
credits.subtract(20, amount=4)
credits.create_grant(40, grant_id="b", amount=10, expiration_timestamp=60)
assert credits.get_balance(10) == 3
assert credits.get_balance(20) is None # instead of -1
assert credits.get_balance(50) is None
# Explanation:
# t=10: 3 (a)
# t=20: None (subtract 4 when only 3 available)
# t=50: None (already went negative at t=20; can't recover)
事件重放 最简单:把所有 create_grant/subtract 作为事件按时间排序,重放到查询 / 操作时刻 t。
活跃额度=create_time ≤ t < expire_time 的 grant 之和;扣减按 最早过期优先 逐个 grant 消耗。
若在某时刻扣减超出可用额度,该时刻起余额为 None。
不要求性能,确保正确性即可。
VOprep 团队长期陪同学员实战各类大厂 OA 与 VO,包括 OpenAI、Google、Amazon、Citadel、SIG 等,提供实时语音助攻、远程陪练与面试节奏提醒,帮助大家在关键时刻不卡壳。
如果你也在准备 Stripe 或类似工程向公司,可以了解一下我们的定制助攻方案——从编程面到系统设计,全程护航上岸。