Skip to content

Commit a107a99

Browse files
committed
fix: Monitoring backend check and review comments fixed
Signed-off-by: Jitendra Yejare <11752425+jyejare@users.noreply.github.com>
1 parent d8e42ea commit a107a99

17 files changed

Lines changed: 326 additions & 152 deletions

docs/how-to-guides/feature-monitoring.md

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,16 +43,16 @@ Done!
4343
The baseline reads all available source data and stores the resulting statistics with `is_baseline=TRUE`. This serves as the reference distribution for future drift detection.
4444

4545
Baseline computation is:
46-
- **Non-blocking**`feast apply` returns immediately; computation runs asynchronously
46+
- **Threaded**runs in a background thread but completes before `feast apply` exits
4747
- **Idempotent** — only features without existing baselines are computed; re-running `feast apply` won't recompute existing baselines
4848

49-
### Disabling auto-baseline
49+
### Enabling auto-baseline
5050

51-
To skip automatic baseline computation on `feast apply`, set the DQM config in `feature_store.yaml`:
51+
To enable automatic baseline computation on `feast apply`, set the DQM config in `feature_store.yaml`:
5252

5353
```yaml
54-
DataQualityMonitoring:
55-
auto_baseline: false
54+
data_quality_monitoring:
55+
auto_baseline: true
5656
```
5757
5858
When using the Feast operator, set this in the `FeatureStore` CR:
@@ -63,9 +63,11 @@ kind: FeatureStore
6363
spec:
6464
feastProject: my_project
6565
dataQualityMonitoring:
66-
autoBaseline: false
66+
autoBaseline: true
6767
```
6868

69+
To disable it, set `auto_baseline: false` (or `autoBaseline: false` in the CR).
70+
6971
## 3. Scheduled monitoring with the CLI
7072

7173
### Auto mode (recommended for production)

sdk/python/feast/api/registry/rest/monitoring.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,29 @@ def _get_store():
7777
)
7878
return store
7979

80+
@router.get("/monitoring/config", tags=["Monitoring"])
81+
def monitoring_config():
82+
"""Report whether DQM is configured, checking the live config file."""
83+
import os
84+
85+
import yaml
86+
87+
s = _get_store()
88+
dqm = getattr(s.config, "data_quality_monitoring_config", None)
89+
if dqm is not None:
90+
return {"enabled": True}
91+
92+
repo_path = getattr(s, "repo_path", None)
93+
if repo_path:
94+
cfg_file = os.path.join(str(repo_path), "feature_store.yaml")
95+
if os.path.exists(cfg_file):
96+
with open(cfg_file) as f:
97+
cfg = yaml.safe_load(f)
98+
if cfg and cfg.get("data_quality_monitoring"):
99+
return {"enabled": True}
100+
101+
return {"enabled": False}
102+
80103
# ------------------------------------------------------------------ #
81104
# DQM Job: submit and track
82105
# ------------------------------------------------------------------ #

sdk/python/feast/monitoring/monitoring_service.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,7 @@ def compute_baseline(
367367
feature_view=fv,
368368
metrics_list=metrics_list,
369369
metric_date=date.today(),
370-
granularity="daily",
370+
granularity="baseline",
371371
set_baseline=True,
372372
now=now,
373373
)

sdk/python/feast/ui_server.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ def _setup_rest_mode(app: FastAPI, store: "feast.FeatureStore"):
8181
grpc_handler = RegistryServer(store.registry)
8282

8383
rest_app = FastAPI(root_path="/api/v1")
84-
register_all_routes(rest_app, grpc_handler)
84+
register_all_routes(rest_app, grpc_handler, store=store)
8585

8686
class PushRequest(BaseModel):
8787
push_source_name: str

ui/src/FeastUISansProviders.tsx

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React from "react";
1+
import React, { useEffect, useState } from "react";
22

33
import "./index.css";
44

@@ -109,6 +109,23 @@ const FeastUISansProvidersInner = ({
109109
fetchOptions: feastUIConfigs?.fetchOptions,
110110
};
111111

112+
const [autoMonitoringEnabled, setAutoMonitoringEnabled] = useState(false);
113+
useEffect(() => {
114+
if (feastUIConfigs?.monitoringConfig) return;
115+
fetch("/api/v1/monitoring/config")
116+
.then((r) => r.json())
117+
.then((data) => {
118+
if (data?.enabled) setAutoMonitoringEnabled(true);
119+
})
120+
.catch(() => {});
121+
}, [feastUIConfigs?.monitoringConfig]);
122+
123+
const monitoringConfig: MonitoringConfig =
124+
feastUIConfigs?.monitoringConfig || {
125+
apiBaseUrl: "/api/v1",
126+
enabled: autoMonitoringEnabled,
127+
};
128+
112129
return (
113130
<EuiProvider colorMode={colorMode}>
114131
<EuiErrorBoundary>
@@ -144,14 +161,7 @@ const FeastUISansProvidersInner = ({
144161
<FeatureFlagsContext.Provider
145162
value={feastUIConfigs?.featureFlags || {}}
146163
>
147-
<MonitoringContext.Provider
148-
value={
149-
feastUIConfigs?.monitoringConfig || {
150-
apiBaseUrl: "/api/v1",
151-
enabled: true,
152-
}
153-
}
154-
>
164+
<MonitoringContext.Provider value={monitoringConfig}>
155165
<ProjectListContext.Provider value={projectListContext}>
156166
<Routes>
157167
<Route path="/" element={<Layout />}>
@@ -195,7 +205,10 @@ const FeastUISansProvidersInner = ({
195205
path="entity/:entityName/*"
196206
element={<EntityInstance />}
197207
/>
198-
<Route path="label-view/" element={<LabelViewIndex />} />
208+
<Route
209+
path="label-view/"
210+
element={<LabelViewIndex />}
211+
/>
199212
<Route
200213
path="label-view/:labelViewName/*"
201214
element={<LabelViewInstance />}

ui/src/contexts/MonitoringContext.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ interface MonitoringConfig {
77

88
const MonitoringContext = React.createContext<MonitoringConfig>({
99
apiBaseUrl: "/api/v1",
10-
enabled: true,
10+
enabled: false,
1111
});
1212

1313
export default MonitoringContext;

ui/src/pages/Sidebar.tsx

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
import React, { useState } from "react";
1+
import React, { useContext, useState } from "react";
22

33
import { EuiIcon, EuiSideNav, htmlIdGenerator } from "@elastic/eui";
44
import { Link, useParams } from "react-router-dom";
55
import { useMatchSubpath } from "../hooks/useMatchSubpath";
6+
import MonitoringContext from "../contexts/MonitoringContext";
67
import useResourceQuery, {
78
entityListPath,
89
featureViewListPath,
@@ -84,6 +85,8 @@ const SideNav = () => {
8485
restSelect: restLabelViewsFromResponse,
8586
});
8687

88+
const { enabled: monitoringEnabled } = useContext(MonitoringContext);
89+
8790
const [isSideNavOpenOnMobile, setisSideNavOpenOnMobile] = useState(false);
8891

8992
const toggleOpenOnMobile = () => {
@@ -99,6 +102,7 @@ const SideNav = () => {
99102
const labelViewsLabel = `Label Views ${lvSuccess && labelViews && labelViews.length > 0 ? `(${labelViews.length})` : ""}`;
100103

101104
const baseUrl = `/p/${projectName}`;
105+
const monitoringSelected = useMatchSubpath(`${baseUrl}/monitoring`);
102106

103107
const sideNav: React.ComponentProps<typeof EuiSideNav>["items"] = [
104108
{
@@ -176,24 +180,19 @@ const SideNav = () => {
176180
renderItem: (props) => <Link {...props} to={`${baseUrl}/data-set`} />,
177181
isSelected: useMatchSubpath(`${baseUrl}/data-set`),
178182
},
179-
{
180-
name: "Monitoring",
181-
id: htmlIdGenerator("monitoring")(),
182-
icon: <EuiIcon type="monitoringApp" />,
183-
renderItem: (props) => (
184-
<Link {...props} to={`${baseUrl}/monitoring`} />
185-
),
186-
isSelected: useMatchSubpath(`${baseUrl}/monitoring`),
187-
},
188-
{
189-
name: "Data Labeling",
190-
id: htmlIdGenerator("dataLabeling")(),
191-
icon: <EuiIcon type="documentEdit" color="#006BB4" />,
192-
renderItem: (props) => (
193-
<Link {...props} to={`${baseUrl}/data-labeling`} />
194-
),
195-
isSelected: useMatchSubpath(`${baseUrl}/data-labeling`),
196-
},
183+
...(monitoringEnabled
184+
? [
185+
{
186+
name: "Monitoring",
187+
id: htmlIdGenerator("monitoring")(),
188+
icon: <EuiIcon type="monitoringApp" />,
189+
renderItem: (props: any) => (
190+
<Link {...props} to={`${baseUrl}/monitoring`} />
191+
),
192+
isSelected: monitoringSelected,
193+
},
194+
]
195+
: []),
197196
{
198197
name: "Permissions",
199198
id: htmlIdGenerator("permissions")(),

ui/src/pages/features/FeatureMonitoringTab.tsx

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -49,15 +49,11 @@ const FeatureMonitoringTab = () => {
4949
if (!metrics || metrics.length === 0) return null;
5050
const withData = metrics.filter((m) => m.row_count > 0);
5151
const candidates = withData.length > 0 ? withData : metrics;
52-
return candidates.reduce((a, b) =>
53-
a.metric_date > b.metric_date ? a : b,
54-
);
52+
return candidates.reduce((a, b) => (a.metric_date > b.metric_date ? a : b));
5553
})();
5654

5755
const baselineMetric =
58-
baselineMetrics && baselineMetrics.length > 0
59-
? baselineMetrics[0]
60-
: null;
56+
baselineMetrics && baselineMetrics.length > 0 ? baselineMetrics[0] : null;
6157

6258
if (isError || !latestMetric) {
6359
return (
@@ -66,15 +62,12 @@ const FeatureMonitoringTab = () => {
6662
title={<h3>No Monitoring Data</h3>}
6763
body={
6864
<p>
69-
No monitoring metrics available for this feature. Run a
70-
monitoring compute job to generate data quality metrics.
65+
No monitoring metrics available for this feature. Run a monitoring
66+
compute job to generate data quality metrics.
7167
</p>
7268
}
7369
actions={
74-
<EuiButton
75-
size="s"
76-
href={`/p/${projectName}/monitoring`}
77-
>
70+
<EuiButton size="s" href={`/p/${projectName}/monitoring`}>
7871
Go to Monitoring
7972
</EuiButton>
8073
}
@@ -91,9 +84,7 @@ const FeatureMonitoringTab = () => {
9184
{isNumeric && latestMetric.histogram && (
9285
<NumericHistogramChart
9386
histogram={latestMetric.histogram as NumericHistogram}
94-
baseline={
95-
baselineMetric?.histogram as NumericHistogram | null
96-
}
87+
baseline={baselineMetric?.histogram as NumericHistogram | null}
9788
title="Distribution"
9889
/>
9990
)}

ui/src/pages/monitoring/FeatureMetricsDetail.tsx

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,7 @@ const FeatureMetricsDetail = () => {
4646
const navigate = useNavigate();
4747
const [selectedGranularity, setSelectedGranularity] = useState("");
4848

49-
useDocumentTitle(
50-
`${featureName} Monitoring | ${featureViewName} | Feast`,
51-
);
49+
useDocumentTitle(`${featureName} Monitoring | ${featureViewName} | Feast`);
5250

5351
const {
5452
data: metrics,
@@ -67,9 +65,7 @@ const FeatureMetricsDetail = () => {
6765
);
6866

6967
const baselineMetric =
70-
baselineMetrics && baselineMetrics.length > 0
71-
? baselineMetrics[0]
72-
: null;
68+
baselineMetrics && baselineMetrics.length > 0 ? baselineMetrics[0] : null;
7369

7470
const availableGranularities = useMemo(() => {
7571
const granularities = new Set<string>();
@@ -97,7 +93,8 @@ const FeatureMetricsDetail = () => {
9793
return options;
9894
}, [availableGranularities, baselineMetric]);
9995

100-
const effectiveGranularity = selectedGranularity || availableGranularities[0] || "";
96+
const effectiveGranularity =
97+
selectedGranularity || availableGranularities[0] || "";
10198

10299
const activeMetric = useMemo(() => {
103100
if (effectiveGranularity === BASELINE_KEY && baselineMetric) {
@@ -114,9 +111,7 @@ const FeatureMetricsDetail = () => {
114111
a.metric_date > b.metric_date ? a : b,
115112
);
116113
}
117-
return matching.reduce((a, b) =>
118-
a.metric_date > b.metric_date ? a : b,
119-
);
114+
return matching.reduce((a, b) => (a.metric_date > b.metric_date ? a : b));
120115
}, [metrics, effectiveGranularity, baselineMetric]);
121116

122117
const breadcrumbs = [
@@ -155,8 +150,8 @@ const FeatureMetricsDetail = () => {
155150
<p>
156151
No monitoring metrics found for feature{" "}
157152
<strong>{featureName}</strong> in feature view{" "}
158-
<strong>{featureViewName}</strong>. Run a monitoring
159-
compute job first.
153+
<strong>{featureViewName}</strong>. Run a monitoring compute job
154+
first.
160155
</p>
161156
}
162157
actions={
@@ -244,9 +239,7 @@ const FeatureMetricsDetail = () => {
244239
</EuiFlexItem>
245240

246241
<EuiFlexItem grow={1}>
247-
<StatsPanel
248-
metric={activeMetric}
249-
/>
242+
<StatsPanel metric={activeMetric} />
250243
</EuiFlexItem>
251244
</EuiFlexGroup>
252245

0 commit comments

Comments
 (0)