Skip to content

Commit 1b09ca2

Browse files
authored
fix: Asynchronously refresh registry for the feast ui command (#2672)
* fix: Allow Feast UI to asynchronously refresh the registry (and pass host + port values in CLI) Signed-off-by: Danny Chiao <[email protected]> * remove lint Signed-off-by: Danny Chiao <[email protected]> * revert change Signed-off-by: Danny Chiao <[email protected]> * increment version Signed-off-by: Danny Chiao <[email protected]>
1 parent 36849fb commit 1b09ca2

File tree

6 files changed

+86
-19
lines changed

6 files changed

+86
-19
lines changed

sdk/python/feast/cli.py

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -110,16 +110,42 @@ def version():
110110

111111

112112
@cli.command()
113+
@click.option(
114+
"--host",
115+
"-h",
116+
type=click.STRING,
117+
default="0.0.0.0",
118+
help="Specify a host for the server [default: 0.0.0.0]",
119+
)
120+
@click.option(
121+
"--port",
122+
"-p",
123+
type=click.INT,
124+
default=8888,
125+
help="Specify a port for the server [default: 8888]",
126+
)
127+
@click.option(
128+
"--registry_ttl_sec",
129+
"-r",
130+
help="Number of seconds after which the registry is refreshed. Default is 5 seconds.",
131+
type=int,
132+
default=5,
133+
)
113134
@click.pass_context
114-
def ui(ctx: click.Context):
135+
def ui(ctx: click.Context, host: str, port: int, registry_ttl_sec: int):
115136
"""
116137
Shows the Feast UI over the current directory
117138
"""
118139
repo = ctx.obj["CHDIR"]
119140
cli_check_repo(repo)
120141
store = FeatureStore(repo_path=str(repo))
121-
repo_config = load_repo_config(repo)
122-
store.serve_ui(registry_dump(repo_config, repo_path=repo))
142+
# Pass in the registry_dump method to get around a circular dependency
143+
store.serve_ui(
144+
host=host,
145+
port=port,
146+
get_registry_dump=registry_dump,
147+
registry_ttl_sec=registry_ttl_sec,
148+
)
123149

124150

125151
@cli.command()

sdk/python/feast/feature_store.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
from typing import (
2222
TYPE_CHECKING,
2323
Any,
24+
Callable,
2425
Dict,
2526
Iterable,
2627
List,
@@ -1995,14 +1996,23 @@ def get_feature_server_endpoint(self) -> Optional[str]:
19951996
return self._provider.get_feature_server_endpoint()
19961997

19971998
@log_exceptions_and_usage
1998-
def serve_ui(self, registry_dump: str) -> None:
1999+
def serve_ui(
2000+
self, host: str, port: int, get_registry_dump: Callable, registry_ttl_sec: int
2001+
) -> None:
19992002
"""Start the UI server locally"""
20002003
warnings.warn(
20012004
"The Feast UI is an experimental feature. "
20022005
"We do not guarantee that future changes will maintain backward compatibility.",
20032006
RuntimeWarning,
20042007
)
2005-
ui_server.start_server(self, registry_dump, self.config.project)
2008+
ui_server.start_server(
2009+
self,
2010+
host=host,
2011+
port=port,
2012+
get_registry_dump=get_registry_dump,
2013+
project_id=self.config.project,
2014+
registry_ttl_sec=registry_ttl_sec,
2015+
)
20062016

20072017
@log_exceptions_and_usage
20082018
def serve_transformations(self, port: int) -> None:

sdk/python/feast/ui/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"@elastic/datemath": "^5.0.3",
77
"@elastic/eui": "^57.0.0",
88
"@emotion/react": "^11.9.0",
9-
"@feast-dev/feast-ui": "^0.20.2",
9+
"@feast-dev/feast-ui": "^0.20.3",
1010
"@testing-library/jest-dom": "^5.16.4",
1111
"@testing-library/react": "^13.2.0",
1212
"@testing-library/user-event": "^13.5.0",

sdk/python/feast/ui_server.py

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import json
2+
import threading
3+
from typing import Callable, Optional
24

35
import pkg_resources
46
import uvicorn
@@ -9,7 +11,12 @@
911
import feast
1012

1113

12-
def get_app(store: "feast.FeatureStore", registry_dump: str, project_id: str):
14+
def get_app(
15+
store: "feast.FeatureStore",
16+
get_registry_dump: Callable,
17+
project_id: str,
18+
registry_ttl_secs: int,
19+
):
1320
ui_dir = pkg_resources.resource_filename(__name__, "ui/build/")
1421

1522
app = FastAPI()
@@ -22,9 +29,33 @@ def get_app(store: "feast.FeatureStore", registry_dump: str, project_id: str):
2229
allow_headers=["*"],
2330
)
2431

32+
# Asynchronously refresh registry, notifying shutdown and canceling the active timer if the app is shutting down
33+
registry_json = ""
34+
shutting_down = False
35+
active_timer: Optional[threading.Timer] = None
36+
37+
def async_refresh():
38+
store.refresh_registry()
39+
nonlocal registry_json
40+
registry_json = get_registry_dump(store.config, store.repo_path)
41+
if shutting_down:
42+
return
43+
nonlocal active_timer
44+
active_timer = threading.Timer(registry_ttl_secs, async_refresh)
45+
active_timer.start()
46+
47+
@app.on_event("shutdown")
48+
def shutdown_event():
49+
nonlocal shutting_down
50+
shutting_down = True
51+
if active_timer:
52+
active_timer.cancel()
53+
54+
async_refresh()
55+
2556
@app.get("/registry")
2657
def read_registry():
27-
return json.loads(registry_dump)
58+
return json.loads(registry_json)
2859

2960
# Generate projects-list json that points to the current repo's project
3061
# TODO(adchia): Enable users to also add project name + description fields in feature_store.yaml
@@ -59,6 +90,13 @@ def catch_all():
5990
return app
6091

6192

62-
def start_server(store: "feast.FeatureStore", registry_dump: str, project_id: str):
63-
app = get_app(store, registry_dump, project_id)
64-
uvicorn.run(app, host="0.0.0.0", port=8888)
93+
def start_server(
94+
store: "feast.FeatureStore",
95+
host: str,
96+
port: int,
97+
get_registry_dump: Callable,
98+
project_id: str,
99+
registry_ttl_sec: int,
100+
):
101+
app = get_app(store, get_registry_dump, project_id, registry_ttl_sec)
102+
uvicorn.run(app, host=host, port=port)

ui/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@feast-dev/feast-ui",
3-
"version": "0.20.2",
3+
"version": "0.20.3",
44
"private": false,
55
"files": [
66
"dist"

ui/src/index.tsx

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -75,13 +75,6 @@ ReactDOM.render(
7575
reactQueryClient={queryClient}
7676
feastUIConfigs={{
7777
tabsRegistry: tabsRegistry,
78-
projectListPromise: fetch("http://0.0.0.0:8888/projects-list", {
79-
headers: {
80-
"Content-Type": "application/json",
81-
},
82-
}).then((res) => {
83-
return res.json();
84-
})
8578
}}
8679
/>
8780
</React.StrictMode>,

0 commit comments

Comments
 (0)