indexify 0.3.3__tar.gz → 0.3.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. {indexify-0.3.3 → indexify-0.3.4}/PKG-INFO +3 -3
  2. {indexify-0.3.3 → indexify-0.3.4}/pyproject.toml +3 -3
  3. {indexify-0.3.3 → indexify-0.3.4}/src/indexify/executor/function_executor/function_executor.py +18 -0
  4. indexify-0.3.4/src/indexify/executor/function_executor/health_checker.py +79 -0
  5. {indexify-0.3.3 → indexify-0.3.4}/src/indexify/executor/function_executor/single_task_runner.py +31 -3
  6. {indexify-0.3.3 → indexify-0.3.4}/src/indexify/function_executor/proto/configuration.py +3 -0
  7. {indexify-0.3.3 → indexify-0.3.4}/src/indexify/function_executor/proto/function_executor.proto +8 -0
  8. {indexify-0.3.3 → indexify-0.3.4}/src/indexify/function_executor/proto/function_executor_pb2.py +7 -3
  9. {indexify-0.3.3 → indexify-0.3.4}/src/indexify/function_executor/proto/function_executor_pb2.pyi +10 -0
  10. {indexify-0.3.3 → indexify-0.3.4}/src/indexify/function_executor/proto/function_executor_pb2_grpc.py +47 -0
  11. {indexify-0.3.3 → indexify-0.3.4}/src/indexify/function_executor/service.py +11 -0
  12. {indexify-0.3.3 → indexify-0.3.4}/README.md +0 -0
  13. {indexify-0.3.3 → indexify-0.3.4}/src/indexify/cli/cli.py +0 -0
  14. {indexify-0.3.3 → indexify-0.3.4}/src/indexify/executor/README.md +0 -0
  15. {indexify-0.3.3 → indexify-0.3.4}/src/indexify/executor/api_objects.py +0 -0
  16. {indexify-0.3.3 → indexify-0.3.4}/src/indexify/executor/downloader.py +0 -0
  17. {indexify-0.3.3 → indexify-0.3.4}/src/indexify/executor/executor.py +0 -0
  18. {indexify-0.3.3 → indexify-0.3.4}/src/indexify/executor/function_executor/function_executor_state.py +0 -0
  19. {indexify-0.3.3 → indexify-0.3.4}/src/indexify/executor/function_executor/invocation_state_client.py +0 -0
  20. {indexify-0.3.3 → indexify-0.3.4}/src/indexify/executor/function_executor/server/function_executor_server.py +0 -0
  21. {indexify-0.3.3 → indexify-0.3.4}/src/indexify/executor/function_executor/server/function_executor_server_factory.py +0 -0
  22. {indexify-0.3.3 → indexify-0.3.4}/src/indexify/executor/function_executor/server/subprocess_function_executor_server.py +0 -0
  23. {indexify-0.3.3 → indexify-0.3.4}/src/indexify/executor/function_executor/server/subprocess_function_executor_server_factory.py +0 -0
  24. {indexify-0.3.3 → indexify-0.3.4}/src/indexify/executor/function_executor/task_input.py +0 -0
  25. {indexify-0.3.3 → indexify-0.3.4}/src/indexify/executor/function_executor/task_output.py +0 -0
  26. {indexify-0.3.3 → indexify-0.3.4}/src/indexify/executor/runtime_probes.py +0 -0
  27. {indexify-0.3.3 → indexify-0.3.4}/src/indexify/executor/task_fetcher.py +0 -0
  28. {indexify-0.3.3 → indexify-0.3.4}/src/indexify/executor/task_reporter.py +0 -0
  29. {indexify-0.3.3 → indexify-0.3.4}/src/indexify/executor/task_runner.py +0 -0
  30. {indexify-0.3.3 → indexify-0.3.4}/src/indexify/function_executor/README.md +0 -0
  31. {indexify-0.3.3 → indexify-0.3.4}/src/indexify/function_executor/handlers/run_function/function_inputs_loader.py +0 -0
  32. {indexify-0.3.3 → indexify-0.3.4}/src/indexify/function_executor/handlers/run_function/handler.py +0 -0
  33. {indexify-0.3.3 → indexify-0.3.4}/src/indexify/function_executor/handlers/run_function/request_validator.py +0 -0
  34. {indexify-0.3.3 → indexify-0.3.4}/src/indexify/function_executor/handlers/run_function/response_helper.py +0 -0
  35. {indexify-0.3.3 → indexify-0.3.4}/src/indexify/function_executor/initialize_request_validator.py +0 -0
  36. {indexify-0.3.3 → indexify-0.3.4}/src/indexify/function_executor/invocation_state/invocation_state_proxy_server.py +0 -0
  37. {indexify-0.3.3 → indexify-0.3.4}/src/indexify/function_executor/invocation_state/proxied_invocation_state.py +0 -0
  38. {indexify-0.3.3 → indexify-0.3.4}/src/indexify/function_executor/invocation_state/response_validator.py +0 -0
  39. {indexify-0.3.3 → indexify-0.3.4}/src/indexify/function_executor/main.py +0 -0
  40. {indexify-0.3.3 → indexify-0.3.4}/src/indexify/function_executor/proto/message_validator.py +0 -0
  41. {indexify-0.3.3 → indexify-0.3.4}/src/indexify/function_executor/server.py +0 -0
  42. {indexify-0.3.3 → indexify-0.3.4}/src/indexify/utils/README.md +0 -0
  43. {indexify-0.3.3 → indexify-0.3.4}/src/indexify/utils/http_client.py +0 -0
  44. {indexify-0.3.3 → indexify-0.3.4}/src/indexify/utils/logging.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: indexify
