Dayward AI

Interview Bank

328 questions total; 2 shown with current filters.

Tag
136 more tags
#api-design6#operations6#sse6#deployment5#message-bus5#tool-calling5#agent-loop4#behavioral4#error-handling4#evaluation4#framework-design4#mcp4#routing4#concurrency3#context-engineering3#interview-prep3#langgraph3#llm-basics3#model-routing3#orchestration3#prompt-injection3#protocol3#redis-streams3#scalability3#scheduling3#agent-design2#auth2#checkpointing2#communication2#cost-control2#database2#debugging2#interview-process2#latency2#long-term-memory2#memory2#ordering2#prompt-engineering2#react2#resume2#retrieval2#sharding2#state-machine2#state-management2#tool-design2#tool-permissions2#trade-offs2#ux2#agent-basics1#agent-quality1#async1#atomicity1#cancellation1#capacity-planning1#career1#chunking1#compression1#configuration1#consistent-hashing1#context1#context-compression1#context-management1#correctness1#customer-support1#data-modeling1#deliberate-practice1#docker1#documentation1#engineering-tradeoffs1#escalation1#event-driven1#fallback1#fan-out1#fencing-token1#forking1#framework-selection1#frontend1#global-market1#hybrid-search1#interrupt-merge1#isolation1#json-parsing1#jwt1#knowledge-organization1#lease1#least-privilege1#llm-as-judge1#loop-guard1#mobile1#multi-tenancy1#nodejs1#performance1#persistence1#pgvector1#portfolio1#prioritization1#proactive-messaging1#product-engineering1#project-storytelling1#prompt1#provider-abstraction1#quiet-hours1#ranking1#recall1#reconnect1#redis1#reflection1#replay1#reporting1#rerank1#retrieval-quality1#retry1#retry-semantics1#rrf1#sampling1#sandboxing1#schema-design1#secrets-management1#self-assessment1#self-introduction1#self-presentation1#service-architecture1#session-management1#split-brain1#star1#stateless1#storytelling1#structured-output1#system-prompt1#testing1#timezone1#tool-execution1#tools1#tracing1#transport1#vector-database1

From Frontend Engineer to Agent Engineer in 30 Days

