Skip to content

Commit fd1f448

Browse files
authored
feat: Support dual-mode REST and gRPC for Feast Registry Server (feast-dev#5396)
Signed-off-by: ntkathole <[email protected]>
1 parent b486f29 commit fd1f448

File tree

4 files changed

+100
-8
lines changed

4 files changed

+100
-8
lines changed

docs/reference/feature-servers/registry-server.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,15 @@
44

55
The Registry server supports both gRPC and REST interfaces for interacting with feature metadata. While gRPC remains the default protocol—enabling clients in any language with gRPC support—the REST API allows users to interact with the registry over standard HTTP using any REST-capable tool or language.
66

7+
Feast supports running the Registry Server in three distinct modes:
8+
9+
| Mode | Command Example | Description |
10+
| ----------- | ------------------------------------------- | -------------------------------------- |
11+
| gRPC only | `feast serve_registry` | Default behavior for SDK and clients |
12+
| REST + gRPC | `feast serve_registry --rest-api` | Enables both interfaces |
13+
| REST only | `feast serve_registry --rest-api --no-grpc` | Used for REST-only clients like the UI |
14+
15+
716
## How to configure the server
817

918
## CLI
@@ -12,10 +21,16 @@ There is a CLI command that starts the Registry server: `feast serve_registry`.
1221
To start the Registry Server in TLS mode, you need to provide the private and public keys using the `--key` and `--cert` arguments.
1322
More info about TLS mode can be found in [feast-client-connecting-to-remote-registry-sever-started-in-tls-mode](../../how-to-guides/starting-feast-servers-tls-mode.md#starting-feast-registry-server-in-tls-mode)
1423

15-
To enable REST API support, start the registry server with REST mode enabled :
24+
To enable REST API support along with gRPC, start the registry server with REST mode enabled :
1625

1726
`feast serve_registry --rest-api`
1827

28+
This launches both the gRPC and REST servers concurrently. The REST server listens on port 6572 by default.
29+
30+
To run a REST-only server (no gRPC):
31+
32+
`feast serve_registry --rest-api --no-grpc`
33+
1934

2035
## How to configure the client
2136

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from feast.registry_server import RegistryServer
1616

1717
logger = logging.getLogger(__name__)
18+
logger.setLevel(logging.INFO)
1819

1920

2021
class RestRegistryServer:
@@ -28,6 +29,7 @@ def __init__(self, store: FeatureStore):
2829
dependencies=[Depends(inject_user_details)],
2930
version="1.0.0",
3031
openapi_url="/openapi.json",
32+
root_path="/api/v1",
3133
docs_url="/",
3234
redoc_url="/docs",
3335
default_status_code=status.HTTP_200_OK,
@@ -86,7 +88,7 @@ def start_server(
8688

8789
if tls_key_path and tls_cert_path:
8890
logger.info("Starting REST registry server in TLS(SSL) mode")
89-
print(f"REST registry server listening on https://localhost:{port}")
91+
logger.info(f"REST registry server listening on https://localhost:{port}")
9092
uvicorn.run(
9193
self.app,
9294
host="0.0.0.0",
@@ -95,8 +97,8 @@ def start_server(
9597
ssl_certfile=tls_cert_path,
9698
)
9799
else:
98-
print("Starting REST registry server in non-TLS(SSL) mode")
99-
print(f"REST registry server listening on http://localhost:{port}")
100+
logger.info("Starting REST registry server in non-TLS(SSL) mode")
101+
logger.info(f"REST registry server listening on http://localhost:{port}")
100102
uvicorn.run(
101103
self.app,
102104
host="0.0.0.0",

sdk/python/feast/cli/serve.py

Lines changed: 75 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
1+
import logging
2+
import multiprocessing
3+
14
import click
25

36
from feast.constants import (
47
DEFAULT_FEATURE_TRANSFORMATION_SERVER_PORT,
58
DEFAULT_OFFLINE_SERVER_PORT,
9+
DEFAULT_REGISTRY_REST_SERVER_PORT,
610
DEFAULT_REGISTRY_SERVER_PORT,
711
)
812
from feast.repo_operations import create_feature_store
913

14+
logging.basicConfig(level=logging.INFO)
15+
1016

1117
@click.command("serve")
1218
@click.option(
@@ -147,6 +153,13 @@ def serve_transformations_command(ctx: click.Context, port: int):
147153
default=DEFAULT_REGISTRY_SERVER_PORT,
148154
help="Specify a port for the server",
149155
)
156+
@click.option(
157+
"--rest-port",
158+
type=click.INT,
159+
default=DEFAULT_REGISTRY_REST_SERVER_PORT,
160+
show_default=True,
161+
help="Specify a port for the REST API server (if enabled).",
162+
)
150163
@click.option(
151164
"--key",
152165
"-k",
@@ -165,29 +178,88 @@ def serve_transformations_command(ctx: click.Context, port: int):
165178
show_default=False,
166179
help="path to TLS certificate public key. You need to pass --key as well to start server in TLS mode",
167180
)
181+
@click.option(
182+
"--grpc/--no-grpc",
183+
is_flag=True,
184+
default=True,
185+
show_default=True,
186+
help="Start a gRPC Registry Server. Enabled by default.",
187+
)
168188
@click.option(
169189
"--rest-api",
170190
"-r",
171191
is_flag=True,
192+
default=False,
172193
show_default=True,
173-
help="Start a REST API Server",
194+
help="Start a REST API Registry Server",
174195
)
175196
@click.pass_context
176197
def serve_registry_command(
177198
ctx: click.Context,
178199
port: int,
179200
tls_key_path: str,
180201
tls_cert_path: str,
202+
grpc: bool,
181203
rest_api: bool,
204+
rest_port: int,
182205
):
183-
"""Start a gRPC or REST api registry server locally on a given port (gRPC by default)."""
206+
"""Start Feast Registry server (gRPC by default, REST opt-in)."""
184207
if (tls_key_path and not tls_cert_path) or (not tls_key_path and tls_cert_path):
185208
raise click.BadParameter(
186209
"Please pass --cert and --key args to start the registry server in TLS mode."
187210
)
188211
store = create_feature_store(ctx)
189212

190-
store.serve_registry(port, tls_key_path, tls_cert_path, rest_api)
213+
if grpc and rest_api:
214+
repo_path = store.repo_path
215+
multiprocessing.set_start_method("spawn", force=True)
216+
servers = [
217+
multiprocessing.Process(
218+
target=_serve_grpc_registry,
219+
args=(repo_path, port, tls_key_path, tls_cert_path),
220+
name="grpc_registry_server",
221+
),
222+
multiprocessing.Process(
223+
target=_serve_rest_registry,
224+
args=(repo_path, rest_port, tls_key_path, tls_cert_path),
225+
name="rest_registry_server",
226+
),
227+
]
228+
logging.info("Starting Feast Registry servers (gRPC + REST)...")
229+
for p in servers:
230+
logging.info(f"Starting {p.name}")
231+
p.start()
232+
for p in servers:
233+
p.join()
234+
else:
235+
store.serve_registry(port, tls_key_path, tls_cert_path, rest_api)
236+
237+
238+
def _serve_grpc_registry(
239+
repo_path: str, port: int, tls_key_path: str, tls_cert_path: str
240+
):
241+
from feast import FeatureStore
242+
243+
store = FeatureStore(repo_path=repo_path)
244+
store.serve_registry(
245+
port=port,
246+
tls_key_path=tls_key_path,
247+
tls_cert_path=tls_cert_path,
248+
)
249+
250+
251+
def _serve_rest_registry(
252+
repo_path: str, port: int, tls_key_path: str, tls_cert_path: str
253+
):
254+
from feast import FeatureStore
255+
256+
store = FeatureStore(repo_path=repo_path)
257+
store.serve_registry(
258+
port=port,
259+
tls_key_path=tls_key_path,
260+
tls_cert_path=tls_cert_path,
261+
rest_api=True,
262+
)
191263

192264

193265
@click.command("serve_offline")

sdk/python/feast/constants.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,12 @@
3535
# Default FTS port
3636
DEFAULT_FEATURE_TRANSFORMATION_SERVER_PORT = 6569
3737

38-
# Default registry server port
38+
# Default registry server grpc port
3939
DEFAULT_REGISTRY_SERVER_PORT = 6570
4040

41+
# Default registry server REST port
42+
DEFAULT_REGISTRY_REST_SERVER_PORT = 6572
43+
4144
# Default offline server port
4245
DEFAULT_OFFLINE_SERVER_PORT = 8815
4346

0 commit comments

Comments
 (0)