3
- Version: 0.3.3
3
+ Version: 0.3.4
4
4
  Summary: Open Source Indexify components and helper tools
5
5
  Home-page: https://github.com/tensorlakeai/indexify
6
6
  License: Apache 2.0
@@ -17,13 +17,13 @@ Classifier: Programming Language :: Python :: 3.13
17
17
  Requires-Dist: grpcio (==1.68.1)
18
18
  Requires-Dist: grpcio-tools (==1.68.1)
19
19
  Requires-Dist: httpx-sse (>=0.4.0,<0.5.0)
20
- Requires-Dist: httpx[http2] (>=0.28.1,<0.29.0)
20
+ Requires-Dist: httpx[http2] (>=0.27,<0.28)
21
21
  Requires-Dist: nanoid (>=2.0.0,<3.0.0)
22
22
  Requires-Dist: pydantic (==2.10.4)
23
23
  Requires-Dist: pyyaml (>=6,<7)
24
24
  Requires-Dist: rich (>=13.9.2,<14.0.0)
25
25
  Requires-Dist: structlog (>=24.4.0,<25.0.0)
26
- Requires-Dist: tensorlake (>=0.1.7,<0.2.0)
26
+ Requires-Dist: tensorlake (>=0.1.9)
27
27
  Requires-Dist: typer (>=0.12,<0.13)
28
28
  Project-URL: Repository, https://github.com/tensorlakeai/indexify
29
29
  Description-Content-Type: text/markdown
@@ -1,7 +1,7 @@
1
1
  [tool.poetry]
2
2
  name = "indexify"
3
3
  # Incremented if any of the components provided in this packages are updated.
4
- version = "0.3.3"
4
+ version = "0.3.4"
5
5
  description = "Open Source Indexify components and helper tools"
6
6
  authors = ["Tensorlake Inc. <support@tensorlake.ai>"]
7
7
  license = "Apache 2.0"
@@ -18,12 +18,12 @@ function-executor = "indexify.function_executor.main:main"
18
18
  python = "^3.9"
19
19
  structlog = "^24.4.0"
20
20
  pyyaml = "^6"
21
- httpx = { version = "^0.28.1", extras = ["http2"] }
21
+ httpx = { version = "^0.27", extras = ["http2"] }
22
22
  grpcio = "1.68.1"
23
23
 
24
24
  # Function Executor only
25
25
  grpcio-tools = "1.68.1"
26
- tensorlake = "~=0.1.7"
26
+ tensorlake = ">=0.1.9"
27
27
 
28
28
  # Executor only
29
29
  pydantic = "2.10.4"
@@ -12,6 +12,7 @@ from indexify.function_executor.proto.function_executor_pb2_grpc import (
12
12
  )
13
13
  from indexify.utils.http_client import get_httpx_client
14
14
 
15
+ from .health_checker import HealthChecker
15
16
  from .invocation_state_client import InvocationStateClient