D20 Scheduled Jobs and Proactive Outreach: Time Zones, Quiet Hours, Daily Caps, a Notification Provider Abstraction

  • How would you implement quiet hours and a per-user daily cap, and where in the pipeline should they be evaluated?quiet hours 和每日发送上限这两条规则你会怎么实现?它们应该放在链路的哪一步判断?
    Common in ChinaCommon overseasIntermediate#rate-limiting#quiet-hours#cost-control

    How to reason about it · think before answering

    1. There are two hinges here and most candidates only answer the first. One is how to evaluate the rules (a details question), the other is where in the pipeline (an architecture question) — the second is where the points are.
    2. Start with quiet hours. Once you fold times into minutes-from-midnight, almost everyone first writes start less-or-equal now and now less-than end. That is correct for a same-day window like a lunch break, but for 22:00 to 08:00 it is always false: start is 1320, end is 480, the condition never holds, and you push at 3am. The fix is to use and when start is before end, and or when start is after end. What makes this bug nasty is that it only misfires on the cross-midnight config, so a unit test written around 13:00 to 14:00 passes.
    3. Then what to do on a hit: defer, do not drop. The decision should not be the sender's mood — attach an expiry to each candidate and drop only when it expires before the window ends, deferring everything else to the window's end. A thirty-minute cancellation warning is worthless tomorrow; a billing summary is just as valid at 8am. Mention the thundering herd too: every deferred message resolves to the same due instant, so add jitter derived from a hash of the user id, never a random number, or you cannot reproduce incidents.
    4. Now two details on the daily cap. First, the day must be the user's local calendar day; keying on the UTC date charges an East-Asian user's 8am message to yesterday's budget. Second, increment first and check the returned value, then give the slot back if it exceeded — a read-then-write races, letting two candidates read the same count and both go out. Return the slot on a hard rejection from the channel as well.
    5. Finish with the architecture half, which is the valuable part: all three gates must run before the content is generated, not at the send step. Get the order wrong and the program still works and sends the same messages; the only difference is that you paid for a model call on every message you then threw away. At 1000 users, three candidates each per day, forty percent blocked and roughly $0.00075 per message, that is about $27 a month wasted — more than the normal conversational spend for the same cohort — and it is invisible in monitoring. Only putting candidate count next to sent count reveals the gap.
    6. Expect: what order do the three gates run in? Timezone, quiet hours, daily cap, with the cap last. A message deferred to tomorrow morning must not consume today's quota; reverse the order and users get rate-limited despite having received almost nothing.

    分析过程 · 先想清楚再作答

    1. 这题有两个题眼,很多人只答了前一个。第一个是「怎么判断」(细节题),第二个是「放在哪一步」(架构题),后者才是拿分点。
    2. 先讲 quiet hours 的判断。把时刻折成从午夜起算的分钟数之后,绝大多数人第一次都会写成 start 小于等于 now 且 now 小于 end。这对午休那种同日区间是对的,对 22:00 到 08:00 恒为 false——start 是 1320、end 是 480,条件永远不成立,于是半夜照发。正确写法是 start 小于 end 时用「且」,start 大于 end(跨午夜)时换成「或」。这个 bug 恶劣在只在跨午夜的配置上错,用 13:00 到 14:00 写的单元测试全绿。
    3. 接着是命中之后怎么办:推迟,不是丢弃。判据不该由发送方临时决定,而应该由消息自己带一个过期时刻——过期时刻早于窗口结束的丢弃,其余一律推迟到窗口结束。限时取消提醒过了今晚就没意义,账单提醒明早发一样有效。另外要提一句惊群:所有推迟的消息会算出同一个到期时刻,要加一个按用户标识哈希得出的抖动(不能用随机数,否则线上复现不了)。
    4. 再讲每日上限的两个细节。一是「一天」必须是**用户本地日历日**,写成 UTC 日的话东八区用户早上八点前发的会算进昨天的额度。二是必须先占坑再判断——原子自增拿返回值比上限,超了再把名额还回去;先查后写在并发下两条候选会同时读到同一个值然后一起发出去。渠道明确拒绝时也要把名额还回去。
    5. 最后是架构题那一半,也是最值钱的一段:三道闸必须在**生成内容之前**判断,不是在发送那一步。顺序错了程序照样跑通、发出的消息也一样,唯一区别是每条被拦下的消息你都已经付过一次模型调用的钱。按 1000 用户每天各 3 条候选、拦掉四成、单条约 0.00075 美元算,一个月白花约 27 美元,比这批用户的正常对话开销还高,而且监控上完全看不出来——只有把候选数和实际发送数并排摆出来才看得见差额。
    6. 可以预期的追问:三道闸内部谁先谁后?答时区、安静时段、每日上限,上限必须最后。因为被安静时段推迟的消息明早才发,不该占掉今天的名额;顺序反了用户会发现自己明明没收到几条却被限流了。

    Key points

    • Cross-midnight quiet hours need or when start is after end; the naive and version is always false for 22:00-08:00
    • On a hit, defer to the end of the window rather than drop; only drop when the message's own expiry precedes that
    • Deferral causes a thundering herd, so add jitter hashed from the user id, never a random value
    • The day in a daily cap must be the user's local calendar day, not the UTC date
    • Increment atomically then compare and release on overflow; read-then-write over-sends under concurrency
    • Run all three gates before generating content — otherwise every blocked message has already been paid for (about $27/month at the example scale); order them timezone, quiet hours, daily cap, with the cap last

    答题要点

    • 跨午夜的安静时段:start 小于 end 用「且」,start 大于 end 换成「或」,朴素写法对 22:00-08:00 恒为 false
    • 命中安静时段是推迟到窗口结束而不是丢弃;只有自带的过期时刻早于窗口结束才丢
    • 推迟会造成惊群,要加按用户标识哈希得出的抖动,不能用随机数
    • 每日上限的「天」必须是用户本地日历日,不是 UTC 日
    • 计数要先占坑再判断(原子自增后比上限,超了还回去),先查后写在并发下会超发
    • 三道闸必须在生成内容之前判断,装晚了每条被拦的消息都已经付过模型调用的钱(示例量级约 27 美元每月);闸内顺序是时区、安静时段、每日上限,上限最后

