22 — Feature Stores & ML Infrastructure for RecSys
“Train-serve skew is a feature store problem masquerading as a model problem.”
Introduction
A feature store is the system that:
- Computes features once, consistently for training and serving.
- Serves them with point-in-time correctness to training jobs.
- Serves them with low latency to online inference.
- Tracks lineage, versioning, and ownership.
If features look different in training vs production, even the best model degrades. This post covers the feature store mental model, open-source and managed options (Feast, Tecton, Vertex AI, Databricks, SageMaker), online/offline parity, and patterns specific to recommenders.
1. The Two-Sided Problem
flowchart LR
A[Raw Events<br/>Kafka / DB] --> B[Feature Pipelines]
B --> C[Offline store<br/>Parquet / BigQuery / S3]
B --> D[Online store<br/>Redis / DynamoDB]
C --> E[Training jobs<br/>point-in-time joins]
D --> F[Online inference<br/>low-latency reads]
G[Feature Registry] --> B
G --> E
G --> F
The two reads (offline batch and online point lookup) must produce the same numbers for the same key and time.
2. Point-in-Time Correctness
Naive features leak: computing “user’s avg purchase price” using the full table includes the purchase you’re trying to predict.
Correct join:
For each training row with (user_id, timestamp T):
feature.value = MAX(feature.value WHERE feature.timestamp <= T)
This is the heart of every feature store API.
# Feast example
from feast import FeatureStore
store = FeatureStore(repo_path="./feature_repo")
training_df = store.get_historical_features(
entity_df=labels_df, # has user_id + event_timestamp
features=[
"user_features:n_views_7d",
"user_features:n_purch_30d",
"item_features:avg_rating_30d",
],
).to_df()
3. Online Reads
Same feature view, online:
online_features = store.get_online_features(
features=[
"user_features:n_views_7d",
"item_features:avg_rating_30d",
],
entity_rows=[{"user_id": 42, "item_id": 1337}],
).to_dict()
Same definition, same compute. No drift.
4. Feature Types in RecSys Context
| Type | Cadence | Store |
|---|---|---|
| Slow-changing (demographics) | Daily | Offline only, materialize daily |
| Aggregations (counts last 7d) | Hourly | Both — incremental |
| Real-time (last click) | Streaming | Online only with Kafka source |
| Item content (title, image) | On publish | Both — versioned |
| Embeddings | Daily or streaming | Both — separate vector store |
5. Architecture Patterns
Lambda
Batch + streaming pipelines feed the same store; reconcile at read time.
Kappa
Single streaming pipeline; batch is a replay.
Tier-Aware Materialization
- Hot tier (Redis): top-1% of features, sub-ms reads.
- Warm tier (DynamoDB): all online-required features.
- Cold tier (Parquet on S3): everything for training.
6. Open Source & Managed Options
| Tool | Notes |
|---|---|
| Feast | Open source, pluggable backends, lightweight |
| Tecton | Managed, deep RT features |
| Hopsworks | OSS + enterprise, strong on Spark + Flink |
| Databricks Feature Store | Tight Spark + ML integration |
| Vertex AI Feature Store | GCP-native |
| SageMaker Feature Store | AWS-native |
| Featureform | OSS, focus on definitions + governance |
| Chronon (Airbnb) | OSS, batch + streaming, point-in-time |
7. Embedding Stores (Adjacent System)
Vector / embedding stores are a separate axis but feature-store-adjacent:
- Online: serve user/item vectors at request time.
- Offline: bulk export for training.
- Versioning: embeddings change with model version; pin combinations.
Tools: Vespa, Vertex AI Matching Engine, Pinecone, Milvus, Qdrant, Weaviate, Redis with vector search.
8. Versioning & Governance
- Every feature has a schema, owner, description, freshness SLA, monitoring.
- Feature transformations are code-reviewed like model code.
- A/B safe: serving both v1 and v2 of a feature simultaneously is supported.
- PII tagging for compliance — GDPR delete must cascade.
9. Monitoring
- Freshness: time since last update per feature.
- Distribution drift: KS test on online vs training distributions.
- Null rates: silent feature-pipeline failures.
- Latency: per-feature p95 online read.
- Reconciliation tests: compare online vs offline reads for the same key + timestamp daily.
10. End-to-End: Mini Feature Store with Feast
# feature_repo/feature_store.yaml
project: rec_demo
registry: data/registry.db
provider: local
online_store:
type: redis
connection_string: localhost:6379
offline_store:
type: file
# feature_repo/views.py
from feast import Entity, FeatureView, Field, FileSource
from feast.types import Int64, Float32
from datetime import timedelta
user = Entity(name="user_id", join_keys=["user_id"])
user_stats_source = FileSource(
path="data/user_stats.parquet",
timestamp_field="event_timestamp",
)
user_stats = FeatureView(
name="user_features",
entities=[user],
ttl=timedelta(days=7),
schema=[Field(name="n_views_7d", dtype=Int64),
Field(name="n_purch_30d", dtype=Int64),
Field(name="ltv_total", dtype=Float32)],
source=user_stats_source,
)
feast apply
feast materialize-incremental $(date -u +%Y-%m-%dT%H:%M:%S)
Now training and serving read from the same schemas via get_historical_features and get_online_features.
11. Patterns Unique to RecSys
- Cross features at request time: user × candidate features (e.g.,
same_brand_as_recent_purchase) must be computed in the ranker — feature stores serve the building blocks. - Sequence features: store last-N item IDs per user; truncate to fixed length at read.
- Embeddings as features: store user/item vectors so retrieval and ranking share them.
- Throttle hot keys: top-1% of users/items dominate traffic; cache aggressively.
12. Pros & Cons
| Pros | Cons |
|---|---|
| Eliminates train-serve skew | Heavy upfront infra investment |
| Reuses features across models | Versioning rigor required |
| Centralized governance / PII | Becomes a SPOF if not redundant |
| Speeds up feature iteration | Adds latency hop if poorly designed |
13. Pitfalls
- Adding features in Python at serve time that weren’t computed offline = silent drift.
- TTL too short → cache misses; TTL too long → stale.
- Not logging feature values served alongside predictions — can’t debug regressions.
- Letting one team’s bad pipeline take down all features — isolate by namespace.
- Treating embeddings like normal features — they need their own vector store with proper indexing.
14. Public References & Datasets
- Feast example repos — https://github.com/feast-dev/feast
- Chronon docs — https://chronon.ai/
- MovieLens — easy to build a feature view: user counts, item averages
- Criteo logs — perfect for testing feature-store join scale — https://ailab.criteo.com/
15. Further Reading
- Uber, Michelangelo Palette: A Feature Store for Machine Learning (Uber Eng Blog 2019)
- Airbnb, Zipline: Airbnb’s Machine Learning Data Management Platform (2018) — predecessor of Chronon
- Tecton & Databricks talks at ApplyConf, Data + AI Summit
- Designing ML Systems (Chip Huyen, O’Reilly 2022) — feature store chapter
- Feast architecture docs — https://docs.feast.dev/