16
17
  from .server.function_executor_server import (
17
18
  FUNCTION_EXECUTOR_SERVER_READY_TIMEOUT_SEC,
@@ -45,6 +46,7 @@ class FunctionExecutor:
45
46
  self._server: Optional[FunctionExecutorServer] = None
46
47
  self._channel: Optional[grpc.aio.Channel] = None
47
48
  self._invocation_state_client: Optional[InvocationStateClient] = None
49
+ self._health_checker: Optional[HealthChecker] = None
48
50
  self._initialized = False
49
51
 
50
52
  async def initialize(
@@ -78,6 +80,11 @@ class FunctionExecutor:
78
80
  )
79
81
  await self._invocation_state_client.start()
80
82
 
83
+ self._health_checker = HealthChecker(
84
+ stub=stub,
85
+ logger=self._logger,
86
+ )
87
+
81
88
  self._initialized = True
82
89
  except Exception:
83
90
  await self.destroy()
@@ -91,10 +98,21 @@ class FunctionExecutor:
91
98
  self._check_initialized()
92
99
  return self._invocation_state_client
93
100
 
101
+ def health_checker(self) -> HealthChecker:
102
+ self._check_initialized()
103
+ return self._health_checker
104
+
94
105
  async def destroy(self):
95
106
  """Destroys all resources owned by this FunctionExecutor.
96
107
 
97
108
  Never raises any exceptions but logs them."""
109
+ try:
110
+ if self._health_checker is not None:
111
+ self._health_checker.stop()
112
+ self._health_checker = None
113
+ except Exception as e:
114
+ self._logger.error("failed to stop HealthChecker", exc_info=e)
115
+
98
116
  try:
99
117
  if self._invocation_state_client is not None:
100
118
  await self._invocation_state_client.destroy()
@@ -0,0 +1,79 @@
1
+ import asyncio
2
+ from collections.abc import Awaitable, Callable
3
+ from typing import Any, Optional
4
+
5
+ from grpc.aio import AioRpcError
6
+
7
+ from indexify.function_executor.proto.configuration import HEALTH_CHECK_TIMEOUT_SEC
8
+ from indexify.function_executor.proto.function_executor_pb2 import (
9
+ HealthCheckRequest,
10
+ HealthCheckResponse,
11
+ )
12
+ from indexify.function_executor.proto.function_executor_pb2_grpc import (
13
+ FunctionExecutorStub,
14
+ )
15
+
16
+ HEALTH_CHECK_POLL_PERIOD_SEC = 10
17
+
18
+
19
+ class HealthChecker:
20
+ def __init__(self, stub: FunctionExecutorStub, logger: Any):
21
+ self._stub: FunctionExecutorStub = stub
22
+ self._logger: Any = logger.bind(module=__name__)
23
+ self._health_check_loop_task: Optional[asyncio.Task] = None
24
+ self._health_check_failed_callback: Optional[Callable[[], Awaitable[None]]] = (
25
+ None
26
+ )
27
+
28
+ async def check(self) -> bool:
29
+ """Runs the health check once and returns the result.
30
+
31
+ Does not raise any exceptions."""
32
+ try:
33
+ response: HealthCheckResponse = await self._stub.check_health(
34
+ HealthCheckRequest(), timeout=HEALTH_CHECK_TIMEOUT_SEC
35
+ )
36
+ return response.healthy
37
+ except AioRpcError:
38
+ return False
39
+ except Exception as e:
40
+ self._logger.warning("Got unexpected exception, ignoring", exc_info=e)
41
+ return False
42
+
43
+ def start(self, callback: Callable[[], Awaitable[None]]) -> None:
44
+ """Starts periodic health checks.
45
+
46
+ The supplied callback is an async function called in the calling thread's
47
+ event loop when the health check fails. The callback is called only once
48
+ and then health checks are stopped.
49
+
50
+ Without a periodic health check a TCP client socket won't detect a server
51
+ socket problem (e.g. it's closed due to server crash) because there are no
52
+ TCP packets floating between them.
53
+
54
+ Does not raise any exceptions.
55
+ """
56
+ if self._health_check_loop_task is not None:
57
+ return
58
+
59
+ self._health_check_failed_callback = callback
60
+ self._health_check_loop_task = asyncio.create_task(self._health_check_loop())
61
+
62
+ def stop(self) -> None:
63
+ """Stops the periodic health checks.
64
+
65
+ Does not raise any exceptions."""
66
+ if self._health_check_loop_task is None:
67
+ return
68
+
69
+ self._health_check_loop_task.cancel()
70
+ self._health_check_loop_task = None
71
+
72
+ async def _health_check_loop(self) -> None:
73
+ while True:
74
+ if not await self.check():
75
+ break
76
+ await asyncio.sleep(HEALTH_CHECK_POLL_PERIOD_SEC)
77
+
78
+ asyncio.create_task(self._health_check_failed_callback())
79
+ self._health_check_loop_task = None
@@ -1,6 +1,8 @@
1
+ from collections.abc import Awaitable, Callable
1
2
  from typing import Any, Optional
2
3
 
3
4
  import grpc
5
+ from grpc.aio import AioRpcError
4
6
 
5
7
  from indexify.function_executor.proto.function_executor_pb2 import (
6
8
  InitializeRequest,
@@ -52,6 +54,12 @@ class SingleTaskRunner:
52
54
  if self._state.is_shutdown:
53
55
  raise RuntimeError("Function Executor state is shutting down.")
54
56
 
57
+ # If Function Executor is not healthy then recreate it.
58
+ if self._state.function_executor is not None:
59
+ if not await self._state.function_executor.health_checker().check():
60
+ self._logger.error("Health check failed, destroying FunctionExecutor.")
61
+ await self._state.destroy_function_executor()
62
+
55
63
  if self._state.function_executor is None:
56
64
  try:
57
65
  await self._create_function_executor()
@@ -110,13 +118,23 @@ class SingleTaskRunner:
110
118
  async with _RunningTaskContextManager(
111
119
  invocation_id=self._task_input.task.invocation_id,
112
120
  task_id=self._task_input.task.id,
121
+ health_check_failed_callback=self._health_check_failed_callback,
113
122
  function_executor_state=self._state,
114
123
  ):
124
+ # If this RPC failed due to customer code crashing the server we won't be
125
+ # able to detect this. We'll treat this as our own error for now and thus
126
+ # let the AioRpcError to be raised here.
115
127
  response: RunTaskResponse = await FunctionExecutorStub(channel).run_task(
116
128
  request
117
129
  )
118
130
  return _task_output(task=self._task_input.task, response=response)
119
131
 
132
+ async def _health_check_failed_callback(self):
133
+ # The Function Executor needs to get recreated on next task run.
134
+ self._logger.error("Health check failed, destroying FunctionExecutor.")
135
+ async with self._state.lock:
136
+ await self._state.destroy_function_executor()
137
+
120
138
 
121
139
  class _RunningTaskContextManager:
122
140
  """Performs all the actions required before and after running a task."""
@@ -125,10 +143,14 @@ class _RunningTaskContextManager:
125
143
  self,
126
144
  invocation_id: str,
127
145
  task_id: str,
146
+ health_check_failed_callback: Callable[[], Awaitable[None]],
128
147
  function_executor_state: FunctionExecutorState,
129
148
  ):
130
149
  self._invocation_id: str = invocation_id
131
150
  self._task_id: str = task_id
151
+ self._health_check_failed_callback: Callable[[], Awaitable[None]] = (
152
+ health_check_failed_callback
153
+ )
132
154
  self._state: FunctionExecutorState = function_executor_state
133
155
 
134
156
  async def __aenter__(self):
@@ -137,6 +159,9 @@ class _RunningTaskContextManager:
137
159
  task_id=self._task_id,
138
160
  invocation_id=self._invocation_id,
139
161
  )
