Skip to content

Production MLOps Platform · May – Jun 2026

ml-feature-store: Lightweight Feature Store

Sub-10ms online serving with point-in-time correct joins

Source

The problem

Training-serving skew: features computed one way for training and another for serving. The failure is silent, it is common, and it makes offline metrics untrustworthy.

Approach

A dual-store architecture (PostgreSQL offline, Redis online) with LEFT JOIN LATERAL point-in-time correct joins, so training data reflects only what was knowable at the label timestamp. A @feature decorator SHA-256 hashes the function body, so changing a weighting automatically creates a new version with no manual version bumps. Schema validation and Evidently AI drift detection run on every ingestion run. Ships with a CLI, a Python SDK and a Streamlit registry.

How it works

Key decisions

One definition, two backing stores
Training and serving read the same feature definitions from stores optimised for different access patterns: full history in PostgreSQL, latest value in Redis. That shared definition is what actually removes training-serving skew.
Hash the code, not the output
Versioning the function body catches a changed weighting even when the resulting values look similar, and removes the class of bug where someone forgets to bump a version number.
NullPool instead of a shared connection pool
A SQLAlchemy async engine created at import time binds to whichever event loop touches it first, and Uvicorn creates its own. Each request now opens and closes its own connection: slightly slower, no cross-loop state.

Architecture

Feature store dual-store architectureData sources feed an ingestion pipeline, which writes to a PostgreSQL offline store and a Redis online store. The offline store supplies training data through a point-in-time correct join; the online store serves inference in under 10 milliseconds. A registry tracks versions across both.SourcesCSV · PG · KafkaIngestion@feature · SHA-256drift detectionOffline · PostgreSQLfull historyOnline · Redislatest value onlypoint-in-timecorrect joinTrainingno leakageServing<10msregistry · versions

What broke

Running it

docker compose up

Full setup, configuration and API reference are in the repository README.

Stack

Written up in