Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
85a18dc
feat: Add enabled/disabled toggle for feature views
rpathade May 13, 2026
5e98c7f
feat: Add demo noteboooks for users
ntkathole May 8, 2026
f735cd2
feat: Add CLI enable/disable commands and registry metadata support
rpathade May 15, 2026
426ae09
Added features
rpathade May 16, 2026
365ecb4
fix(compute-engine/local): Honor field_mapping on join keys in dedup …
1fanwang May 13, 2026
5b94d4d
feat: Add Prometheus gauges for FeatureStore installation telemetry (…
ntkathole May 13, 2026
b5297b8
docs: Rename Atlas Vector Search to MongoDB Vector Search and fix cod…
jvincent-mongodb May 13, 2026
04d500c
feat(dynamodb): Use ProjectionExpression when requested_features is set
Jwrede May 3, 2026
c9e368b
fix(dynamodb): Fix mypy type for _build_projection_expression return
Jwrede May 3, 2026
de050ab
fix(bigquery): Enable list inference for parquet loads in offline_wri…
Jwrede May 3, 2026
78fcd72
fix(trino): Clean up temporary entity tables after retrieval (#6381)
Jwrede May 14, 2026
2014acd
feat(bigquery): Support DATE-type event timestamp columns (#6362)
Jwrede May 14, 2026
5e69e5f
fix: Fixes for ray source
ntkathole May 14, 2026
53e4a92
feat: Expose registry endpoints on feature server for MCP access
patelchaitany Apr 21, 2026
e9791b0
fix: Revert state propagation to always update in _update_metadata_fi…
rpathade May 16, 2026
f7fd0f8
fix: Recompile protos for protobuf 4.x compatibility and fix state ma…
rpathade May 18, 2026
a4f7ab8
feat: Add unit tests for state machine and clean up lazy imports in r…
rpathade May 18, 2026
3aa8778
fix: Address review comments for feature view state management
rpathade May 20, 2026
e626433
fix: Resolve integration test failures in apply loop
rpathade May 20, 2026
8130b63
fix: Resolve integration test failures in apply loop
rpathade May 20, 2026
2b54bd0
Apply suggestion from @ntkathole
rpathade May 21, 2026
cb33b6d
fix: Resolve review comments for feature_store
rpathade May 21, 2026
f3b94eb
fix: Resolve review comments for feature_views.py
rpathade May 21, 2026
a4c85dc
Merge branch 'master' into feat/feature-view-enabled-disabled-v2
rpathade May 21, 2026
abb6a41
Merge remote-tracking branch 'origin/master' into feat/feature-view-e…
rpathade May 21, 2026
f4dd0a4
feat: Add FeatureStore methods and update describe for enabled/state
rpathade May 22, 2026
a3f65b0
Merge branch 'master' into feat/feature-view-enabled-disabled-v2
rpathade May 22, 2026
efd41a6
fix: Add type: ignore comments for mypy on BaseFeatureView attr access
rpathade May 22, 2026
436a712
fix: Remove REST API endpoints for enable/disable/set-state (deferred…
rpathade May 22, 2026
7110346
Merge remote-tracking branch 'origin/master' into feat/feature-view-e…
rpathade May 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
feat: Add Prometheus gauges for FeatureStore installation telemetry (#…
…6354)

Signed-off-by: ntkathole <nikhilkathole2683@gmail.com>
Signed-off-by: RutujaPathade <73137503+RutujaPathade@users.noreply.github.com>
  • Loading branch information
ntkathole authored and rpathade committed May 21, 2026
commit 5b94d4dde79e0e77d7a2c2eafb77d40c003ce1ad
19 changes: 17 additions & 2 deletions infra/feast-operator/cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import (
routev1 "github.com/openshift/api/route/v1"

"github.com/feast-dev/feast/infra/feast-operator/internal/controller"
feastmetrics "github.com/feast-dev/feast/infra/feast-operator/internal/controller/metrics"
"github.com/feast-dev/feast/infra/feast-operator/internal/controller/services"
// +kubebuilder:scaffold:imports
)
Expand Down Expand Up @@ -95,6 +96,7 @@ func main() {
var probeAddr string
var secureMetrics bool
var enableHTTP2 bool
var featureStoreMetrics bool
var tlsOpts []func(*tls.Config)
flag.StringVar(&metricsAddr, "metrics-bind-address", "0", "The address the metrics endpoint binds to. "+
"Use :8443 for HTTPS or :8080 for HTTP, or leave as 0 to disable the metrics service.")
Expand All @@ -106,6 +108,9 @@ func main() {
"If set, the metrics endpoint is served securely via HTTPS. Use --metrics-secure=false to use HTTP instead.")
flag.BoolVar(&enableHTTP2, "enable-http2", false,
"If set, HTTP/2 will be enabled for the metrics and webhook servers")
flag.BoolVar(&featureStoreMetrics, "feature-store-metrics", true,
"Enable Prometheus gauges exposing online/offline store and registry configuration per FeatureStore. "+
"Disable with --feature-store-metrics=false.")
opts := zap.Options{
Development: true,
}
Expand Down Expand Up @@ -206,9 +211,19 @@ func main() {

services.SetIsOpenShift(mgr.GetConfig())

var fsMetrics *feastmetrics.FeatureStoreMetrics
if featureStoreMetrics {
fsMetrics = feastmetrics.NewFeatureStoreMetrics()
fsMetrics.Register()
setupLog.Info("FeatureStore installation metrics enabled")
} else {
setupLog.Info("FeatureStore installation metrics disabled (--feature-store-metrics=false)")
}

if err = (&controller.FeatureStoreReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Metrics: fsMetrics,
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "FeatureStore")
os.Exit(1)
Expand Down
2 changes: 1 addition & 1 deletion infra/feast-operator/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ require (

require (
github.com/prometheus-operator/prometheus-operator/pkg/client v0.83.0
github.com/prometheus/client_golang v1.22.0
k8s.io/utils v0.0.0-20250502105355-0f33e8f1c979
)

Expand Down Expand Up @@ -55,7 +56,6 @@ require (
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.83.0 // indirect
github.com/prometheus/client_golang v1.22.0 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.62.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import (
feastdevv1 "github.com/feast-dev/feast/infra/feast-operator/api/v1"
"github.com/feast-dev/feast/infra/feast-operator/internal/controller/authz"
feasthandler "github.com/feast-dev/feast/infra/feast-operator/internal/controller/handler"
feastmetrics "github.com/feast-dev/feast/infra/feast-operator/internal/controller/metrics"
"github.com/feast-dev/feast/infra/feast-operator/internal/controller/services"
routev1 "github.com/openshift/api/route/v1"
)
Expand All @@ -55,7 +56,8 @@ const (
// FeatureStoreReconciler reconciles a FeatureStore object
type FeatureStoreReconciler struct {
client.Client
Scheme *runtime.Scheme
Scheme *runtime.Scheme
Metrics *feastmetrics.FeatureStoreMetrics
}

// +kubebuilder:rbac:groups=feast.dev,resources=featurestores,verbs=get;list;watch;create;update;patch;delete
Expand Down Expand Up @@ -87,6 +89,9 @@ func (r *FeatureStoreReconciler) Reconcile(ctx context.Context, req ctrl.Request
if apierrors.IsNotFound(err) {
// CR deleted since request queued, child objects getting GC'd, no requeue
logger.V(1).Info("FeatureStore CR not found, has been deleted")
if r.Metrics != nil {
r.Metrics.DeleteFeatureStore(req.NamespacedName.Namespace, req.NamespacedName.Name)
}
// Clean up namespace registry entry even if the CR is not found
if err := r.cleanupNamespaceRegistry(ctx, &feastdevv1.FeatureStore{
ObjectMeta: metav1.ObjectMeta{
Expand All @@ -107,6 +112,9 @@ func (r *FeatureStoreReconciler) Reconcile(ctx context.Context, req ctrl.Request
// Handle deletion - clean up namespace registry entry
if cr.DeletionTimestamp != nil {
logger.Info("FeatureStore is being deleted, cleaning up namespace registry entry")
if r.Metrics != nil {
r.Metrics.DeleteFeatureStore(cr.Namespace, cr.Name)
}
if err := r.cleanupNamespaceRegistry(ctx, cr); err != nil {
logger.Error(err, "Failed to clean up namespace registry entry")
return ctrl.Result{}, err
Expand All @@ -115,6 +123,9 @@ func (r *FeatureStoreReconciler) Reconcile(ctx context.Context, req ctrl.Request
}

result, recErr = r.deployFeast(ctx, cr)
if recErr == nil && r.Metrics != nil {
r.Metrics.RecordFeatureStore(cr)
}
if cr.DeletionTimestamp == nil && !reflect.DeepEqual(currentStatus, cr.Status) {
if err = r.Client.Status().Update(ctx, cr); err != nil {
if apierrors.IsConflict(err) {
Expand Down
133 changes: 133 additions & 0 deletions infra/feast-operator/internal/controller/metrics/metrics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
/*
Copyright 2026 Feast Community.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// Package metrics provides a Prometheus info gauge that records the store
// types configured for each FeatureStore CR (online store, offline store,
// registry). These operator-level metrics are distinct from the Feast
// feature-server application metrics (feast_feature_server_*) and are useful
// for usage telemetry and assessing the impact of removing store type support.
package metrics

import (
feastdevv1 "github.com/feast-dev/feast/infra/feast-operator/api/v1"
"github.com/prometheus/client_golang/prometheus"
ctrlmetrics "sigs.k8s.io/controller-runtime/pkg/metrics"
)

const typeNone = "none"

// FeatureStoreMetrics holds the Prometheus GaugeVec for feast-operator
// installation telemetry.
type FeatureStoreMetrics struct {
FeatureStoreInfo *prometheus.GaugeVec
}

// NewFeatureStoreMetrics creates a new FeatureStoreMetrics with the GaugeVec
// initialised but not yet registered. Call Register() before starting the manager.
func NewFeatureStoreMetrics() *FeatureStoreMetrics {
return &FeatureStoreMetrics{
FeatureStoreInfo: prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Name: "feast_operator_feature_store_info",
Help: "Information about a deployed FeatureStore. " +
"Value is always 1. Labels carry the configured store types: " +
"'online_store_type', 'offline_store_type', and 'registry_type' " +
"are set to the persistence type (e.g. redis, snowflake.offline, local) " +
"or 'none' when that component is not configured.",
},
[]string{"namespace", "name", "online_store_type", "offline_store_type", "registry_type"},
),
}
}

// Register registers the metric with the controller-runtime metrics registry
// so it is exposed on the manager's /metrics endpoint.
func (m *FeatureStoreMetrics) Register() {
ctrlmetrics.Registry.MustRegister(m.FeatureStoreInfo)
}

// RecordFeatureStore updates the gauge for the given FeatureStore using the
// applied configuration stored in status.Applied (which has operator defaults
// applied). The previous label set for this FeatureStore is deleted first so
// that store type changes are reflected cleanly on the next scrape.
func (m *FeatureStoreMetrics) RecordFeatureStore(fs *feastdevv1.FeatureStore) {
svcs := fs.Status.Applied.Services
m.FeatureStoreInfo.DeletePartialMatch(prometheus.Labels{
"namespace": fs.Namespace,
"name": fs.Name,
})
m.FeatureStoreInfo.WithLabelValues(
fs.Namespace,
fs.Name,
onlineStoreType(svcs),
offlineStoreType(svcs),
registryType(svcs),
).Set(1)
}

// DeleteFeatureStore removes the metric label set for the given FeatureStore.
// Safe to call when the CR has already been deleted from the API server.
func (m *FeatureStoreMetrics) DeleteFeatureStore(namespace, name string) {
m.FeatureStoreInfo.DeletePartialMatch(prometheus.Labels{
"namespace": namespace,
"name": name,
})
}

// onlineStoreType returns the online store persistence type or "none".
func onlineStoreType(svcs *feastdevv1.FeatureStoreServices) string {
if svcs == nil || svcs.OnlineStore == nil {
return typeNone
}
if p := svcs.OnlineStore.Persistence; p != nil && p.DBPersistence != nil {
return p.DBPersistence.Type
}
return "file"
}

// offlineStoreType returns the offline store persistence type or "none".
func offlineStoreType(svcs *feastdevv1.FeatureStoreServices) string {
if svcs == nil || svcs.OfflineStore == nil {
return typeNone
}
if p := svcs.OfflineStore.Persistence; p != nil {
if p.DBPersistence != nil {
return p.DBPersistence.Type
}
if p.FilePersistence != nil && p.FilePersistence.Type != "" {
return p.FilePersistence.Type
}
}
return "file"
}

// registryType returns "local", "remote", "remote_feastref", or "none".
func registryType(svcs *feastdevv1.FeatureStoreServices) string {
if svcs == nil || svcs.Registry == nil {
return typeNone
}
switch {
case svcs.Registry.Local != nil:
return "local"
case svcs.Registry.Remote != nil:
if svcs.Registry.Remote.FeastRef != nil {
return "remote_feastref"
}
return "remote"
default:
return typeNone
}
}
Loading