162
+ self._state.function_executor.health_checker().start(
163
+ self._health_check_failed_callback
164
+ )
140
165
  # Unlock the state so other tasks can act depending on it.
141
166
  self._state.lock.release()
142
167
  return self
@@ -144,9 +169,12 @@ class _RunningTaskContextManager:
144
169
  async def __aexit__(self, exc_type, exc_val, exc_tb):
145
170
  await self._state.lock.acquire()
146
171
  self._state.decrement_running_tasks()
147
- self._state.function_executor.invocation_state_client().remove_task_to_invocation_id_entry(
148
- task_id=self._task_id
149
- )
172
+ # Health check callback could destroy the FunctionExecutor.
173
+ if self._state.function_executor is not None:
174
+ self._state.function_executor.invocation_state_client().remove_task_to_invocation_id_entry(
175
+ task_id=self._task_id
176
+ )
177
+ self._state.function_executor.health_checker().stop()
150
178
 
151
179
 
152
180
  def _task_output(task: Task, response: RunTaskResponse) -> TaskOutput:
@@ -19,3 +19,6 @@ GRPC_SERVER_OPTIONS = [
19
19
  ]
20
20
 
21
21
  GRPC_CHANNEL_OPTIONS = GRPC_SERVER_OPTIONS
22
+
23
+ # If a health check takes more than this duration then the server is considered unhealthy.
24
+ HEALTH_CHECK_TIMEOUT_SEC = 5
@@ -106,6 +106,12 @@ message RunTaskResponse {
106
106
  optional bool success = 7;
107
107
  }
108
108
 