D29 Shoring Up Weak Points + a Coding Warm-Up: Rate Limiter, LRU, Concurrency Control, Streaming JSON Parsing

  • What are the common rate limiting algorithms, what are their trade-offs, and which one would you actually ship?限流器有哪几种常见算法?各自的优缺点是什么?如果只能落地一种,你选哪个?
    Common in ChinaCommon overseasBasic#rate-limiting#concurrency

    How to reason about it · think before answering

    1. This question tests whether you know rate limiting has several distinct semantics, not whether you can write a counter. Naming only one algorithm reads as never having run real traffic.
    2. Lay the four out by complexity and attach a weakness to each: fixed window is cheapest but has the boundary burst; sliding window log is exact but its memory grows with request count; sliding window counter is an approximation with constant memory; token bucket allows bursts with constant memory. That ordering is the skeleton of a good answer.
    3. Make the boundary burst concrete, because it is the standard follow-up: with a 100-per-minute limit, a client can spend 100 at 12:00:59 and another 100 the instant the counter resets at 12:01:00 — 200 requests inside two seconds, double the quota.
    4. Pick the token bucket and justify it by traffic shape: real traffic is bursty, and the bucket gives you two independent knobs — refill rate caps the long-run rate, capacity caps the burst. Implement it with lazy refill: compute the top-up from the elapsed time when a token is requested, never run a timer per user.
    5. Production angle: the in-memory version only holds for a single instance. Across gateway replicas, read-compute-write has a race and two replicas can both see 'one token left' and both allow. Fix it with a Redis Lua script so refill and deduction happen in one atomic step — Lua is not for speed here, it is for gluing three commands into one.
    6. Expect the follow-up: why not read the clock inside the script? Because that makes the script non-deterministic. Pass the timestamp in from the caller, and say the cost out loud — replica clocks now have to be roughly aligned.

    分析过程 · 先想清楚再作答

    1. 这题在考「你知不知道限流有多种语义」,而不是「你会不会写计数器」。只答出一种算法的人,会被默认没做过真正的流量治理。
    2. 先把四种按复杂度排开再逐个给弱点:固定窗口最省内存但有边界双倍;滑动窗口日志最精确但内存和请求数同阶;滑动窗口计数是近似解、内存回到常数;令牌桶允许突发、内存常数。这个排列顺序本身就是答案的骨架。
    3. 边界双倍要用具体数字讲,它是本题最常见的追问:限每分钟 100 次,用户在 12:00:59 打满 100 次,12:01:00 计数器清零又能打 100 次,跨边界的这 2 秒实际放行了 200 次。说不出这个例子,等于没答第一问。
    4. 结论选令牌桶,理由要落在业务形状上:真实流量本来就是突发的,令牌桶同时约束了长期速率(补充速度)和瞬时突发(桶容量),两个旋钮分别对应两个业务问题。实现上必须是惰性补充——取的时候按时间差现算,不要给每个用户起一个定时器,十万用户就是十万个定时器。
    5. 生产视角:单机内存版只在单实例下成立。多个网关实例共享配额时,「读余额 → 算补充 → 写回」三步之间一定有竞态,两个实例都读到「还剩 1 个」就会双双放行。修法是把三步塞进一段 Redis Lua 脚本,靠单线程执行整段脚本拿到原子性——用 Lua 不是为了快,是为了把三条命令粘成一条。
    6. 可以预期的追问:脚本里为什么不直接取当前时间?因为那会让脚本变得不确定,时间戳应该由调用方传进来;代价是各实例的时钟要大致对齐,这个取舍要主动说出口。

    Key points

    • Four algorithms: fixed window (cheap, boundary burst), sliding window log (exact, memory grows with requests), sliding window counter (approximate, constant memory), token bucket (bursty, constant memory)
    • The fixed-window boundary burst lets twice the quota through in the two seconds around a window edge, which is enough to overload a database or model API
    • Ship the token bucket: refill rate bounds the long-run rate and capacity bounds the burst, two knobs for two real constraints
    • Use lazy refill — top up from elapsed time on access instead of running one timer per key
    • For the distributed version, put refill and deduction in one Redis Lua script; a GET followed by a SET always races. Pass the timestamp in to keep the script deterministic

    答题要点

    • 四种算法:固定窗口(省内存但边界双倍)、滑动窗口日志(精确但内存与请求数同阶)、滑动窗口计数(近似、常数内存)、令牌桶(允许突发、常数内存)
    • 固定窗口的边界双倍:跨窗口交界的 2 秒内可以放行两倍配额,下游是数据库或模型 API 时足以打穿
    • 落地选令牌桶:补充速度管长期速率、桶容量管瞬时突发,两个旋钮对应两个真实业务约束
    • 必须用惰性补充:取令牌时按时间差现算,不要为每个 key 起定时器
    • 分布式版把补充与扣减写进一段 Redis Lua 脚本,先 GET 再 SET 一定有竞态;时间戳由调用方传入以保持脚本确定性