GeezerVison is showing github.com — feat: Add gunicorn for serve with multiprocess (#3636) · ExpediaGroup/feast@4de7faf
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: sdk/python/feast/cli.py +26-1Lines changed: 26 additions & 1 deletion | Original file line number | Diff line number | Diff line change |
|---|
@@ -649,6 +649,21 @@ def init_command(project_directory, minimal: bool, template: str): | 649 | 649 | show_default=True, | 650 | 650 | help="Disable logging served features", | 651 | 651 | ) | | 652 | +@click.option( | | 653 | + "--workers", | | 654 | + "-w", | | 655 | + type=click.INT, | | 656 | + default=1, | | 657 | + show_default=True, | | 658 | + help="Number of worker", | | 659 | +) | | 660 | +@click.option( | | 661 | + "--keep-alive-timeout", | | 662 | + type=click.INT, | | 663 | + default=5, | | 664 | + show_default=True, | | 665 | + help="Timeout for keep alive", | | 666 | +) | 652 | 667 | @click.pass_context | 653 | 668 | def serve_command( | 654 | 669 | ctx: click.Context, | @@ -657,11 +672,21 @@ def serve_command( | 657 | 672 | type_: str, | 658 | 673 | no_access_log: bool, | 659 | 674 | no_feature_log: bool, | | 675 | + workers: int, | | 676 | + keep_alive_timeout: int, | 660 | 677 | ): | 661 | 678 | """Start a feature server locally on a given port.""" | 662 | 679 | store = create_feature_store(ctx) | 663 | 680 |
| 664 | | - store.serve(host, port, type_, no_access_log, no_feature_log) | | 681 | + store.serve( | | 682 | + host=host, | | 683 | + port=port, | | 684 | + type_=type_, | | 685 | + no_access_log=no_access_log, | | 686 | + no_feature_log=no_feature_log, | | 687 | + workers=workers, | | 688 | + keep_alive_timeout=keep_alive_timeout, | | 689 | + ) | 665 | 690 |
| 666 | 691 |
| 667 | 692 | @cli.command("serve_transformations") | |
Collapse file Copy file name to clipboardExpand all lines: sdk/python/feast/feature_server.py +31-4Lines changed: 31 additions & 4 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 2 | 2 | import traceback | 3 | 3 | import warnings | 4 | 4 |
| | 5 | +import gunicorn.app.base | 5 | 6 | import pandas as pd | 6 | | -import uvicorn | 7 | 7 | from fastapi import FastAPI, HTTPException, Request, Response, status | 8 | 8 | from fastapi.logger import logger | 9 | 9 | from fastapi.params import Depends | @@ -137,8 +137,35 @@ def health(): | 137 | 137 | return app | 138 | 138 |
| 139 | 139 |
| | 140 | +class FeastServeApplication(gunicorn.app.base.BaseApplication): | | 141 | + def __init__(self, store: "feast.FeatureStore", **options): | | 142 | + self._app = get_app(store=store) | | 143 | + self._options = options | | 144 | + super().__init__() | | 145 | + | | 146 | + def load_config(self): | | 147 | + for key, value in self._options.items(): | | 148 | + if key.lower() in self.cfg.settings and value is not None: | | 149 | + self.cfg.set(key.lower(), value) | | 150 | + | | 151 | + self.cfg.set("worker_class", "uvicorn.workers.UvicornWorker") | | 152 | + | | 153 | + def load(self): | | 154 | + return self._app | | 155 | + | | 156 | + | 140 | 157 | def start_server( | 141 | | - store: "feast.FeatureStore", host: str, port: int, no_access_log: bool | | 158 | + store: "feast.FeatureStore", | | 159 | + host: str, | | 160 | + port: int, | | 161 | + no_access_log: bool, | | 162 | + workers: int, | | 163 | + keep_alive_timeout: int, | 142 | 164 | ): | 143 | | - app = get_app(store) | 144 | | - uvicorn.run(app, host=host, port=port, access_log=(not no_access_log)) | | 165 | + FeastServeApplication( | | 166 | + store=store, | | 167 | + bind=f"{host}:{port}", | | 168 | + accesslog=None if no_access_log else "-", | | 169 | + workers=workers, | | 170 | + keepalive=keep_alive_timeout, | | 171 | + ).run() |
Collapse file Copy file name to clipboardExpand all lines: sdk/python/feast/feature_store.py +10-2Lines changed: 10 additions & 2 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -2152,7 +2152,6 @@ def _get_feature_views_to_use( | 2152 | 2152 | allow_cache=False, | 2153 | 2153 | hide_dummy_entity: bool = True, | 2154 | 2154 | ) -> Tuple[List[FeatureView], List[RequestFeatureView], List[OnDemandFeatureView]]: | 2155 | | - | 2156 | 2155 | fvs = { | 2157 | 2156 | fv.name: fv | 2158 | 2157 | for fv in [ | @@ -2223,6 +2222,8 @@ def serve( | 2223 | 2222 | type_: str, | 2224 | 2223 | no_access_log: bool, | 2225 | 2224 | no_feature_log: bool, | | 2225 | + workers: int, | | 2226 | + keep_alive_timeout: int, | 2226 | 2227 | ) -> None: | 2227 | 2228 | """Start the feature consumption server locally on a given port.""" | 2228 | 2229 | type_ = type_.lower() | @@ -2231,7 +2232,14 @@ def serve( | 2231 | 2232 | f"Python server only supports 'http'. Got '{type_}' instead." | 2232 | 2233 | ) | 2233 | 2234 | # Start the python server | 2234 | | - feature_server.start_server(self, host, port, no_access_log) | | 2235 | + feature_server.start_server( | | 2236 | + self, | | 2237 | + host=host, | | 2238 | + port=port, | | 2239 | + no_access_log=no_access_log, | | 2240 | + workers=workers, | | 2241 | + keep_alive_timeout=keep_alive_timeout, | | 2242 | + ) | 2235 | 2243 |
| 2236 | 2244 | @log_exceptions_and_usage | 2237 | 2245 | def get_feature_server_endpoint(self) -> Optional[str]: | |
Collapse file Copy file name to clipboardExpand all lines: sdk/python/requirements/py3.10-ci-requirements.txt +2Lines changed: 2 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -326,6 +326,8 @@ grpcio-testing==1.54.2 | 326 | 326 | # via feast (setup.py) | 327 | 327 | grpcio-tools==1.54.2 | 328 | 328 | # via feast (setup.py) | | 329 | +gunicorn==20.1.0 | | 330 | + # via feast (setup.py) | 329 | 331 | h11==0.14.0 | 330 | 332 | # via | 331 | 333 | # httpcore | |
Collapse file Copy file name to clipboardExpand all lines: sdk/python/requirements/py3.10-requirements.txt +2Lines changed: 2 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -59,6 +59,8 @@ grpcio==1.54.2 | 59 | 59 | # grpcio-reflection | 60 | 60 | grpcio-reflection==1.54.2 | 61 | 61 | # via feast (setup.py) | | 62 | +gunicorn==20.1.0 | | 63 | + # via feast (setup.py) | 62 | 64 | h11==0.14.0 | 63 | 65 | # via | 64 | 66 | # httpcore | |
Collapse file Copy file name to clipboardExpand all lines: sdk/python/requirements/py3.8-ci-requirements.txt +2Lines changed: 2 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -330,6 +330,8 @@ grpcio-testing==1.54.2 | 330 | 330 | # via feast (setup.py) | 331 | 331 | grpcio-tools==1.54.2 | 332 | 332 | # via feast (setup.py) | | 333 | +gunicorn==20.1.0 | | 334 | + # via feast (setup.py) | 333 | 335 | h11==0.14.0 | 334 | 336 | # via | 335 | 337 | # httpcore | |
Collapse file Copy file name to clipboardExpand all lines: sdk/python/requirements/py3.8-requirements.txt +2Lines changed: 2 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -59,6 +59,8 @@ grpcio==1.54.2 | 59 | 59 | # grpcio-reflection | 60 | 60 | grpcio-reflection==1.54.2 | 61 | 61 | # via feast (setup.py) | | 62 | +gunicorn==20.1.0 | | 63 | + # via feast (setup.py) | 62 | 64 | h11==0.14.0 | 63 | 65 | # via | 64 | 66 | # httpcore | |
Collapse file Copy file name to clipboardExpand all lines: sdk/python/requirements/py3.9-ci-requirements.txt +2Lines changed: 2 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -326,6 +326,8 @@ grpcio-testing==1.54.2 | 326 | 326 | # via feast (setup.py) | 327 | 327 | grpcio-tools==1.54.2 | 328 | 328 | # via feast (setup.py) | | 329 | +gunicorn==20.1.0 | | 330 | + # via feast (setup.py) | 329 | 331 | h11==0.14.0 | 330 | 332 | # via | 331 | 333 | # httpcore | |
Collapse file Copy file name to clipboardExpand all lines: sdk/python/requirements/py3.9-requirements.txt +2Lines changed: 2 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -59,6 +59,8 @@ grpcio==1.54.2 | 59 | 59 | # grpcio-reflection | 60 | 60 | grpcio-reflection==1.54.2 | 61 | 61 | # via feast (setup.py) | | 62 | +gunicorn==20.1.0 | | 63 | + # via feast (setup.py) | 62 | 64 | h11==0.14.0 | 63 | 65 | # via | 64 | 66 | # httpcore | |
Collapse file Copy file name to clipboardExpand all lines: setup.py +2-3Lines changed: 2 additions & 3 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 70 | 70 | "typeguard==2.13.3", | 71 | 71 | "fastapi>=0.68.0,<1", | 72 | 72 | "uvicorn[standard]>=0.14.0,<1", | | 73 | + "gunicorn", | 73 | 74 | "dask>=2021.1.0", | 74 | 75 | "bowler", # Needed for automatic repo upgrades | 75 | 76 | # FastAPI does not correctly pull starlette dependency on httpx see thread(https://github.com/tiangolo/fastapi/issues/5656). | | 103 | 104 | "pyspark>=3.0.0,<4", | 104 | 105 | ] | 105 | 106 |
| 106 | | -TRINO_REQUIRED = [ | 107 | | - "trino>=0.305.0,<0.400.0", "regex" | 108 | | -] | | 107 | +TRINO_REQUIRED = ["trino>=0.305.0,<0.400.0", "regex"] | 109 | 108 |
| 110 | 109 | POSTGRES_REQUIRED = [ | 111 | 110 | "psycopg2-binary>=2.8.3,<3", | |
|