109
+ message HealthCheckRequest {}
110
+
111
+ message HealthCheckResponse {
112
+ optional bool healthy = 1;
113
+ }
114
+
109
115
  service FunctionExecutor {
110
116
  // Initializes the Function Executor to run tasks
111
117
  // for a particular function. This method is called only
@@ -119,4 +125,6 @@ service FunctionExecutor {
119
125
  // Executes the task defined in the request.
120
126
  // Multiple tasks can be running in parallel.
121
127
  rpc run_task(RunTaskRequest) returns (RunTaskResponse);
128
+ // Health check method to check if the Function Executor is healthy.
129
+ rpc check_health(HealthCheckRequest) returns (HealthCheckResponse);
122
130
  }
@@ -24,7 +24,7 @@ _sym_db = _symbol_database.Default()
24
24
 
25
25
 
26
26
  DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(
27
- b'\n8indexify/function_executor/proto/function_executor.proto\x12\x19\x66unction_executor_service"i\n\x10SerializedObject\x12\x0f\n\x05\x62ytes\x18\x01 \x01(\x0cH\x00\x12\x10\n\x06string\x18\x02 \x01(\tH\x00\x12\x19\n\x0c\x63ontent_type\x18\x03 \x01(\tH\x01\x88\x01\x01\x42\x06\n\x04\x64\x61taB\x0f\n\r_content_type"\x88\x02\n\x11InitializeRequest\x12\x16\n\tnamespace\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x17\n\ngraph_name\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x1a\n\rgraph_version\x18\x03 \x01(\tH\x02\x88\x01\x01\x12\x1a\n\rfunction_name\x18\x05 \x01(\tH\x03\x88\x01\x01\x12?\n\x05graph\x18\x07 \x01(\x0b\x32+.function_executor_service.SerializedObjectH\x04\x88\x01\x01\x42\x0c\n\n_namespaceB\r\n\x0b_graph_nameB\x10\n\x0e_graph_versionB\x10\n\x0e_function_nameB\x08\n\x06_graph"f\n\x12InitializeResponse\x12\x14\n\x07success\x18\x01 \x01(\x08H\x00\x88\x01\x01\x12\x1b\n\x0e\x63ustomer_error\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\n\n\x08_successB\x11\n\x0f_customer_error"\x80\x01\n\x19SetInvocationStateRequest\x12\x10\n\x03key\x18\x01 \x01(\tH\x00\x88\x01\x01\x12?\n\x05value\x18\x02 \x01(\x0b\x32+.function_executor_service.SerializedObjectH\x01\x88\x01\x01\x42\x06\n\x04_keyB\x08\n\x06_value"\x1c\n\x1aSetInvocationStateResponse"5\n\x19GetInvocationStateRequest\x12\x10\n\x03key\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\x06\n\x04_key"\x81\x01\n\x1aGetInvocationStateResponse\x12\x10\n\x03key\x18\x01 \x01(\tH\x00\x88\x01\x01\x12?\n\x05value\x18\x02 \x01(\x0b\x32+.function_executor_service.SerializedObjectH\x01\x88\x01\x01\x42\x06\n\x04_keyB\x08\n\x06_value"\xf7\x01\n\x16InvocationStateRequest\x12\x17\n\nrequest_id\x18\x01 \x01(\tH\x01\x88\x01\x01\x12\x14\n\x07task_id\x18\x02 \x01(\tH\x02\x88\x01\x01\x12\x43\n\x03set\x18\x03 \x01(\x0b\x32\x34.function_executor_service.SetInvocationStateRequestH\x00\x12\x43\n\x03get\x18\x04 \x01(\x0b\x32\x34.function_executor_service.GetInvocationStateRequestH\x00\x42\t\n\x07requestB\r\n\x0b_request_idB\n\n\x08_task_id"\xfb\x01\n\x17InvocationStateResponse\x12\x17\n\nrequest_id\x18\x01 \x01(\tH\x01\x88\x01\x01\x12\x14\n\x07success\x18\x02 \x01(\x08H\x02\x88\x01\x01\x12\x44\n\x03set\x18\x03 \x01(\x0b\x32\x35.function_executor_service.SetInvocationStateResponseH\x00\x12\x44\n\x03get\x18\x04 \x01(\x0b\x32\x35.function_executor_service.GetInvocationStateResponseH\x00\x42\n\n\x08responseB\r\n\x0b_request_idB\n\n\x08_success"N\n\x0e\x46unctionOutput\x12<\n\x07outputs\x18\x01 \x03(\x0b\x32+.function_executor_service.SerializedObject"\x1d\n\x0cRouterOutput\x12\r\n\x05\x65\x64ges\x18\x01 \x03(\t"\xda\x03\n\x0eRunTaskRequest\x12\x16\n\tnamespace\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x17\n\ngraph_name\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x1a\n\rgraph_version\x18\x03 \x01(\tH\x02\x88\x01\x01\x12\x1a\n\rfunction_name\x18\x04 \x01(\tH\x03\x88\x01\x01\x12 \n\x13graph_invocation_id\x18\x05 \x01(\tH\x04\x88\x01\x01\x12\x14\n\x07task_id\x18\x06 \x01(\tH\x05\x88\x01\x01\x12H\n\x0e\x66unction_input\x18\x07 \x01(\x0b\x32+.function_executor_service.SerializedObjectH\x06\x88\x01\x01\x12M\n\x13\x66unction_init_value\x18\x08 \x01(\x0b\x32+.function_executor_service.SerializedObjectH\x07\x88\x01\x01\x42\x0c\n\n_namespaceB\r\n\x0b_graph_nameB\x10\n\x0e_graph_versionB\x10\n\x0e_function_nameB\x16\n\x14_graph_invocation_idB\n\n\x08_task_idB\x11\n\x0f_function_inputB\x16\n\x14_function_init_value"\xf1\x02\n\x0fRunTaskResponse\x12\x14\n\x07task_id\x18\x01 \x01(\tH\x00\x88\x01\x01\x12G\n\x0f\x66unction_output\x18\x02 \x01(\x0b\x32).function_executor_service.FunctionOutputH\x01\x88\x01\x01\x12\x43\n\rrouter_output\x18\x03 \x01(\x0b\x32\'.function_executor_service.RouterOutputH\x02\x88\x01\x01\x12\x13\n\x06stdout\x18\x04 \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06stderr\x18\x05 \x01(\tH\x04\x88\x01\x01\x12\x17\n\nis_reducer\x18\x06 \x01(\x08H\x05\x88\x01\x01\x12\x14\n\x07success\x18\x07 \x01(\x08H\x06\x88\x01\x01\x42\n\n\x08_task_idB\x12\n\x10_function_outputB\x10\n\x0e_router_outputB\t\n\x07_stdoutB\t\n\x07_stderrB\r\n\x0b_is_reducerB\n\n\x08_success2\xf2\x02\n\x10\x46unctionExecutor\x12i\n\ninitialize\x12,.function_executor_service.InitializeRequest\x1a-.function_executor_service.InitializeResponse\x12\x8f\x01\n"initialize_invocation_state_server\x12\x32.function_executor_service.InvocationStateResponse\x1a\x31.function_executor_service.InvocationStateRequest(\x01\x30\x01\x12\x61\n\x08run_task\x12).function_executor_service.RunTaskRequest\x1a*.function_executor_service.RunTaskResponseb\x06proto3'
27
+ b'\n8indexify/function_executor/proto/function_executor.proto\x12\x19\x66unction_executor_service"i\n\x10SerializedObject\x12\x0f\n\x05\x62ytes\x18\x01 \x01(\x0cH\x00\x12\x10\n\x06string\x18\x02 \x01(\tH\x00\x12\x19\n\x0c\x63ontent_type\x18\x03 \x01(\tH\x01\x88\x01\x01\x42\x06\n\x04\x64\x61taB\x0f\n\r_content_type"\x88\x02\n\x11InitializeRequest\x12\x16\n\tnamespace\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x17\n\ngraph_name\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x1a\n\rgraph_version\x18\x03 \x01(\tH\x02\x88\x01\x01\x12\x1a\n\rfunction_name\x18\x05 \x01(\tH\x03\x88\x01\x01\x12?\n\x05graph\x18\x07 \x01(\x0b\x32+.function_executor_service.SerializedObjectH\x04\x88\x01\x01\x42\x0c\n\n_namespaceB\r\n\x0b_graph_nameB\x10\n\x0e_graph_versionB\x10\n\x0e_function_nameB\x08\n\x06_graph"f\n\x12InitializeResponse\x12\x14\n\x07success\x18\x01 \x01(\x08H\x00\x88\x01\x01\x12\x1b\n\x0e\x63ustomer_error\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\n\n\x08_successB\x11\n\x0f_customer_error"\x80\x01\n\x19SetInvocationStateRequest\x12\x10\n\x03key\x18\x01 \x01(\tH\x00\x88\x01\x01\x12?\n\x05value\x18\x02 \x01(\x0b\x32+.function_executor_service.SerializedObjectH\x01\x88\x01\x01\x42\x06\n\x04_keyB\x08\n\x06_value"\x1c\n\x1aSetInvocationStateResponse"5\n\x19GetInvocationStateRequest\x12\x10\n\x03key\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\x06\n\x04_key"\x81\x01\n\x1aGetInvocationStateResponse\x12\x10\n\x03key\x18\x01 \x01(\tH\x00\x88\x01\x01\x12?\n\x05value\x18\x02 \x01(\x0b\x32+.function_executor_service.SerializedObjectH\x01\x88\x01\x01\x42\x06\n\x04_keyB\x08\n\x06_value"\xf7\x01\n\x16InvocationStateRequest\x12\x17\n\nrequest_id\x18\x01 \x01(\tH\x01\x88\x01\x01\x12\x14\n\x07task_id\x18\x02 \x01(\tH\x02\x88\x01\x01\x12\x43\n\x03set\x18\x03 \x01(\x0b\x32\x34.function_executor_service.SetInvocationStateRequestH\x00\x12\x43\n\x03get\x18\x04 \x01(\x0b\x32\x34.function_executor_service.GetInvocationStateRequestH\x00\x42\t\n\x07requestB\r\n\x0b_request_idB\n\n\x08_task_id"\xfb\x01\n\x17InvocationStateResponse\x12\x17\n\nrequest_id\x18\x01 \x01(\tH\x01\x88\x01\x01\x12\x14\n\x07success\x18\x02 \x01(\x08H\x02\x88\x01\x01\x12\x44\n\x03set\x18\x03 \x01(\x0b\x32\x35.function_executor_service.SetInvocationStateResponseH\x00\x12\x44\n\x03get\x18\x04 \x01(\x0b\x32\x35.function_executor_service.GetInvocationStateResponseH\x00\x42\n\n\x08responseB\r\n\x0b_request_idB\n\n\x08_success"N\n\x0e\x46unctionOutput\x12<\n\x07outputs\x18\x01 \x03(\x0b\x32+.function_executor_service.SerializedObject"\x1d\n\x0cRouterOutput\x12\r\n\x05\x65\x64ges\x18\x01 \x03(\t"\xda\x03\n\x0eRunTaskRequest\x12\x16\n\tnamespace\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x17\n\ngraph_name\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x1a\n\rgraph_version\x18\x03 \x01(\tH\x02\x88\x01\x01\x12\x1a\n\rfunction_name\x18\x04 \x01(\tH\x03\x88\x01\x01\x12 \n\x13graph_invocation_id\x18\x05 \x01(\tH\x04\x88\x01\x01\x12\x14\n\x07task_id\x18\x06 \x01(\tH\x05\x88\x01\x01\x12H\n\x0e\x66unction_input\x18\x07 \x01(\x0b\x32+.function_executor_service.SerializedObjectH\x06\x88\x01\x01\x12M\n\x13\x66unction_init_value\x18\x08 \x01(\x0b\x32+.function_executor_service.SerializedObjectH\x07\x88\x01\x01\x42\x0c\n\n_namespaceB\r\n\x0b_graph_nameB\x10\n\x0e_graph_versionB\x10\n\x0e_function_nameB\x16\n\x14_graph_invocation_idB\n\n\x08_task_idB\x11\n\x0f_function_inputB\x16\n\x14_function_init_value"\xf1\x02\n\x0fRunTaskResponse\x12\x14\n\x07task_id\x18\x01 \x01(\tH\x00\x88\x01\x01\x12G\n\x0f\x66unction_output\x18\x02 \x01(\x0b\x32).function_executor_service.FunctionOutputH\x01\x88\x01\x01\x12\x43\n\rrouter_output\x18\x03 \x01(\x0b\x32\'.function_executor_service.RouterOutputH\x02\x88\x01\x01\x12\x13\n\x06stdout\x18\x04 \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06stderr\x18\x05 \x01(\tH\x04\x88\x01\x01\x12\x17\n\nis_reducer\x18\x06 \x01(\x08H\x05\x88\x01\x01\x12\x14\n\x07success\x18\x07 \x01(\x08H\x06\x88\x01\x01\x42\n\n\x08_task_idB\x12\n\x10_function_outputB\x10\n\x0e_router_outputB\t\n\x07_stdoutB\t\n\x07_stderrB\r\n\x0b_is_reducerB\n\n\x08_success"\x14\n\x12HealthCheckRequest"7\n\x13HealthCheckResponse\x12\x14\n\x07healthy\x18\x01 \x01(\x08H\x00\x88\x01\x01\x42\n\n\x08_healthy2\xe1\x03\n\x10\x46unctionExecutor\x12i\n\ninitialize\x12,.function_executor_service.InitializeRequest\x1a-.function_executor_service.InitializeResponse\x12\x8f\x01\n"initialize_invocation_state_server\x12\x32.function_executor_service.InvocationStateResponse\x1a\x31.function_executor_service.InvocationStateRequest(\x01\x30\x01\x12\x61\n\x08run_task\x12).function_executor_service.RunTaskRequest\x1a*.function_executor_service.RunTaskResponse\x12m\n\x0c\x63heck_health\x12-.function_executor_service.HealthCheckRequest\x1a..function_executor_service.HealthCheckResponseb\x06proto3'
28
28
  )
29
29
 
30
30
  _globals = globals()
@@ -60,6 +60,10 @@ if not _descriptor._USE_C_DESCRIPTORS:
60
60
  _globals["_RUNTASKREQUEST"]._serialized_end = 2003
61
61
  _globals["_RUNTASKRESPONSE"]._serialized_start = 2006
62
62
  _globals["_RUNTASKRESPONSE"]._serialized_end = 2375
63
- _globals["_FUNCTIONEXECUTOR"]._serialized_start = 2378
64
- _globals["_FUNCTIONEXECUTOR"]._serialized_end = 2748
63
+ _globals["_HEALTHCHECKREQUEST"]._serialized_start = 2377
64
+ _globals["_HEALTHCHECKREQUEST"]._serialized_end = 2397
65
+ _globals["_HEALTHCHECKRESPONSE"]._serialized_start = 2399
66
+ _globals["_HEALTHCHECKRESPONSE"]._serialized_end = 2454
67
+ _globals["_FUNCTIONEXECUTOR"]._serialized_start = 2457
68
+ _globals["_FUNCTIONEXECUTOR"]._serialized_end = 2938
65
69
  # @@protoc_insertion_point(module_scope)
@@ -213,3 +213,13 @@ class RunTaskResponse(_message.Message):
213
213
  is_reducer: bool = ...,
214
214
  success: bool = ...,
215
215
  ) -> None: ...
