ML Feature Pipelines and Feature Stores Questions
Data infrastructure for machine learning: feature pipelines, feature stores, online/offline consistency, training-serving skew, and data preparation for models. Covers building reliable feature platforms and preventing leakage in the data path feeding models. The data-engineering-for-ML topic.
A model has started showing more false positives, and you suspect a mismatch between offline feature computation and online serving retrieval. Describe a plan to detect, reproduce, and fix issues caused by inconsistent feature computation (for example, a stale cache, missing keys, or a serialization difference), including the instrumentation and tests you would add to prevent recurrence.
Sample Answer
Direct answer: Detecting and reproducing a training-serving inconsistency starts by comparing the exact feature values the model saw at training time against what the online path currently returns for the same entities, narrowing down whether the divergence is a stale cache, a missing key, or a serialization mismatch, and closing the loop with instrumentation that would catch this class of bug automatically going forward.
Structured elaboration:
- Detect. Set up a systematic comparison: for a sample of entities, pull their feature values from the offline (training-time) computation and from the online serving path for the same point in time, and diff them; a consistently non-zero diff rate (not just occasional noise) confirms a real inconsistency rather than expected minor timing differences.
- Reproduce. Narrow to specific entities showing the largest divergence and trace each one's value through the pipeline: what did the offline computation produce, what does the online store currently hold, and when was the online value last updated, which starts distinguishing the candidate causes.
- Stale cache. If the online value's last-updated timestamp is old relative to when it should have refreshed, the online materialization pipeline itself may be lagging or failing silently for a subset of entities (worth checking if it correlates with a specific partition or shard); the fix here is addressing the materialization lag/failure, not the feature logic itself.
- Missing keys. If some entities have no online value at all (falling back to a default or null that the offline path would not have produced), trace whether those entities are newly created (a cold-start gap between when an entity is created and when it first gets materialized online) or whether a upstream join or filter is silently excluding them from the online materialization pipeline that the offline pipeline does not exclude them from.
- Serialization differences. If values exist on both sides but differ, check whether the offline and online computation paths are using genuinely the same transformation logic (a common root cause is the two paths having independently-implemented, subtly different logic, e.g. different rounding, different null-handling, or a unit mismatch) versus a serialization/deserialization bug (a type coercion, an encoding mismatch) that corrupts an otherwise-correct value on the way to or from the online store.
- Instrumentation and tests to prevent recurrence. Add an automated, continuously-running online/offline consistency check (not just a one-time investigation) that samples entities and alerts if the divergence rate exceeds a threshold; add a unit or integration test asserting the offline and online computation paths, when fed the same input, produce identical output, so a future code change that lets the two paths drift apart is caught in CI rather than in production months later.
Worked example: The investigation finds that entities with a specific upstream data source (say, a newer mobile client version) show the highest divergence; tracing one such entity reveals the online path's transformation logic was updated recently to handle a new field from that client version, but the corresponding offline (batch) transformation logic was never updated to match, meaning the two paths have silently diverged for exactly the population using the new client, which explains both the false-positive symptom (features computed differently for a subset of the population) and why it was not caught immediately (it only affected a growing but still partial slice of traffic).
Trade-offs & pitfalls: A common trap is fixing the specific instance of divergence found (patching the online or offline logic to match) without addressing why the two paths were able to drift apart in the first place; the durable fix is usually architectural, such as sharing the actual transformation code between the offline and online paths (a single source of truth for the logic, executed in both a batch and a streaming/serving context) rather than maintaining two independently-written implementations that have to be manually kept in sync, which is the root cause this class of bug keeps recurring from in practice.
Explain the trade-offs between batch and streaming ingestion for computing ML features. Cover latency, throughput, cost, operational complexity, ordering and completeness guarantees, and failure-recovery implications, and give concrete examples of when you would choose each (for example, nightly aggregates for reporting versus near-real-time features for fraud detection).
Sample Answer
Direct answer: Batch ingestion computes features on a schedule (hourly, daily) over accumulated data, favoring throughput, cost, and simplicity; streaming ingestion computes features continuously as events arrive, favoring freshness at the cost of higher operational complexity. Choose batch when the use case can tolerate the latency and streaming when it cannot.
Structured elaboration:
| Dimension | Batch | Streaming |
|---|---|---|
| Latency | Minutes to hours (bounded by schedule) | Sub-second to seconds |
| Throughput/cost efficiency | Higher (amortizes overhead across large chunks) | Lower per-event efficiency, but avoids storing raw data twice |
| Operational complexity | Lower (retry a failed batch job, simple to reason about) | Higher (stateful processing, watermarking, exactly-once concerns) |
| Ordering/completeness | Straightforward (process a complete, bounded dataset) | Requires explicit handling of late/out-of-order data |
| Failure recovery | Rerun the batch job over the same time range | Requires checkpointing and careful replay to avoid gaps or duplicates |
Worked example: A nightly ETL job computing "total purchases in the last 30 days" for a reporting dashboard is a natural batch fit: the dashboard is checked once a day at most, so computing the feature every 24 hours wastes no value and is far simpler and cheaper to operate than a continuously-updating streaming job. In contrast, a fraud-detection model scoring transactions in real time needs a "transactions in the last 10 minutes" feature that is fresh to the second; computing that with an hourly batch job would make the feature up to an hour stale, defeating its purpose, so a streaming pipeline is required despite the added operational cost.
Trade-offs & pitfalls: Teams sometimes default to streaming because it feels more modern, even when the use case's actual latency requirement (checked once a day, or once an hour) does not need it; this trades real operational complexity for freshness nobody uses. Conversely, defaulting to batch for a genuinely latency-sensitive use case (deciding after the fact that "we'll just run it every 15 minutes") often turns into a worse version of streaming, since a frequent-batch job re-scans overlapping data repeatedly and still cannot match true event-driven freshness; if 15-minute batch turns out to be insufficient and the next request is "make it 1 minute," that is usually the signal the use case actually needed streaming from the start.
You need to communicate a data contract for real-time feature serving to the engineering team, given this sample feature specification: feature_name | type | unit | cardinality | null_policy | freshness_seconds. Describe the required schema details, cardinality constraints, null handling, update frequency, and freshness SLA you would specify, and how you would manage a later schema change without breaking downstream models.
Sample Answer
Direct answer: Communicating a feature's data contract to engineering means being explicit and unambiguous about five things for every field: its type, its unit, its expected cardinality, how nulls are handled, and the freshness SLA (service-level agreement), since each of these is a place where an implicit assumption on either side commonly causes a production bug.
Structured elaboration:
- Type. State the exact type (int32 vs int64, float precision, string vs. categorical enum) since a mismatch here is a common source of silent truncation or serialization errors that only appear at scale.
- Unit. Explicitly state units for any numeric feature (seconds vs. milliseconds, dollars vs. cents, percent as 0-1 vs. 0-100), since unit mismatches between what a data scientist assumes and what engineering implements are a classic, easy-to-miss bug class that produces plausible-looking but wrong numbers.
- Cardinality constraints. For categorical features, state the expected set of values (an enum) or an approximate cardinality bound for high-cardinality fields, so engineering can make sane storage and indexing decisions and so an unexpected new value (a bug, or a legitimate new category that needs handling) can be detected rather than silently accepted.
- Null handling. State explicitly whether null is a valid value, what it means semantically if so (missing data vs. "not applicable" vs. "zero"), and what engineering should do if a null appears where the contract says it shouldn't (reject the record, substitute a default, alert).
- Update frequency and freshness SLA. State how often the feature is expected to update and the maximum acceptable staleness before it's considered a violation, which engineering needs to design the materialization cadence and monitoring thresholds around, and which also tells them what latency budget they have for the underlying computation.
Worked example: For the sample schema feature_name | type | unit | cardinality | null_policy | freshness_seconds, a row like avg_session_duration | float64 | seconds | continuous, 0-86400 range | null means no sessions in window, treat as 0 for scoring | 300 unambiguously tells engineering: store as a 64-bit float, values are in seconds and bounded to a sensible daily range (useful for a sanity-check validator), a null has a specific defined meaning with a specific handling rule (not an error condition), and the feature must be no more than 5 minutes stale, which directly informs how often the materialization job needs to run.
Managing a later schema change without breaking downstream models: treat any change to this contract the same way as the schema-evolution content elsewhere in this topic: introduce a new field or a new version rather than silently changing the existing field's meaning, communicate the change and a migration timeline to all known consumers, and only retire the old shape once consumers have confirmed migration, since a downstream model retrained on a silently-changed feature (same name, different meaning) can degrade in a way that is very hard to diagnose because nothing "broke" in an obviously visible way, the feature just quietly started meaning something different.
Trade-offs & pitfalls: Under-specifying the contract (skipping units or null semantics because "it's obvious") is the single most common way this communication fails in practice, since what is obvious to the person who defined the feature is frequently not obvious to the engineer implementing it, and the resulting bug (a unit mismatch, a mishandled null) is often subtle enough to pass initial testing and only surface once the feature is in wide production use. A contract that is written once and never revisited also drifts from reality as the feature's actual implementation evolves informally, so treating the contract as a living document that changes alongside the feature (not a one-time handoff artifact) keeps it trustworthy.
What is the difference between event time and processing time in a streaming system? Give a concrete example where using processing time would produce an incorrect feature value, and explain how you would correct for it.
Sample Answer
Direct answer: Event time is when something actually happened in the real world (recorded on the event itself); processing time is when the stream-processing system happens to observe and process that event. They can diverge because of network delays, retries, batching, or a client being offline and syncing later, and computing features on processing time instead of event time can silently corrupt time-based features.
Structured elaboration: Consider ad-impression logging: a mobile client logs an impression at 2:00pm but is offline and only successfully sends the event at 2:45pm because of a poor connection. If a feature like "impressions in the last hour" is computed using processing time, that impression gets counted as if it happened at 2:45pm, which both undercounts the 2:00-2:45 window it actually belongs to and overcounts the 2:45-3:45 window it gets misattributed to. Over many such delayed events, this systematically skews any time-windowed feature, and the skew is worse for exactly the segments most likely to have delayed or bursty connectivity (e.g. users on poor networks), which can introduce a subtle bias into the feature.
Correcting for it: use the event's own recorded timestamp (event time) as the basis for windowing, not the time the processing system received it. This requires a watermarking strategy, since event-time processing means the system has to decide when it is safe to close a window despite not knowing in advance whether more delayed events for that window will still arrive. Most modern stream processors (Flink, Spark Structured Streaming) support event-time windowing natively, so the fix is largely a matter of configuring the job to key off the event's timestamp field rather than the system's ingestion clock, plus setting an appropriate allowed-lateness/watermark policy.
Trade-offs & pitfalls: Event-time processing requires trusting the timestamp on the event itself, which assumes the producing client's clock is reasonably accurate; a client with a badly skewed clock can inject events with timestamps far in the future or past, which either get dropped by sanity-check bounds or corrupt a window if not filtered. Processing time is simpler and requires no watermark logic at all, which is why naive implementations default to it, but that simplicity is exactly why it silently produces incorrect features whenever delivery is not instantaneous and uniform, which in practice it never is at scale.
Design an online feature retrieval service to achieve median latency under 5ms at 100,000 requests per second. Cover data store choice, multi-region caching, cache warming, hotspot mitigation, consistency model, load balancing, handling high write throughput from upstream streaming jobs, and how you would measure and maintain tail latency.
Sample Answer
Direct answer: Hitting a 5ms median at 100k requests/sec means the design has to eliminate every unnecessary network hop and put the hot path entirely in memory close to the caller, with the data store itself sharded wide enough that no single shard is the bottleneck. The architecture layers, in order from the client: a local or co-located cache, a sharded low-latency store (Redis Cluster or a DynamoDB-style store), multi-region replicas, and an observability layer that separately tracks median and tail latency because at this scale they diverge.
Structured elaboration:
flowchart LR
Client["Inference client"] --> Region1["Regional read path"]
subgraph Region["Nearest region"]
Region1 --> LocalCache["Local cache"]
LocalCache -->|miss| Shard["Sharded online store (20 shards, 3x replicas)"]
end
Primary["Primary write path"] -->|async replication| Region
Primary -->|async replication| RegionB["Other regions"]
Shard -->|slow path fallback| Stale["Serve slightly stale cached value"]
- Data store and sharding. Partition the keyspace (typically by hashing entity ID) across enough shards that each shard handles a small fraction of 100k QPS (queries per second) with headroom, e.g. 20 shards at ~5k QPS each with 3x replication for read fan-out.
- Multi-region caching. Serve reads from the region closest to the caller. Each region holds a local read replica (or a fully local cache warmed from the primary) so a request never crosses a region boundary on the read path; cross-region replication happens asynchronously in the background.
- Cache warming. On a region failover or a cold cache, warming from a snapshot or from the primary region avoids a thundering herd of cache misses hitting the backing store simultaneously; a common pattern is to replay the last N minutes of writes into the new cache before routing traffic to it.
- Hotspot mitigation. A small number of keys will still dominate traffic (see the hot-key sub-area); mitigate with a local in-process cache for the hottest keys, request coalescing (collapse concurrent identical lookups into one backend call), and, if needed, replicating hot keys onto every shard rather than one.
- Consistency model. Accept eventual consistency between regions (bounded by replication lag, typically tens to low hundreds of milliseconds) in exchange for the latency win; document the staleness bound so consumers know what they are trading.
- Load balancing. Client-side or sidecar-based load balancing with health checks and fast failure detection, so a single slow shard does not drag down the aggregate tail.
- Measuring and maintaining tail latency. Track p50, p95, p99, and p99.9 separately, not just an average; instrument per-shard and per-region so you can find the shard causing the tail rather than only seeing the aggregate. Use load shedding or a fast timeout-plus-fallback (serve a slightly stale cached value) for the small percentage of requests that would otherwise blow the SLA (service-level agreement).
Worked example: At 100k QPS with a target median of 5ms, budget the latency: roughly 1-2ms for the network hop to a co-located cache, 0.5-1ms for the cache lookup itself, and the remainder as safety margin for queueing under load. If each shard node can sustain 8,000 ops/sec at that latency (a realistic number for an in-memory store with pipelining), you need at least ceil(100,000 / 8,000) = 13 shards for the primary write path, and you would typically round up to 16-20 to leave headroom for uneven key distribution and node maintenance.
Trade-offs & pitfalls: The most common mistake at this scale is designing for the median and being surprised by the tail: a 5ms median with a 200ms p99.9 still means thousands of slow requests per second at 100k QPS, which is often unacceptable for a synchronous inference path. A second common mistake is under-provisioning replica read fan-out, so a single popular shard saturates even though the aggregate cluster has spare capacity. Cross-region async replication means a model can read a feature that is a few hundred milliseconds stale right after a write; if the use case cannot tolerate that (for example, a fraud rule keyed on a just-written flag), the design needs a synchronous or quorum-read path for that specific feature instead of the default async replication.
Unlock Full Question Bank
Get access to all 17 ML Feature Pipelines and Feature Stores interview questions and detailed answers.
Sign in to ContinueJoin thousands of developers preparing for their dream job.