prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  gRPC: Received message larger than max (11059277 vs. 4194304)
  We are using mlserver (gRPC) for serving custom PyTorch ML Model and are sending images of size 640x640x3 . But because of the size we are getting the below error:
  
  > status = StatusCode.RESOURCE_EXHAUSTED
  > 	details = "Received message larger than max (11059277 vs. 4194304)"
  > 	debug_error_string = "{"created":"@1630074151.799330000","description":"Error received from peer ipv4:0.0.0.0:28081","file":"src/core/lib/surface/call.cc","file_line":1070,"grpc_message":"Received message larger than max (11059277 vs. 4194304)","grpc_status":8}"
  
  Setting the following on client side did not work:
  ```
  maxMsgLength = 20 * 1024 * 1024
  ch_options = [('grpc.max_message_length', maxMsgLength),
                ('grpc.max_send_message_length', maxMsgLength),
                ('grpc.max_receive_message_length', maxMsgLength)]
  channel = grpc.insecure_channel(f'{host}:{port}', options=ch_options)
  
  ```
  However setting it on server side worked:
  ```
  def _create_server(self):
          self._inference_servicer = InferenceServicer(self._data_plane)
          self._model_repository_servicer = ModelRepositoryServicer(
              self._model_repository_handlers
          )
          channel_opt = [('grpc.max_send_message_length', 20 * 1024 * 1024), ('grpc.max_receive_message_length', 20 * 1024 * 1024)]
          self._server = aio.server(
              ThreadPoolExecutor(max_workers=self._settings.grpc_workers), options = channel_opt
          )
  
          add_GRPCInferenceServiceServicer_to_server(
              self._inference_servicer, self._server
          )
  
  ```
  But this is not a proper solution, since our mlserver runs in a docker and does a pip install.
  This should be part of the server config in MLServer.
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `mlserver/grpc/server.py`
  Content:
  ```
  1 from grpc import aio
  2 from concurrent.futures import ThreadPoolExecutor
  3 
  4 from ..handlers import DataPlane, ModelRepositoryHandlers
  5 from ..settings import Settings
  6 
  7 from .servicers import InferenceServicer, ModelRepositoryServicer
  8 from .dataplane_pb2_grpc import add_GRPCInferenceServiceServicer_to_server
  9 from .model_repository_pb2_grpc import add_ModelRepositoryServiceServicer_to_server
  10 
  11 
  12 class GRPCServer:
  13     def __init__(
  14         self,
  15         settings: Settings,
  16         data_plane: DataPlane,
  17         model_repository_handlers: ModelRepositoryHandlers,
  18     ):
  19         self._settings = settings
  20         self._data_plane = data_plane
  21         self._model_repository_handlers = model_repository_handlers
  22 
  23     def _create_server(self):
  24         self._inference_servicer = InferenceServicer(self._data_plane)
  25         self._model_repository_servicer = ModelRepositoryServicer(
  26             self._model_repository_handlers
  27         )
  28         self._server = aio.server(
  29             ThreadPoolExecutor(max_workers=self._settings.grpc_workers)
  30         )
  31 
  32         add_GRPCInferenceServiceServicer_to_server(
  33             self._inference_servicer, self._server
  34         )
  35         add_ModelRepositoryServiceServicer_to_server(
  36             self._model_repository_servicer, self._server
  37         )
  38 
  39         self._server.add_insecure_port(
  40             f"{self._settings.host}:{self._settings.grpc_port}"
  41         )
  42 
  43         return self._server
  44 
  45     async def start(self):
  46         self._create_server()
  47 
  48         await self._server.start()
  49         await self._server.wait_for_termination()
  50 
  51     async def stop(self):
  52         # TODO: Read from config
  53         await self._server.stop(grace=5)
  54
  ```
  
  Path: `mlserver/settings.py`
  Content:
  ```
  1 from typing import List, Optional
  2 from pydantic import BaseSettings, PyObject
  3 
  4 from .version import __version__
  5 from .types import MetadataTensor
  6 
  7 ENV_PREFIX_SETTINGS = "MLSERVER_"
  8 ENV_PREFIX_MODEL_SETTINGS = "MLSERVER_MODEL_"
  9 
  10 
  11 class Settings(BaseSettings):
  12     class Config:
  13         env_prefix = ENV_PREFIX_SETTINGS
  14 
  15     debug: bool = True
  16 
  17     # Model repository folder
  18     model_repository_root: str = "."
  19     load_models_at_startup: bool = True
  20 
  21     # Server metadata
  22     server_name: str = "mlserver"
  23     server_version: str = __version__
  24     extensions: List[str] = []
  25 
  26     # Server settings
  27     host: str = "0.0.0.0"
  28     http_port: int = 8080
  29     grpc_port: int = 8081
  30     grpc_workers: int = 10
  31 
  32 
  33 class ModelParameters(BaseSettings):
  34     """
  35     Parameters that apply only to a particular instance of a model.
  36     This can include things like model weights.
  37     The main difference with respect to ModelSettings is that parameters can
  38     change on each instance (e.g. each version) of the model.
  39     """
  40 
  41     class Config:
  42         env_prefix = ENV_PREFIX_MODEL_SETTINGS
  43 
  44     uri: Optional[str] = None
  45     version: Optional[str] = None
  46     format: Optional[str] = None
  47     extra: Optional[dict] = {}
  48 
  49 
  50 class ModelSettings(BaseSettings):
  51     class Config:
  52         env_prefix = ENV_PREFIX_MODEL_SETTINGS
  53 
  54     name: str = ""
  55 
  56     # Model metadata
  57     platform: str = ""
  58     versions: Optional[List[str]] = []
  59     inputs: Optional[List[MetadataTensor]] = []
  60     outputs: Optional[List[MetadataTensor]] = []
  61 
  62     # Parallel settings
  63     parallel_workers: Optional[int] = 4
  64 
  65     # Custom model class implementation
  66     implementation: PyObject = "mlserver.model.MLModel"  # type: ignore
  67 
  68     # Model parameters are meant to be set directly by the MLServer runtime.
  69     # However, it's also possible to override them manually.
  70     parameters: Optional[ModelParameters] = None
  71
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/mlserver/grpc/server.py b/mlserver/grpc/server.py
  --- a/mlserver/grpc/server.py
  +++ b/mlserver/grpc/server.py
  @@ -1,5 +1,6 @@
   from grpc import aio
   from concurrent.futures import ThreadPoolExecutor
  +from typing import Any, List, Tuple
   
   from ..handlers import DataPlane, ModelRepositoryHandlers
   from ..settings import Settings
  @@ -25,8 +26,10 @@
           self._model_repository_servicer = ModelRepositoryServicer(
               self._model_repository_handlers
           )
  +
           self._server = aio.server(
  -            ThreadPoolExecutor(max_workers=self._settings.grpc_workers)
  +            ThreadPoolExecutor(max_workers=self._settings.grpc_workers),
  +            options=self._get_options(),
           )
   
           add_GRPCInferenceServiceServicer_to_server(
  @@ -42,6 +45,19 @@
   
           return self._server
   
  +    def _get_options(self) -> List[Tuple[str, Any]]:
  +        options = []
  +
  +        max_message_length = self._settings.grpc_max_message_length
  +        if max_message_length is not None:
  +            options += [
  +                ("grpc.max_message_length", max_message_length),
  +                ("grpc.max_send_message_length", max_message_length),
  +                ("grpc.max_receive_message_length", max_message_length),
  +            ]
  +
  +        return options
  +
       async def start(self):
           self._create_server()
   
  diff --git a/mlserver/settings.py b/mlserver/settings.py
  --- a/mlserver/settings.py
  +++ b/mlserver/settings.py
  @@ -28,6 +28,7 @@
       http_port: int = 8080
       grpc_port: int = 8081
       grpc_workers: int = 10
  +    grpc_max_message_length: Optional[int] = None
   
   
   class ModelParameters(BaseSettings):
  
verification_info: {"golden_diff": "diff --git a/mlserver/grpc/server.py b/mlserver/grpc/server.py\n--- a/mlserver/grpc/server.py\n+++ b/mlserver/grpc/server.py\n@@ -1,5 +1,6 @@\n from grpc import aio\n from concurrent.futures import ThreadPoolExecutor\n+from typing import Any, List, Tuple\n \n from ..handlers import DataPlane, ModelRepositoryHandlers\n from ..settings import Settings\n@@ -25,8 +26,10 @@\n         self._model_repository_servicer = ModelRepositoryServicer(\n             self._model_repository_handlers\n         )\n+\n         self._server = aio.server(\n-            ThreadPoolExecutor(max_workers=self._settings.grpc_workers)\n+            ThreadPoolExecutor(max_workers=self._settings.grpc_workers),\n+            options=self._get_options(),\n         )\n \n         add_GRPCInferenceServiceServicer_to_server(\n@@ -42,6 +45,19 @@\n \n         return self._server\n \n+    def _get_options(self) -> List[Tuple[str, Any]]:\n+        options = []\n+\n+        max_message_length = self._settings.grpc_max_message_length\n+        if max_message_length is not None:\n+            options += [\n+                (\"grpc.max_message_length\", max_message_length),\n+                (\"grpc.max_send_message_length\", max_message_length),\n+                (\"grpc.max_receive_message_length\", max_message_length),\n+            ]\n+\n+        return options\n+\n     async def start(self):\n         self._create_server()\n \ndiff --git a/mlserver/settings.py b/mlserver/settings.py\n--- a/mlserver/settings.py\n+++ b/mlserver/settings.py\n@@ -28,6 +28,7 @@\n     http_port: int = 8080\n     grpc_port: int = 8081\n     grpc_workers: int = 10\n+    grpc_max_message_length: Optional[int] = None\n \n \n class ModelParameters(BaseSettings):\n", "issue": "gRPC: Received message larger than max (11059277 vs. 4194304)\nWe are using mlserver (gRPC) for serving custom PyTorch ML Model and are sending images of size 640x640x3 . But because of the size we are getting the below error:\r\n\r\n> status = StatusCode.RESOURCE_EXHAUSTED\r\n> \tdetails = \"Received message larger than max (11059277 vs. 4194304)\"\r\n> \tdebug_error_string = \"{\"created\":\"@1630074151.799330000\",\"description\":\"Error received from peer ipv4:0.0.0.0:28081\",\"file\":\"src/core/lib/surface/call.cc\",\"file_line\":1070,\"grpc_message\":\"Received message larger than max (11059277 vs. 4194304)\",\"grpc_status\":8}\"\r\n\r\nSetting the following on client side did not work:\r\n```\r\nmaxMsgLength = 20 * 1024 * 1024\r\nch_options = [('grpc.max_message_length', maxMsgLength),\r\n              ('grpc.max_send_message_length', maxMsgLength),\r\n              ('grpc.max_receive_message_length', maxMsgLength)]\r\nchannel = grpc.insecure_channel(f'{host}:{port}', options=ch_options)\r\n\r\n```\r\nHowever setting it on server side worked:\r\n```\r\ndef _create_server(self):\r\n        self._inference_servicer = InferenceServicer(self._data_plane)\r\n        self._model_repository_servicer = ModelRepositoryServicer(\r\n            self._model_repository_handlers\r\n        )\r\n        channel_opt = [('grpc.max_send_message_length', 20 * 1024 * 1024), ('grpc.max_receive_message_length', 20 * 1024 * 1024)]\r\n        self._server = aio.server(\r\n            ThreadPoolExecutor(max_workers=self._settings.grpc_workers), options = channel_opt\r\n        )\r\n\r\n        add_GRPCInferenceServiceServicer_to_server(\r\n            self._inference_servicer, self._server\r\n        )\r\n\r\n```\r\nBut this is not a proper solution, since our mlserver runs in a docker and does a pip install.\r\nThis should be part of the server config in MLServer.\n", "before_files": [{"content": "from grpc import aio\nfrom concurrent.futures import ThreadPoolExecutor\n\nfrom ..handlers import DataPlane, ModelRepositoryHandlers\nfrom ..settings import Settings\n\nfrom .servicers import InferenceServicer, ModelRepositoryServicer\nfrom .dataplane_pb2_grpc import add_GRPCInferenceServiceServicer_to_server\nfrom .model_repository_pb2_grpc import add_ModelRepositoryServiceServicer_to_server\n\n\nclass GRPCServer:\n    def __init__(\n        self,\n        settings: Settings,\n        data_plane: DataPlane,\n        model_repository_handlers: ModelRepositoryHandlers,\n    ):\n        self._settings = settings\n        self._data_plane = data_plane\n        self._model_repository_handlers = model_repository_handlers\n\n    def _create_server(self):\n        self._inference_servicer = InferenceServicer(self._data_plane)\n        self._model_repository_servicer = ModelRepositoryServicer(\n            self._model_repository_handlers\n        )\n        self._server = aio.server(\n            ThreadPoolExecutor(max_workers=self._settings.grpc_workers)\n        )\n\n        add_GRPCInferenceServiceServicer_to_server(\n            self._inference_servicer, self._server\n        )\n        add_ModelRepositoryServiceServicer_to_server(\n            self._model_repository_servicer, self._server\n        )\n\n        self._server.add_insecure_port(\n            f\"{self._settings.host}:{self._settings.grpc_port}\"\n        )\n\n        return self._server\n\n    async def start(self):\n        self._create_server()\n\n        await self._server.start()\n        await self._server.wait_for_termination()\n\n    async def stop(self):\n        # TODO: Read from config\n        await self._server.stop(grace=5)\n", "path": "mlserver/grpc/server.py"}, {"content": "from typing import List, Optional\nfrom pydantic import BaseSettings, PyObject\n\nfrom .version import __version__\nfrom .types import MetadataTensor\n\nENV_PREFIX_SETTINGS = \"MLSERVER_\"\nENV_PREFIX_MODEL_SETTINGS = \"MLSERVER_MODEL_\"\n\n\nclass Settings(BaseSettings):\n    class Config:\n        env_prefix = ENV_PREFIX_SETTINGS\n\n    debug: bool = True\n\n    # Model repository folder\n    model_repository_root: str = \".\"\n    load_models_at_startup: bool = True\n\n    # Server metadata\n    server_name: str = \"mlserver\"\n    server_version: str = __version__\n    extensions: List[str] = []\n\n    # Server settings\n    host: str = \"0.0.0.0\"\n    http_port: int = 8080\n    grpc_port: int = 8081\n    grpc_workers: int = 10\n\n\nclass ModelParameters(BaseSettings):\n    \"\"\"\n    Parameters that apply only to a particular instance of a model.\n    This can include things like model weights.\n    The main difference with respect to ModelSettings is that parameters can\n    change on each instance (e.g. each version) of the model.\n    \"\"\"\n\n    class Config:\n        env_prefix = ENV_PREFIX_MODEL_SETTINGS\n\n    uri: Optional[str] = None\n    version: Optional[str] = None\n    format: Optional[str] = None\n    extra: Optional[dict] = {}\n\n\nclass ModelSettings(BaseSettings):\n    class Config:\n        env_prefix = ENV_PREFIX_MODEL_SETTINGS\n\n    name: str = \"\"\n\n    # Model metadata\n    platform: str = \"\"\n    versions: Optional[List[str]] = []\n    inputs: Optional[List[MetadataTensor]] = []\n    outputs: Optional[List[MetadataTensor]] = []\n\n    # Parallel settings\n    parallel_workers: Optional[int] = 4\n\n    # Custom model class implementation\n    implementation: PyObject = \"mlserver.model.MLModel\"  # type: ignore\n\n    # Model parameters are meant to be set directly by the MLServer runtime.\n    # However, it's also possible to override them manually.\n    parameters: Optional[ModelParameters] = None\n", "path": "mlserver/settings.py"}], "after_files": [{"content": "from grpc import aio\nfrom concurrent.futures import ThreadPoolExecutor\nfrom typing import Any, List, Tuple\n\nfrom ..handlers import DataPlane, ModelRepositoryHandlers\nfrom ..settings import Settings\n\nfrom .servicers import InferenceServicer, ModelRepositoryServicer\nfrom .dataplane_pb2_grpc import add_GRPCInferenceServiceServicer_to_server\nfrom .model_repository_pb2_grpc import add_ModelRepositoryServiceServicer_to_server\n\n\nclass GRPCServer:\n    def __init__(\n        self,\n        settings: Settings,\n        data_plane: DataPlane,\n        model_repository_handlers: ModelRepositoryHandlers,\n    ):\n        self._settings = settings\n        self._data_plane = data_plane\n        self._model_repository_handlers = model_repository_handlers\n\n    def _create_server(self):\n        self._inference_servicer = InferenceServicer(self._data_plane)\n        self._model_repository_servicer = ModelRepositoryServicer(\n            self._model_repository_handlers\n        )\n\n        self._server = aio.server(\n            ThreadPoolExecutor(max_workers=self._settings.grpc_workers),\n            options=self._get_options(),\n        )\n\n        add_GRPCInferenceServiceServicer_to_server(\n            self._inference_servicer, self._server\n        )\n        add_ModelRepositoryServiceServicer_to_server(\n            self._model_repository_servicer, self._server\n        )\n\n        self._server.add_insecure_port(\n            f\"{self._settings.host}:{self._settings.grpc_port}\"\n        )\n\n        return self._server\n\n    def _get_options(self) -> List[Tuple[str, Any]]:\n        options = []\n\n        max_message_length = self._settings.grpc_max_message_length\n        if max_message_length is not None:\n            options += [\n                (\"grpc.max_message_length\", max_message_length),\n                (\"grpc.max_send_message_length\", max_message_length),\n                (\"grpc.max_receive_message_length\", max_message_length),\n            ]\n\n        return options\n\n    async def start(self):\n        self._create_server()\n\n        await self._server.start()\n        await self._server.wait_for_termination()\n\n    async def stop(self):\n        # TODO: Read from config\n        await self._server.stop(grace=5)\n", "path": "mlserver/grpc/server.py"}, {"content": "from typing import List, Optional\nfrom pydantic import BaseSettings, PyObject\n\nfrom .version import __version__\nfrom .types import MetadataTensor\n\nENV_PREFIX_SETTINGS = \"MLSERVER_\"\nENV_PREFIX_MODEL_SETTINGS = \"MLSERVER_MODEL_\"\n\n\nclass Settings(BaseSettings):\n    class Config:\n        env_prefix = ENV_PREFIX_SETTINGS\n\n    debug: bool = True\n\n    # Model repository folder\n    model_repository_root: str = \".\"\n    load_models_at_startup: bool = True\n\n    # Server metadata\n    server_name: str = \"mlserver\"\n    server_version: str = __version__\n    extensions: List[str] = []\n\n    # Server settings\n    host: str = \"0.0.0.0\"\n    http_port: int = 8080\n    grpc_port: int = 8081\n    grpc_workers: int = 10\n    grpc_max_message_length: Optional[int] = None\n\n\nclass ModelParameters(BaseSettings):\n    \"\"\"\n    Parameters that apply only to a particular instance of a model.\n    This can include things like model weights.\n    The main difference with respect to ModelSettings is that parameters can\n    change on each instance (e.g. each version) of the model.\n    \"\"\"\n\n    class Config:\n        env_prefix = ENV_PREFIX_MODEL_SETTINGS\n\n    uri: Optional[str] = None\n    version: Optional[str] = None\n    format: Optional[str] = None\n    extra: Optional[dict] = {}\n\n\nclass ModelSettings(BaseSettings):\n    class Config:\n        env_prefix = ENV_PREFIX_MODEL_SETTINGS\n\n    name: str = \"\"\n\n    # Model metadata\n    platform: str = \"\"\n    versions: Optional[List[str]] = []\n    inputs: Optional[List[MetadataTensor]] = []\n    outputs: Optional[List[MetadataTensor]] = []\n\n    # Parallel settings\n    parallel_workers: Optional[int] = 4\n\n    # Custom model class implementation\n    implementation: PyObject = \"mlserver.model.MLModel\"  # type: ignore\n\n    # Model parameters are meant to be set directly by the MLServer runtime.\n    # However, it's also possible to override them manually.\n    parameters: Optional[ModelParameters] = None\n", "path": "mlserver/settings.py"}]}