216
+
217
+ class HealthCheckRequest(_message.Message):
218
+ __slots__ = ()
219
+ def __init__(self) -> None: ...
220
+
221
+ class HealthCheckResponse(_message.Message):
222
+ __slots__ = ("healthy",)
223
+ HEALTHY_FIELD_NUMBER: _ClassVar[int]
224
+ healthy: bool
225
+ def __init__(self, healthy: bool = ...) -> None: ...
@@ -58,6 +58,12 @@ class FunctionExecutorStub(object):
58
58
  response_deserializer=indexify_dot_function__executor_dot_proto_dot_function__executor__pb2.RunTaskResponse.FromString,
59
59
  _registered_method=True,
60
60
  )
61
+ self.check_health = channel.unary_unary(
62
+ "/function_executor_service.FunctionExecutor/check_health",
63
+ request_serializer=indexify_dot_function__executor_dot_proto_dot_function__executor__pb2.HealthCheckRequest.SerializeToString,
64
+ response_deserializer=indexify_dot_function__executor_dot_proto_dot_function__executor__pb2.HealthCheckResponse.FromString,
65
+ _registered_method=True,
66
+ )
61
67
 
62
68
 
63
69
  class FunctionExecutorServicer(object):
@@ -90,6 +96,12 @@ class FunctionExecutorServicer(object):
90
96
  context.set_details("Method not implemented!")
