Jina 3.10.1 Update

Abstract tech-themed banner with "3.10.1 NEW RELEASE" text beside a stylized CPU under a magnifying glass on a black backdrop

Jina is a MLOps framework that empowers anyone to build cross-modal and multi-modal applications on the cloud. It uplifts a PoC into a production-ready service. Jina handles the infrastructure complexity, making advanced solution engineering and cloud-native technologies accessible to every developer.

Release ๐Ÿ’ซ Patch v3.10.1 ยท jina-ai/jina
Release Note (3.10.1) Release time: 2022-10-06 15:05:48 This release contains 1 new feature, 1 refactor, 3 bug fixes and 7 documentation improvements.๐Ÿ†• FeaturesDistributed replicas across diffe...
Full release note with links to pull requests

Release Note (3.10.1)

Release time: 2022-10-06 15:05:48

This release contains 1 new feature, 1 refactor, 3 bug fixes and 7 documentation improvements.

๐Ÿ†• Features

Distributed replicas across different hosts (#5217)

You can now start different Executors on different machines and use them as replicas by passing their addresses in the host parameter as a list, together with the external flag set to True:

f.add(host='localhost:12345,91.198.174.192:12346', external=True)

The Flow ensures the requests are load balanced between Executor instances.

โš™ Refactoring

All code related to hubble and communication with Jina Hub has been moved to the jina-hubble-sdk repo and has been added as an external dependency.

๐Ÿž Bug Fixes

Disable rich traceback in logging (#5242)

We disabled the use of rich logging when printing tracebacks for Exceptions. This makes it easier to debug issues as the lines are not broken.

Previously, an Exception would be reported to the console as:


ERROR  executor0/rep-0@183551 ZeroDivisionError('division by [10/05/22 11:26:23]
       zero')                                                                   
        add "--quiet-error" to suppress the exception                           
       details                                                                  
       โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ Traceback (most recent call last) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ                    
       โ”‚ /home/xxx/Documents/workspace/Jina/jina/jina/seโ€ฆ โ”‚                    
       โ”‚ in process_data                                   โ”‚                    
       โ”‚                                                   โ”‚                    
       โ”‚   179 โ”‚   โ”‚   โ”‚   โ”‚   if self.logger.debug_enable โ”‚                    
       โ”‚   180 โ”‚   โ”‚   โ”‚   โ”‚   โ”‚   self._log_data_request( โ”‚                    
       โ”‚   181 โ”‚   โ”‚   โ”‚   โ”‚                               โ”‚                    
       โ”‚ โฑ 182 โ”‚   โ”‚   โ”‚   โ”‚   result = await self._data_r โ”‚                    
       โ”‚   183 โ”‚   โ”‚   โ”‚   โ”‚   if self._successful_request โ”‚                    
       โ”‚   184 โ”‚   โ”‚   โ”‚   โ”‚   โ”‚   self._successful_reques โ”‚                    
       โ”‚   185 โ”‚   โ”‚   โ”‚   โ”‚   return result               โ”‚                    
       โ”‚                                                   โ”‚                    
       โ”‚ /home/xxx/Documents/workspace/Jina/jina/jina/seโ€ฆ โ”‚                    
       โ”‚ in handle                                         โ”‚                    
       โ”‚                                                   โ”‚                    
       โ”‚   161 โ”‚   โ”‚   )                                   โ”‚                    
       โ”‚   162 โ”‚   โ”‚                                       โ”‚                    
       โ”‚   163 โ”‚   โ”‚   # executor logic                    โ”‚                    
       โ”‚ โฑ 164 โ”‚   โ”‚   return_data = await self._executor. โ”‚                    
       โ”‚   165 โ”‚   โ”‚   โ”‚   req_endpoint=requests[0].header โ”‚                    
       โ”‚   166 โ”‚   โ”‚   โ”‚   docs=docs,                      โ”‚                    
       โ”‚   167 โ”‚   โ”‚   โ”‚   parameters=params,              โ”‚                    
       โ”‚                                                   โ”‚                    
       โ”‚ /home/xxx/Documents/workspace/Jina/jina/jina/seโ€ฆ โ”‚                    
       โ”‚ in __acall__                                      โ”‚                    
       โ”‚                                                   โ”‚                    
       โ”‚   290 โ”‚   โ”‚   if req_endpoint in self.requests:   โ”‚                    
       โ”‚   291 โ”‚   โ”‚   โ”‚   return await self.__acall_endpo โ”‚                    
       โ”‚   292 โ”‚   โ”‚   elif __default_endpoint__ in self.r โ”‚                    
       โ”‚ โฑ 293 โ”‚   โ”‚   โ”‚   return await self.__acall_endpo โ”‚                    
       โ”‚   294 โ”‚                                           โ”‚                    
       โ”‚   295 โ”‚   async def __acall_endpoint__(self, req_ โ”‚                    
       โ”‚   296 โ”‚   โ”‚   func = self.requests[req_endpoint]  โ”‚                    
       โ”‚                                                   โ”‚                    
       โ”‚ /home/xxx/Documents/workspace/Jina/jina/jina/seโ€ฆ โ”‚                    
       โ”‚ in __acall_endpoint__                             โ”‚                    
       โ”‚                                                   โ”‚                    
       โ”‚   311 โ”‚   โ”‚   โ”‚   if iscoroutinefunction(func):   โ”‚                    
       โ”‚   312 โ”‚   โ”‚   โ”‚   โ”‚   return await func(self, **k โ”‚                    
       โ”‚   313 โ”‚   โ”‚   โ”‚   else:                           โ”‚                    
       โ”‚ โฑ 314 โ”‚   โ”‚   โ”‚   โ”‚   return func(self, **kwargs) โ”‚                    
       โ”‚   315 โ”‚                                           โ”‚                    
       โ”‚   316 โ”‚   @property                               โ”‚                    
       โ”‚   317 โ”‚   def workspace(self) -> Optional[str]:   โ”‚                    
       โ”‚                                                   โ”‚                    
       โ”‚ /home/xxx/Documents/workspace/Jina/jina/jina/seโ€ฆ โ”‚                    
       โ”‚ in arg_wrapper                                    โ”‚                    
       โ”‚                                                   โ”‚                    
       โ”‚   159 โ”‚   โ”‚   โ”‚   โ”‚   def arg_wrapper(            โ”‚                    
       โ”‚   160 โ”‚   โ”‚   โ”‚   โ”‚   โ”‚   executor_instance, *arg โ”‚                    
       โ”‚   161 โ”‚   โ”‚   โ”‚   โ”‚   ):  # we need to get the su โ”‚                    
       โ”‚       the self                                    โ”‚                    
       โ”‚ โฑ 162 โ”‚   โ”‚   โ”‚   โ”‚   โ”‚   return fn(executor_inst โ”‚                    
       โ”‚   163 โ”‚   โ”‚   โ”‚   โ”‚                               โ”‚                    
       โ”‚   164 โ”‚   โ”‚   โ”‚   โ”‚   self.fn = arg_wrapper       โ”‚                    
       โ”‚   165                                             โ”‚                    
       โ”‚                                                   โ”‚                    
       โ”‚ /home/USER/.config/JetBrains/PyCharmCE2022.2/scrโ€ฆ โ”‚                    
       โ”‚ in foo                                            โ”‚                    
       โ”‚                                                   โ”‚                    
       โ”‚   12 โ”‚                                            โ”‚                    
       โ”‚   13 โ”‚   @requests                                โ”‚                    
       โ”‚   14 โ”‚   def foo(self, docs, **kwargs):           โ”‚                    
       โ”‚ โฑ 15 โ”‚   โ”‚   1/0                                  โ”‚                    
       โ”‚   16                                              โ”‚                    
       โ”‚   17 with Flow().add(uses=MyExec) as f:           โ”‚                    
       โ”‚   18                                              โ”‚                    
       โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ                    
       ZeroDivisionError: division by zero                                      
Traceback (most recent call last):
  File "/home/USER/.config/JetBrains/PyCharmCE2022.2/scratches/scratch_6.py", line 19, in <module>
    f.search(inputs=[Document()])
  File "/home/USER/Documents/workspace/Jina/jina/jina/clients/mixin.py", line 271, in post
    return run_async(
  File "/home/USER/Documents/workspace/Jina/jina/jina/helper.py", line 1334, in run_async
    return asyncio.run(func(*args, **kwargs))
  File "/home/USER/.pyenv/versions/3.9.10/lib/python3.9/asyncio/runners.py", line 44, in run
    return loop.run_until_complete(main)
  File "/home/USER/.pyenv/versions/3.9.10/lib/python3.9/asyncio/base_events.py", line 642, in run_until_complete
    return future.result()
  File "/home/USER/Documents/workspace/Jina/jina/jina/clients/mixin.py", line 262, in _get_results
    async for resp in c._get_results(*args, **kwargs):
  File "/home/USER/Documents/workspace/Jina/jina/jina/clients/base/grpc.py", line 122, in _get_results
    callback_exec(
  File "/home/USER/Documents/workspace/Jina/jina/jina/clients/helper.py", line 81, in callback_exec
    raise BadServer(response.header)
jina.excepts.BadServer: request_id: "c50a57014fb948ccbd8065ada487a136"
status {
  code: ERROR
  description: "ZeroDivisionError(\'division by zero\')"
  exception {
    name: "ZeroDivisionError"
    args: "division by zero"
    stacks: "Traceback (most recent call last):\n"
    stacks: "  File \"/home/USER/Documents/workspace/Jina/jina/jina/serve/runtimes/worker/__init__.py\", line 182, in process_data\n    result = await self._data_request_handler.handle(requests=requests)\n"
    stacks: "  File \"/home/USER/Documents/workspace/Jina/jina/jina/serve/runtimes/request_handlers/data_request_handler.py\", line 164, in handle\n    return_data = await self._executor.__acall__(\n"
    stacks: "  File \"/home/USER/Documents/workspace/Jina/jina/jina/serve/executors/__init__.py\", line 293, in __acall__\n    return await self.__acall_endpoint__(__default_endpoint__, **kwargs)\n"
    stacks: "  File \"/home/USER/Documents/workspace/Jina/jina/jina/serve/executors/__init__.py\", line 314, in __acall_endpoint__\n    return func(self, **kwargs)\n"
    stacks: "  File \"/home/USER/Documents/workspace/Jina/jina/jina/serve/executors/decorators.py\", line 162, in arg_wrapper\n    return fn(executor_instance, *args, **kwargs)\n"
    stacks: "  File \"/home/USER/.config/JetBrains/PyCharmCE2022.2/scratches/scratch_6.py\", line 15, in foo\n    1/0\n"
    stacks: "ZeroDivisionError: division by zero\n"
    executor: "MyExec"
  }
}
exec_endpoint: "/search"
target_executor: ""


Process finished with exit code 1

After the fix, the error is much clearer:


ERROR  executor0/rep-0@198650 ZeroDivisionError('division by [10/05/22 11:40:37]
     zero')                                                                   
      add "--quiet-error" to suppress the exception                           
     details                                                                  
     Traceback (most recent call last):                                       
       File                                                                   
     "/home/USER/Documents/workspace/Jina/jina/jina/serveโ€ฆ                    
     line 182, in process_data                                                
         result = await                                                       
     self._data_request_handler.handle(requests=requests)                     
       File                                                                   
     "/home/USER/Documents/workspace/Jina/jina/jina/serveโ€ฆ                    
     line 164, in handle                                                      
         return_data = await self._executor.__acall__(                        
       File                                                                   
     "/home/USER/Documents/workspace/Jina/jina/jina/serveโ€ฆ                    
     line 293, in __acall__                                                   
         return await                                                         
     self.__acall_endpoint__(__default_endpoint__,                            
     **kwargs)                                                                
       File                                                                   
     "/home/USER/Documents/workspace/Jina/jina/jina/serveโ€ฆ                    
     line 314, in __acall_endpoint__                                          
         return func(self, **kwargs)                                          
       File                                                                   
     "/home/USER/Documents/workspace/Jina/jina/jina/serveโ€ฆ                    
     line 162, in arg_wrapper                                                 
         return fn(executor_instance, *args, **kwargs)                        
       File                                                                   
     "/home/USER/.config/JetBrains/PyCharmCE2022.2/scratcโ€ฆ                    
     line 15, in foo                                                          
         1/0                                                                  
     ZeroDivisionError: division by zero                                      
Traceback (most recent call last):
File "/home/USER/.config/JetBrains/PyCharmCE2022.2/scratches/scratch_6.py", line 19, in <module>
  f.search(inputs=[Document()])
File "/home/USER/Documents/workspace/Jina/jina/jina/clients/mixin.py", line 271, in post
  return run_async(
File "/home/USER/Documents/workspace/Jina/jina/jina/helper.py", line 1334, in run_async
  return asyncio.run(func(*args, **kwargs))
File "/home/USER/.pyenv/versions/3.9.10/lib/python3.9/asyncio/runners.py", line 44, in run
  return loop.run_until_complete(main)
File "/home/USER/.pyenv/versions/3.9.10/lib/python3.9/asyncio/base_events.py", line 642, in run_until_complete
  return future.result()
File "/home/USER/Documents/workspace/Jina/jina/jina/clients/mixin.py", line 262, in _get_results
  async for resp in c._get_results(*args, **kwargs):
File "/home/USER/Documents/workspace/Jina/jina/jina/clients/base/grpc.py", line 122, in _get_results
  callback_exec(
File "/home/USER/Documents/workspace/Jina/jina/jina/clients/helper.py", line 81, in callback_exec
  raise BadServer(response.header)
jina.excepts.BadServer: request_id: "5d5e0338d0cd4bdd89efc430bbaaa74d"
status {
code: ERROR
description: "ZeroDivisionError(\'division by zero\')"
exception {
  name: "ZeroDivisionError"
  args: "division by zero"
  stacks: "Traceback (most recent call last):\n"
  stacks: "  File \"/home/USER/Documents/workspace/Jina/jina/jina/serve/runtimes/worker/__init__.py\", line 182, in process_data\n    result = await self._data_request_handler.handle(requests=requests)\n"
  stacks: "  File \"/home/USER/Documents/workspace/Jina/jina/jina/serve/runtimes/request_handlers/data_request_handler.py\", line 164, in handle\n    return_data = await self._executor.__acall__(\n"
  stacks: "  File \"/home/USER/Documents/workspace/Jina/jina/jina/serve/executors/__init__.py\", line 293, in __acall__\n    return await self.__acall_endpoint__(__default_endpoint__, **kwargs)\n"
  stacks: "  File \"/home/USER/Documents/workspace/Jina/jina/jina/serve/executors/__init__.py\", line 314, in __acall_endpoint__\n    return func(self, **kwargs)\n"
  stacks: "  File \"/home/USER/Documents/workspace/Jina/jina/jina/serve/executors/decorators.py\", line 162, in arg_wrapper\n    return fn(executor_instance, *args, **kwargs)\n"
  stacks: "  File \"/home/USER/.config/JetBrains/PyCharmCE2022.2/scratches/scratch_6.py\", line 15, in foo\n    1/0\n"
  stacks: "ZeroDivisionError: division by zero\n"
  executor: "MyExec"
}
}
exec_endpoint: "/search"
target_executor: ""


Process finished with exit code 1

Remove lambda function in logger (#5249)

We removed a reference to a lambda function in the JinaLogger so that there is no problem pickling it:

import pickle

from jina import Executor

class MyExecutor(Executor):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)


executor = MyExecutor()

with open('executor.pickle', 'wb') as f:
    pickle.dump(executor, f)

Previously, an Executor could not be pickled because it had a reference to a JinaLogger.

This used to raise an AttributeError exception:

Traceback (most recent call last):
  File "/home/USER/toy.py", line 14, in <module>
    pickle.dump(executor, f)
AttributeError: Can't pickle local object 'JinaLogger.__init__.<locals>.<lambda>'

Fix gRPC fork support (#5250)

We enabled proper fork support for gRPC to correctly clean up gRPC usage in parent and child processes. This prevents potential issues when forking Executor processes.

๐Ÿ“— Documentation Improvements

  • Explain Executor patching in Kubernetes (#5235)
  • Add note about keeping same Jina versions across microservices (#5239)
  • Add missing request numbers in sample code (#5237)
  • JCloud YAML reorder (#5234)
  • Docs spelling/grammar/punctuation (#5244) (#5240)
  • Fix typos (#5236)

๐ŸคŸ Contributors

We would like to thank all contributors to this release:

  • Sami Jaghouar (@samsja)
  • Alex Cureton-Griffiths (@alexcg1)
  • Johannes Messner (@JohannesMessner)
  • Delgermurun (@delgermurun)
  • AlaeddineAbdessalem (@alaeddine-13)
  • Jackmin801 (@Jackmin801)
  • Joan Fontanals (@JoanFM)
  • Girish Chandrashekar (@girishc13)