Expected Behavior
The Feast operator should deploy only the services declared in a FeatureStore CR. It should be possible to deploy a standalone registry or a standalone offline store, with no online store, by simply not specifying spec.services.onlineStore. This mirrors how registry and offlineStore already behave and is required by the documented federated / shared registry pattern.
Current Behavior
The operator always deploys an online store service (including a serving pod), even when the CR does not declare spec.services.onlineStore. Root cause is in ApplyDefaultsToStatus (internal/controller/services/util.go):
// default to onlineStore service deployment
if services.OnlineStore == nil {
services.OnlineStore = &feastdevv1.OnlineStore{}
}
// ...
if services.OnlineStore.Server == nil {
services.OnlineStore.Server = &feastdevv1.ServerConfigs{} // forces a serving pod
}
Unlike registry (if services.Registry != nil) and offlineStore (if services.OfflineStore != nil), which are only defaulted when declared, and whose .Server is only defaulted when already present, the onlineStore block runs unconditionally and creates Server from nil.
Downstream:
isOnlineStore() is always true → reconcileServices() always deploys the online store, and isOnlineServer() is always true. There's no opt-out, the OnlineStore CRD type has no disabled field. (This also makes the "at least one local server must be configured" guard in Deploy() unreachable.)
- The scaling admission validation is asymmetric (api/v1/featurestore_types.go): the offline rule allows the store to be absent (
!has(self.services.offlineStore) || ...), but the online rule requires it to exist and be DB-backed. So scaling a registry-only/offline-only store fails admission with Scaling requires DB-backed persistence for the online store. Configure services.onlineStore.persistence.store when using replicas > 1 or autoscaling., forcing DB persistence for an online store the user never asked for.
Steps to reproduce
- Apply a CR declaring only a registry (or only an offline store), with no
spec.services.onlineStore.
kubectl get deploy -l feast.dev/name=<name> → an online/feature-server container is deployed alongside the registry (expected: registry only).
- Add
spec.replicas: 2 (or services.scaling.autoscaling) to that registry-only CR → rejected at admission with the online-store DB-persistence error, despite no online store being declared.
Specifications
- Version: 0.64.0
- Platform: Kubernetes
- Subsystem: feast-operator — internal/controller/services
Possible Solution
- Guard the online-store block in
ApplyDefaultsToStatus with if services.OnlineStore != nil { ... } (and only default Server when already present), making it symmetric with registry/offline. Preferable to adding a new opt-out API field.
- Add an "online store absent" escape hatch to the online CEL rule (
!has(self.services.onlineStore) || (...)), matching the offline rule.
- Keep
noLocalCoreServerConfigured() enforcing that at least one core server is deployed.
Happy to open a PR! 😄
Expected Behavior
The Feast operator should deploy only the services declared in a FeatureStore CR. It should be possible to deploy a standalone registry or a standalone offline store, with no online store, by simply not specifying spec.services.onlineStore. This mirrors how registry and offlineStore already behave and is required by the documented federated / shared registry pattern.
Current Behavior
The operator always deploys an online store service (including a serving pod), even when the CR does not declare spec.services.onlineStore. Root cause is in ApplyDefaultsToStatus (internal/controller/services/util.go):
Unlike registry
(if services.Registry != nil)andofflineStore (if services.OfflineStore != nil), which are only defaulted when declared, and whose.Serveris only defaulted when already present, the onlineStore block runs unconditionally and creates Server from nil.Downstream:
isOnlineStore()is always true →reconcileServices()always deploys the online store, andisOnlineServer()is always true. There's no opt-out, theOnlineStoreCRD type has nodisabledfield. (This also makes the "at least one local server must be configured" guard inDeploy()unreachable.)!has(self.services.offlineStore) || ...), but the online rule requires it to exist and be DB-backed. So scaling a registry-only/offline-only store fails admission withScaling requires DB-backed persistence for the online store. Configure services.onlineStore.persistence.store when using replicas > 1 or autoscaling., forcing DB persistence for an online store the user never asked for.Steps to reproduce
spec.services.onlineStore.kubectl get deploy -l feast.dev/name=<name>→ an online/feature-server container is deployed alongside the registry (expected: registry only).spec.replicas: 2(orservices.scaling.autoscaling) to that registry-only CR → rejected at admission with the online-store DB-persistence error, despite no online store being declared.Specifications
Possible Solution
ApplyDefaultsToStatuswith ifservices.OnlineStore != nil { ... }(and only default Server when already present), making it symmetric with registry/offline. Preferable to adding a new opt-out API field.!has(self.services.onlineStore) || (...)), matching the offline rule.noLocalCoreServerConfigured()enforcing that at least one core server is deployed.Happy to open a PR! 😄