91
97
  raise NotImplementedError("Method not implemented!")
92
98
 
99
+ def check_health(self, request, context):
100
+ """Health check method to check if the Function Executor is healthy."""
101
+ context.set_code(grpc.StatusCode.UNIMPLEMENTED)
102
+ context.set_details("Method not implemented!")
103
+ raise NotImplementedError("Method not implemented!")
104
+
93
105
 
94
106
  def add_FunctionExecutorServicer_to_server(servicer, server):
95
107
  rpc_method_handlers = {
@@ -108,6 +120,11 @@ def add_FunctionExecutorServicer_to_server(servicer, server):
108
120
  request_deserializer=indexify_dot_function__executor_dot_proto_dot_function__executor__pb2.RunTaskRequest.FromString,
109
121
  response_serializer=indexify_dot_function__executor_dot_proto_dot_function__executor__pb2.RunTaskResponse.SerializeToString,
110
122
  ),
123
+ "check_health": grpc.unary_unary_rpc_method_handler(
124
+ servicer.check_health,
125
+ request_deserializer=indexify_dot_function__executor_dot_proto_dot_function__executor__pb2.HealthCheckRequest.FromString,
126
+ response_serializer=indexify_dot_function__executor_dot_proto_dot_function__executor__pb2.HealthCheckResponse.SerializeToString,
127
+ ),
111
128
  }
