docs/reference/registries/sql.md
By default, the registry Feast uses a file-based registry implementation, which stores the protobuf representation of the registry as a serialized file. This registry file can be stored in a local file system, or in cloud storage (in, say, S3 or GCS).
However, there's inherent limitations with a file-based registry, since changing a single field in the registry requires re-writing the whole registry file. With multiple concurrent writers, this presents a risk of data loss, or bottlenecks writes to the registry since all changes have to be serialized (e.g. when running materialization for multiple feature views or time ranges concurrently).
An alternative to the file-based registry is the SQLRegistry which ships with Feast. This implementation stores the registry in a relational database, and allows for changes to individual objects atomically. Under the hood, the SQL Registry implementation uses SQLAlchemy to abstract over the different databases. Consequently, any database supported by SQLAlchemy can be used by the SQL Registry. The following databases are supported and tested out of the box:
Feast can use the SQL Registry via a config change in the feature_store.yaml file. An example of how to configure this would be:
project: <your project name>
provider: <provider name>
online_store: redis
offline_store: file
registry:
registry_type: sql
path: postgresql://postgres:[email protected]:55001/feast
cache_ttl_seconds: 60
sqlalchemy_config_kwargs:
echo: false
pool_pre_ping: true
Specifically, the registry_type needs to be set to sql in the registry config block. On doing so, the path should refer to the Database URL for the database to be used, as expected by SQLAlchemy. No other additional commands are currently needed to configure this registry.
Should you choose to use a database technology that is compatible with one of
Feast's supported registry backends, but which speaks a different dialect (e.g.
cockroachdb, which is compatible with postgres) then some further
intervention may be required on your part.
SQLAlchemy, used by the registry, may not be able to detect your database
version without first updating your DSN scheme to the appropriate
DBAPI/dialect combination.
When this happens, your database is likely using what is referred to as an
external dialect
in SQLAlchemy terminology. See your database's documentation for examples on
how to set its scheme in the Database URL.
Psycopg, which is the database library leveraged by the online and offline
stores, is not impacted by the need to speak a particular dialect, and so the
following only applies to the registry.
If you are not running Feast in a container, to accomodate SQLAlchemy's need
to speak an external dialect, install additional Python modules like we do as
follows using cockroachdb for example:
pip install sqlalchemy-cockroachdb
If you are running Feast in a container, you will need to create a custom image
like we do as follows, again using cockroachdb as an example:
cat <<'EOF' >Dockerfile
ARG QUAY_IO_FEASTDEV_FEATURE_SERVER
FROM quay.io/feastdev/feature-server:${QUAY_IO_FEASTDEV_FEATURE_SERVER}
ARG PYPI_ORG_PROJECT_SQLALCHEMY_COCKROACHDB
RUN pip install -I --no-cache-dir \
sqlalchemy-cockroachdb==${PYPI_ORG_PROJECT_SQLALCHEMY_COCKROACHDB}
EOF
export QUAY_IO_FEASTDEV_FEATURE_SERVER=0.27.1
export PYPI_ORG_PROJECT_SQLALCHEMY_COCKROACHDB=1.4.4
docker build \
--build-arg QUAY_IO_FEASTDEV_FEATURE_SERVER \
--build-arg PYPI_ORG_PROJECT_SQLALCHEMY_COCKROACHDB \
--tag ${MY_REGISTRY}/feastdev/feature-server:${QUAY_IO_FEASTDEV_FEATURE_SERVER} .
If you are running Feast in Kubernetes, set the image.repository and
imagePullSecrets Helm values accordingly to utilize your custom image.
There are some things to note about how the SQL registry works:
sdk/python/feast/infra/registry/sql.py. It is intentionally simple, storing the serialized protobuf versions of each Feast object keyed by its name.LONGBLOBThe registry stores each Feast object as a serialized protobuf in a binary
column. On MySQL these columns are created as LONGBLOB (up to 4 GB). Earlier
versions created them as BLOB, which caps at 64 KB — a single FeatureView
proto routinely exceeds that, so MySQL would silently truncate the write and the
registry would later fail to load with a protobuf DecodeError (for example,
feast serve failing to start). Other dialects (PostgreSQL, SQLite) were never
affected.
New deployments get the correct schema automatically — the registry creates its
tables as LONGBLOB on first use. When an existing MySQL/MariaDB registry still
has BLOB columns, the registry logs an error at startup listing the affected
columns (it does not refuse to start — a registry whose protos all fit in 64 KB
is unaffected). Existing deployments are not migrated automatically: the
registry only creates tables that do not already exist, and it has no
schema-migration step, so previously created BLOB columns remain BLOB. To
upgrade an existing MySQL registry, alter each serialized-proto column to
LONGBLOB, for example:
⚠️ Run the migration carefully on a live registry. A
BLOB→LONGBLOBchange is a column data-type change, which MySQL InnoDB performs withALGORITHM=COPY— a full table rebuild under a metadata lock that blocks readers and writers for the duration (potentially minutes on a large table such asfeature_view_version_history).ALGORITHM=INPLACEis not generally supported for this change and is rejected withER_ALTER_OPERATION_NOT_SUPPORTED_REASONon most builds — do not rely on it.Before running any
ALTER TABLE:
- Stop all
feast applyand materialization jobs. This is required, not optional — a write of a>64 KBproto to a not-yet-widenedBLOBcolumn truncates silently with no error, and concurrent writes also extend theALTER's lock duration.- Confirm there are no active writers (e.g.
SHOW PROCESSLIST).- Verify you have a backup of the registry database.
Then, to minimize the lock window:
- On large tables, or on managed MySQL (AWS RDS, Aurora) without shell access, use an online schema-change tool —
pt-online-schema-change(Percona Toolkit) orgh-ost— which rebuild the table without a long-held lock. For small tables a plainALTER TABLEin the maintenance window is fine.- Apply one table at a time so a failure is easy to isolate and re-run.
- Resume jobs only after all
ALTER TABLEstatements complete successfully.- Rollback is safe (revert
MODIFY ... BLOB) only while no stored proto exceeds 64 KB; otherwise a revert re-introduces truncation.
ALTER TABLE projects MODIFY project_proto LONGBLOB NOT NULL;
ALTER TABLE entities MODIFY entity_proto LONGBLOB NOT NULL;
ALTER TABLE data_sources MODIFY data_source_proto LONGBLOB NOT NULL;
ALTER TABLE feature_views MODIFY materialized_intervals LONGBLOB,
MODIFY feature_view_proto LONGBLOB NOT NULL,
MODIFY user_metadata LONGBLOB;
ALTER TABLE stream_feature_views MODIFY feature_view_proto LONGBLOB NOT NULL,
MODIFY user_metadata LONGBLOB;
ALTER TABLE on_demand_feature_views MODIFY feature_view_proto LONGBLOB NOT NULL,
MODIFY user_metadata LONGBLOB;
ALTER TABLE label_views MODIFY feature_view_proto LONGBLOB NOT NULL,
MODIFY user_metadata LONGBLOB;
ALTER TABLE feature_services MODIFY feature_service_proto LONGBLOB NOT NULL;
ALTER TABLE saved_datasets MODIFY saved_dataset_proto LONGBLOB NOT NULL;
ALTER TABLE validation_references MODIFY validation_reference_proto LONGBLOB NOT NULL;
ALTER TABLE managed_infra MODIFY infra_proto LONGBLOB NOT NULL;
ALTER TABLE permissions MODIFY permission_proto LONGBLOB NOT NULL;
-- LARGE TABLE: one row per versioned apply — likely the slowest ALTER. Use
-- pt-online-schema-change or gh-ost if this registry has significant history.
ALTER TABLE feature_view_version_history MODIFY feature_view_proto LONGBLOB NOT NULL;
Any object whose proto already exceeded 64 KB before the upgrade may have been
stored truncated; re-run feast apply for those objects after altering the
columns so the full proto is rewritten.
The SQL Registry should be used when materializing feature views concurrently to ensure correctness of data in the registry. This can be achieved by simply running feast materialize or feature_store.materialize multiple times using a correctly configured feature_store.yaml. This will make each materialization process talk to the registry database concurrently, and ensure the metadata updates are serialized.