Skip to content
Merged
Prev Previous commit
Next Next commit
feat: Optional Monitoring and utils file added
Signed-off-by: Jitendra Yejare <11752425+jyejare@users.noreply.github.com>
  • Loading branch information
jyejare committed Jun 9, 2026
commit e6fcd741468f74a1c19ddebe740456c719508973
46 changes: 23 additions & 23 deletions .secrets.baseline

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 26 additions & 0 deletions docs/how-to-guides/feature-monitoring.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,32 @@ Baseline computation is:
- **Non-blocking** — `feast apply` returns immediately; computation runs asynchronously
- **Idempotent** — only features without existing baselines are computed; re-running `feast apply` won't recompute existing baselines

### Disabling auto-baseline

To skip automatic baseline computation on `feast apply`, set the DQM config in `feature_store.yaml`:

```yaml
feature_server:
type: local
dqm:
distribution:
initial:
enabled: false
```

When using the Feast operator, set this in the `FeatureStore` CR:

```yaml
apiVersion: feast.dev/v1
kind: FeatureStore
spec:
feastProject: my_project
dqm:
distribution:
initial:
enabled: false
```

## 3. Scheduled monitoring with the CLI

### Auto mode (recommended for production)
Expand Down
2 changes: 1 addition & 1 deletion examples/monitoring/monitoring-quickstart.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -867,7 +867,7 @@
},
{
"cell_type": "code",
"execution_count": 21,
"execution_count": 44,
"metadata": {},
"outputs": [
{
Expand Down
24 changes: 24 additions & 0 deletions infra/feast-operator/api/v1/featurestore_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,9 @@ type FeatureStoreSpec struct {
AuthzConfig *AuthzConfig `json:"authz,omitempty"`
CronJob *FeastCronJob `json:"cronJob,omitempty"`
BatchEngine *BatchEngineConfig `json:"batchEngine,omitempty"`
// Dqm configures Data Quality Monitoring behaviour.
// +optional
Dqm *DqmConfig `json:"dqm,omitempty"`
Comment thread
jyejare marked this conversation as resolved.
Outdated
// Replicas is the desired number of pod replicas. Used by the scale sub-resource.
// Mutually exclusive with services.scaling.autoscaling.
// +kubebuilder:default=1
Expand Down Expand Up @@ -229,6 +232,27 @@ type BatchEngineConfig struct {
ConfigMapKey string `json:"configMapKey,omitempty"`
}

// DqmConfig defines the Data Quality Monitoring configuration.
type DqmConfig struct {
// Distribution controls distribution-related DQM behaviour.
// +optional
Distribution *DqmDistributionConfig `json:"distribution,omitempty"`
}

// DqmDistributionConfig controls distribution monitoring settings.
type DqmDistributionConfig struct {
// Initial controls automatic baseline distribution computation on feast apply.
// +optional
Initial *DqmInitialDistributionConfig `json:"initial,omitempty"`
}

// DqmInitialDistributionConfig controls the initial baseline distribution computation.
type DqmInitialDistributionConfig struct {
// Enabled controls whether baseline distribution is computed on feast apply. Defaults to true.
// +kubebuilder:default=true
Enabled *bool `json:"enabled,omitempty"`
}

// JobSpec describes how the job execution will look like.
type JobSpec struct {
// PodTemplateAnnotations are annotations to be applied to the CronJob's PodTemplate
Expand Down
65 changes: 65 additions & 0 deletions infra/feast-operator/api/v1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,10 @@ func getServiceRepoConfig(
}
}

if appliedSpec.Dqm != nil {
setRepoConfigDqm(appliedSpec.Dqm, &repoConfig)
}

return repoConfig, nil
}

Expand Down Expand Up @@ -486,6 +490,21 @@ func coerceStringToYamlType(v string) interface{} {
return v
}

func setRepoConfigDqm(dqmConfig *feastdevv1.DqmConfig, repoConfig *RepoConfig) {
if dqmConfig.Distribution == nil || dqmConfig.Distribution.Initial == nil || dqmConfig.Distribution.Initial.Enabled == nil {
return
}
repoConfig.FeatureServer = &FeatureServerYamlConfig{
Dqm: &DqmYamlConfig{
Distribution: &DqmDistributionYamlConfig{
Initial: &DqmInitialDistributionYamlConfig{
Enabled: *dqmConfig.Distribution.Initial.Enabled,
},
},
},
}
}

func (feast *FeastServices) getClientFeatureStoreYaml() ([]byte, error) {
clientRepo := getClientRepoConfig(feast.Handler.FeatureStore, feast)
return yaml.Marshal(clientRepo)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,37 @@ var _ = Describe("Repo Config", func() {
Expect(repoConfig.OfflineStore).To(Equal(expectedOfflineConfig))
Expect(repoConfig.OnlineStore).To(Equal(expectedOnlineConfig))
Expect(repoConfig.Registry).To(Equal(expectedRegistryConfig))

By("Having DQM config with initial distribution disabled")
featureStore = minimalFeatureStore()
dqmEnabled := false
featureStore.Spec.Dqm = &feastdevv1.DqmConfig{
Distribution: &feastdevv1.DqmDistributionConfig{
Initial: &feastdevv1.DqmInitialDistributionConfig{
Enabled: &dqmEnabled,
},
},
}
ApplyDefaultsToStatus(featureStore)
repoConfig, err = getServiceRepoConfig(featureStore, emptyMockExtractConfigFromSecret, emptyMockExtractConfigFromConfigMap, false)
Expect(err).NotTo(HaveOccurred())
Expect(repoConfig.FeatureServer).NotTo(BeNil())
Expect(repoConfig.FeatureServer.Dqm).NotTo(BeNil())
Expect(repoConfig.FeatureServer.Dqm.Distribution).NotTo(BeNil())
Expect(repoConfig.FeatureServer.Dqm.Distribution.Initial).NotTo(BeNil())
Expect(repoConfig.FeatureServer.Dqm.Distribution.Initial.Enabled).To(BeFalse())

fsYaml, marshalErr := yaml.Marshal(repoConfig)
Expect(marshalErr).NotTo(HaveOccurred())
Expect(string(fsYaml)).To(ContainSubstring("feature_server:"))
Expect(string(fsYaml)).To(ContainSubstring("enabled: false"))

By("Having no DQM config — feature_server should be nil")
featureStore = minimalFeatureStore()
ApplyDefaultsToStatus(featureStore)
repoConfig, err = getServiceRepoConfig(featureStore, emptyMockExtractConfigFromSecret, emptyMockExtractConfigFromConfigMap, false)
Expect(err).NotTo(HaveOccurred())
Expect(repoConfig.FeatureServer).To(BeNil())
})

It("should set feature_server block with type local and all options", func() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,7 @@ type FeatureServerYamlConfig struct {
McpServerName *string `yaml:"mcp_server_name,omitempty"`
McpServerVersion *string `yaml:"mcp_server_version,omitempty"`
McpTransport *string `yaml:"mcp_transport,omitempty"`
Dqm *DqmYamlConfig `yaml:"dqm,omitempty"`
}

// MetricsYamlConfig maps to the feature_server.metrics section of feature_store.yaml.
Expand All @@ -298,6 +299,20 @@ type MetricsYamlConfig struct {
Categories map[string]interface{} `yaml:",inline,omitempty"`
}

type DqmYamlConfig struct {
Distribution *DqmDistributionYamlConfig `yaml:"distribution,omitempty"`
}

// DqmDistributionYamlConfig mirrors the Python DqmDistributionConfig.
type DqmDistributionYamlConfig struct {
Initial *DqmInitialDistributionYamlConfig `yaml:"initial,omitempty"`
}

// DqmInitialDistributionYamlConfig mirrors the Python DqmInitialDistributionConfig.
type DqmInitialDistributionYamlConfig struct {
Enabled bool `yaml:"enabled"`
}

// MaterializationYamlConfig maps to the materialization section of feature_store.yaml.
// ExtraConfig is merged inline so future Feast MaterializationConfig fields appear
// at the same YAML level as the typed fields above.
Expand Down
19 changes: 19 additions & 0 deletions sdk/python/feast/infra/feature_servers/base_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,22 @@ class MetricsConfig(FeastConfigBaseModel):
identity, entity keys, feature views, row counts, and latency."""


class DqmInitialDistributionConfig(FeastConfigBaseModel):

@ntkathole ntkathole May 6, 2026 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these configs should also live at top instead of feature_server. It uses the offline store, not the online server. This field is more similar to materialization, which is top-level config.

#feature_store.yaml        

feature_monitoring:   
  auto_baseline: false 

This matches the pattern: materialization: spans offline+online stores, openlineage: spans apply+materialize - feature_monitoring: spans offline store (compute/storage) + apply trigger + server API.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be due to the existing metrics config is residing under server config. The metrics(even operational metrics) could also be computed for offline store. So I think its good if we move both operational metrics and dqm metrics under the parent monitoring config.

"""Controls whether baseline distribution is computed on ``feast apply``."""

enabled: StrictBool = True


class DqmDistributionConfig(FeastConfigBaseModel):
initial: DqmInitialDistributionConfig = DqmInitialDistributionConfig()


class DqmConfig(FeastConfigBaseModel):
"""Data Quality Monitoring (DQM) configuration."""

distribution: DqmDistributionConfig = DqmDistributionConfig()


class BaseFeatureServerConfig(FeastConfigBaseModel):
"""Base Feature Server config that should be extended"""

Expand All @@ -107,6 +123,9 @@ class BaseFeatureServerConfig(FeastConfigBaseModel):
feature_logging: Optional[FeatureLoggingConfig] = None
""" Feature logging configuration """

dqm: Optional[DqmConfig] = None
"""Data Quality Monitoring configuration."""

offline_push_batching_enabled: Optional[StrictBool] = None
"""Whether to batch writes to the offline store via the `/push` endpoint."""

Expand Down
Loading