112
129
  generic_handler = grpc.method_handlers_generic_handler(
113
130
  "function_executor_service.FunctionExecutor", rpc_method_handlers
@@ -211,3 +228,33 @@ class FunctionExecutor(object):
211
228
  metadata,
212
229
  _registered_method=True,
213
230
  )
231
+
232
+ @staticmethod
233
+ def check_health(
234
+ request,
235
+ target,
236
+ options=(),
237
+ channel_credentials=None,
238
+ call_credentials=None,
239
+ insecure=False,
240
+ compression=None,
241
+ wait_for_ready=None,
242
+ timeout=None,
243
+ metadata=None,
244
+ ):
245
+ return grpc.experimental.unary_unary(
246
+ request,
247
+ target,
248
+ "/function_executor_service.FunctionExecutor/check_health",
249
+ indexify_dot_function__executor_dot_proto_dot_function__executor__pb2.HealthCheckRequest.SerializeToString,
250
+ indexify_dot_function__executor_dot_proto_dot_function__executor__pb2.HealthCheckResponse.FromString,
251
+ options,
252
+ channel_credentials,
253
+ insecure,
254
+ call_credentials,
255
+ compression,
256
+ wait_for_ready,
257
+ timeout,
258
+ metadata,
259
+ _registered_method=True,
260
+ )
@@ -13,6 +13,8 @@ from .initialize_request_validator import InitializeRequestValidator
13
13
  from .invocation_state.invocation_state_proxy_server import InvocationStateProxyServer
14
14
  from .invocation_state.proxied_invocation_state import ProxiedInvocationState
15
15
  from .proto.function_executor_pb2 import (
16
+ HealthCheckRequest,
17
+ HealthCheckResponse,
16
18
  InitializeRequest,
17
19
  InitializeResponse,
18
20
  InvocationStateResponse,
@@ -120,3 +122,12 @@ class Service(FunctionExecutorServicer):
120
122
  raise ValueError(
121
123
  f"This Function Executor is not initialized for this function_name {request.function_name}"
122
124
  )
125
+
126
+ def check_health(
127
+ self, request: HealthCheckRequest, context: grpc.ServicerContext
128
+ ) -> HealthCheckResponse:
129
+ # This health check validates that the Server:
130
+ # - Has its process alive (not exited).
131
+ # - Didn't exhaust its thread pool.
132
+ # - Is able to communicate over its server socket.
133
+ return HealthCheckResponse(healthy=True)
File without changes