indexify 0.2.44__py3-none-any.whl → 0.2.45__py3-none-any.whl

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 (42) hide show
  1. indexify/__init__.py +2 -0
  2. indexify/cli.py +41 -80
  3. indexify/executor/api_objects.py +2 -0
  4. indexify/executor/downloader.py +23 -25
  5. indexify/executor/executor.py +29 -35
  6. indexify/executor/function_executor/function_executor.py +120 -19
  7. indexify/executor/function_executor/function_executor_state.py +75 -0
  8. indexify/executor/function_executor/invocation_state_client.py +232 -0
  9. indexify/executor/function_executor/server/function_executor_server.py +24 -0
  10. indexify/executor/function_executor/server/function_executor_server_factory.py +43 -0
  11. indexify/executor/function_executor/server/subprocess_function_executor_server.py +25 -0
  12. indexify/executor/function_executor/{process_function_executor_factory.py → server/subprocess_function_executor_server_factory.py} +21 -21
  13. indexify/executor/function_executor/single_task_runner.py +160 -0
  14. indexify/executor/function_executor/task_input.py +23 -0
  15. indexify/executor/function_executor/task_output.py +36 -0
  16. indexify/executor/task_reporter.py +10 -17
  17. indexify/executor/task_runner.py +104 -0
  18. indexify/function_executor/function_executor_service.py +22 -7
  19. indexify/function_executor/handlers/run_function/handler.py +13 -12
  20. indexify/function_executor/invocation_state/invocation_state_proxy_server.py +170 -0
  21. indexify/function_executor/invocation_state/proxied_invocation_state.py +24 -0
  22. indexify/function_executor/invocation_state/response_validator.py +29 -0
  23. indexify/function_executor/proto/function_executor.proto +47 -0
  24. indexify/function_executor/proto/function_executor_pb2.py +23 -11
  25. indexify/function_executor/proto/function_executor_pb2.pyi +70 -0
  26. indexify/function_executor/proto/function_executor_pb2_grpc.py +50 -0
  27. indexify/functions_sdk/graph.py +3 -3
  28. indexify/functions_sdk/image.py +142 -9
  29. indexify/functions_sdk/indexify_functions.py +45 -79
  30. indexify/functions_sdk/invocation_state/invocation_state.py +22 -0
  31. indexify/functions_sdk/invocation_state/local_invocation_state.py +30 -0
  32. indexify/http_client.py +0 -17
  33. {indexify-0.2.44.dist-info → indexify-0.2.45.dist-info}/METADATA +1 -1
  34. indexify-0.2.45.dist-info/RECORD +60 -0
  35. indexify/executor/function_executor/function_executor_factory.py +0 -26
  36. indexify/executor/function_executor/function_executor_map.py +0 -91
  37. indexify/executor/function_executor/process_function_executor.py +0 -64
  38. indexify/executor/function_worker.py +0 -253
  39. indexify-0.2.44.dist-info/RECORD +0 -50
  40. {indexify-0.2.44.dist-info → indexify-0.2.45.dist-info}/LICENSE.txt +0 -0
  41. {indexify-0.2.44.dist-info → indexify-0.2.45.dist-info}/WHEEL +0 -0
  42. {indexify-0.2.44.dist-info → indexify-0.2.45.dist-info}/entry_points.txt +0 -0
@@ -10,7 +10,7 @@ from indexify.function_executor.proto.function_executor_pb2 import (
10
10
  FunctionOutput,
11
11
  )
12
12
 
13
- from .function_worker import FunctionWorkerOutput
13
+ from .task_runner import TaskOutput
14
14
 
15
15
 
16
16
  # https://github.com/psf/requests/issues/1081#issuecomment-428504128
@@ -48,17 +48,10 @@ class TaskReporter:
48
48
  # results in not reusing established TCP connections to server.
49
49
  self._client = get_httpx_client(config_path, make_async=False)
50
50
 
51
- async def report(
52
- self, task: Task, output: Optional[FunctionWorkerOutput], logger: Any
53
- ):
54
- """Reports result of the supplied task.
55
-
56
- If FunctionWorkerOutput is None this means that the task didn't finish and failed with internal error.
57
- """
51
+ async def report(self, output: TaskOutput, logger: Any):
52
+ """Reports result of the supplied task."""
58
53
  logger = logger.bind(module=__name__)
59
- task_result, output_files, output_summary = self._process_task_output(
60
- task, output
61
- )
54
+ task_result, output_files, output_summary = self._process_task_output(output)
62
55
  task_result_data = task_result.model_dump_json(exclude_none=True)
63
56
 
64
57
  logger.info(
@@ -100,16 +93,16 @@ class TaskReporter:
100
93
  ) from e
101
94
 
102
95
  def _process_task_output(
103
- self, task: Task, output: Optional[FunctionWorkerOutput]
96
+ self, output: TaskOutput
104
97
  ) -> Tuple[TaskResult, List[Any], TaskOutputSummary]:
105
98
  task_result = TaskResult(
106
99
  outcome="failure",
107
- namespace=task.namespace,
108
- compute_graph=task.compute_graph,
109
- compute_fn=task.compute_fn,
110
- invocation_id=task.invocation_id,
100
+ namespace=output.task.namespace,
101
+ compute_graph=output.task.compute_graph,
102
+ compute_fn=output.task.compute_fn,
103
+ invocation_id=output.task.invocation_id,
111
104
  executor_id=self._executor_id,
112
- task_id=task.id,
105
+ task_id=output.task.id,
113
106
  )
114
107
  output_files: List[Any] = []
115
108
  summary: TaskOutputSummary = TaskOutputSummary()
@@ -0,0 +1,104 @@
1
+ from typing import Any, Dict, Optional
2
+
3
+ from .api_objects import Task
4
+ from .function_executor.function_executor_state import FunctionExecutorState
5
+ from .function_executor.server.function_executor_server_factory import (
6
+ FunctionExecutorServerFactory,
7
+ )
8
+ from .function_executor.single_task_runner import SingleTaskRunner
9
+ from .function_executor.task_input import TaskInput
10
+ from .function_executor.task_output import TaskOutput
11
+
12
+
13
+ class TaskRunner:
14
+ """Routes a task to its container following a scheduling policy.
15
+
16
+ Due to the scheduling policy a task might be blocked for a while."""
17
+
18
+ def __init__(
19
+ self,
20
+ function_executor_server_factory: FunctionExecutorServerFactory,
21
+ base_url: str,
22
+ config_path: Optional[str],
23
+ ):
24
+ self._factory: FunctionExecutorServerFactory = function_executor_server_factory
25
+ self._base_url: str = base_url
26
+ self._config_path: Optional[str] = config_path
27
+ # We don't lock this map cause we never await while reading and modifying it.
28
+ self._function_executor_states: Dict[str, FunctionExecutorState] = {}
29
+
30
+ async def run(self, task_input: TaskInput, logger: Any) -> TaskOutput:
31
+ logger = logger.bind(module=__name__)
32
+ try:
33
+ return await self._run(task_input, logger)
34
+ except Exception as e:
35
+ logger.error(
36
+ "failed running the task",
37
+ exc_info=e,
38
+ )
39
+ return TaskOutput.internal_error(task_input.task)
40
+
41
+ async def _run(self, task_input: TaskInput, logger: Any) -> TaskOutput:
42
+ state = self._get_or_create_state(task_input.task)
43
+ async with state.lock:
44
+ await self._run_task_policy(state, task_input.task)
45
+ return await self._run_task(state, task_input, logger)
46
+
47
+ async def _run_task_policy(self, state: FunctionExecutorState, task: Task) -> None:
48
+ # Current policy for running tasks:
49
+ # - There can only be a single Function Executor per function regardless of function versions.
50
+ # -- If a Function Executor already exists for a different function version then wait until
51
+ # all the tasks finish in the existing Function Executor and then destroy it.
52
+ # -- This prevents failed tasks for different versions of the same function continiously
53
+ # destroying each other's Function Executors.
54
+ # - Each Function Executor rans at most 1 task concurrently.
55
+ await state.wait_running_tasks_less(1)
56
+
57
+ if state.function_id_with_version != _function_id_with_version(task):
58
+ await state.destroy_function_executor()
59
+ state.function_id_with_version = _function_id_with_version(task)
60
+ # At this point the state belongs to the version of the function from the task
61
+ # and there are no running tasks in the Function Executor.
62
+
63
+ def _get_or_create_state(self, task: Task) -> FunctionExecutorState:
64
+ id = _function_id_without_version(task)
65
+ if id not in self._function_executor_states:
66
+ state = FunctionExecutorState(
67
+ function_id_with_version=_function_id_with_version(task),
68
+ function_id_without_version=id,
69
+ )
70
+ self._function_executor_states[id] = state
71
+ return self._function_executor_states[id]
72
+
73
+ async def _run_task(
74
+ self, state: FunctionExecutorState, task_input: TaskInput, logger: Any
75
+ ) -> TaskOutput:
76
+ runner: SingleTaskRunner = SingleTaskRunner(
77
+ function_executor_state=state,
78
+ task_input=task_input,
79
+ function_executor_server_factory=self._factory,
80
+ base_url=self._base_url,
81
+ config_path=self._config_path,
82
+ logger=logger,
83
+ )
84
+ return await runner.run()
85
+
86
+ async def shutdown(self) -> None:
87
+ # When shutting down there's no need to wait for completion of the running
88
+ # FunctionExecutor tasks.
89
+ while self._function_executor_states:
90
+ id, state = self._function_executor_states.popitem()
91
+ # At this point the state is not visible to new tasks.
92
+ # Only ongoing tasks who read it already have a reference to it.
93
+ await state.destroy_function_executor_not_locked()
94
+ # The task running inside the Function Executor will fail because it's destroyed.
95
+ # asyncio tasks waiting to run inside the Function Executor will get cancelled by
96
+ # the caller's shutdown code.
97
+
98
+
99
+ def _function_id_with_version(task: Task) -> str:
100
+ return f"versioned/{task.namespace}/{task.compute_graph}/{task.graph_version}/{task.compute_fn}"
101
+
102
+
103
+ def _function_id_without_version(task: Task) -> str:
104
+ return f"not_versioned/{task.namespace}/{task.compute_graph}/{task.compute_fn}"
@@ -1,4 +1,4 @@
1
- from typing import Optional, Union
1
+ from typing import Iterator, Optional, Union
2
2
 
3
3
  import grpc
4
4
  import structlog
@@ -14,9 +14,14 @@ from .handlers.run_function.request_validator import (
14
14
  RequestValidator as RunTaskRequestValidator,
15
15
  )
16
16
  from .initialize_request_validator import InitializeRequestValidator
17
+ from .invocation_state.invocation_state_proxy_server import (
18
+ InvocationStateProxyServer,
19
+ )
20
+ from .invocation_state.proxied_invocation_state import ProxiedInvocationState
17
21
  from .proto.function_executor_pb2 import (
18
22
  InitializeRequest,
19
23
  InitializeResponse,
24
+ InvocationStateResponse,
20
25
  RunTaskRequest,
21
26
  RunTaskResponse,
22
27
  )
@@ -24,15 +29,14 @@ from .proto.function_executor_pb2_grpc import FunctionExecutorServicer
24
29
 
25
30
 
26
31
  class FunctionExecutorService(FunctionExecutorServicer):
27
- def __init__(self, indexify_server_address: str, config_path: Optional[str]):
28
- self._indexify_server_address = indexify_server_address
29
- self._config_path = config_path
32
+ def __init__(self):
30
33
  self._logger = structlog.get_logger(module=__name__)
31
34
  self._namespace: Optional[str] = None
32
35
  self._graph_name: Optional[str] = None
33
36
  self._graph_version: Optional[int] = None
34
37
  self._function_name: Optional[str] = None
35
38
  self._function: Optional[Union[IndexifyFunction, IndexifyRouter]] = None
39
+ self._invocation_state_proxy_server: Optional[InvocationStateProxyServer] = None
36
40
 
37
41
  def initialize(
38
42
  self, request: InitializeRequest, context: grpc.ServicerContext
@@ -65,6 +69,17 @@ class FunctionExecutorService(FunctionExecutorServicer):
65
69
 
66
70
  return InitializeResponse(success=True)
67
71
 
72
+ def initialize_invocation_state_server(
73
+ self,
74
+ client_responses: Iterator[InvocationStateResponse],
75
+ context: grpc.ServicerContext,
76
+ ):
77
+ self._invocation_state_proxy_server = InvocationStateProxyServer(
78
+ client_responses, self._logger
79
+ )
80
+ self._logger.info("initialized invocation proxy server")
81
+ yield from self._invocation_state_proxy_server.run()
82
+
68
83
  def run_task(
69
84
  self, request: RunTaskRequest, context: grpc.ServicerContext
70
85
  ) -> RunTaskResponse:
@@ -75,12 +90,12 @@ class FunctionExecutorService(FunctionExecutorServicer):
75
90
  RunTaskRequestValidator(request=request).check()
76
91
  return RunTaskHandler(
77
92
  request=request,
78
- namespace=self._namespace,
79
93
  graph_name=self._graph_name,
80
94
  graph_version=self._graph_version,
81
95
  function_name=self._function_name,
82
96
  function=self._function,
97
+ invocation_state=ProxiedInvocationState(
98
+ request.task_id, self._invocation_state_proxy_server
99
+ ),
83
100
  logger=self._logger,
84
- indexify_server_addr=self._indexify_server_address,
85
- config_path=self._config_path,
86
101
  ).run()
@@ -16,6 +16,9 @@ from indexify.functions_sdk.indexify_functions import (
16
16
  IndexifyRouter,
17
17
  RouterCallResult,
18
18
  )
19
+ from indexify.functions_sdk.invocation_state.invocation_state import (
20
+ InvocationState,
21
+ )
19
22
  from indexify.http_client import IndexifyClient
20
23
 
21
24
  from .function_inputs_loader import FunctionInputs, FunctionInputsLoader
@@ -26,14 +29,12 @@ class Handler:
26
29
  def __init__(
27
30
  self,
28
31
  request: RunTaskRequest,
29
- namespace: str,
30
32
  graph_name: str,
31
33
  graph_version: int,
32
34
  function_name: str,
33
35
  function: Union[IndexifyFunction, IndexifyRouter],
36
+ invocation_state: InvocationState,
34
37
  logger: Any,
35
- indexify_server_addr: str,
36
- config_path: Optional[str],
37
38
  ):
38
39
  self._function_name: str = function_name
39
40
  self._logger = logger.bind(
@@ -53,12 +54,7 @@ class Handler:
53
54
  invocation_id=request.graph_invocation_id,
54
55
  graph_name=graph_name,
55
56
  graph_version=str(graph_version),
56
- indexify_client=_indexify_client(
57
- logger=self._logger,
58
- namespace=namespace,
59
- indexify_server_addr=indexify_server_addr,
60
- config_path=config_path,
61
- ),
57
+ invocation_state=invocation_state,
62
58
  ),
63
59
  )
64
60
 
@@ -139,9 +135,14 @@ def _indexify_client(
139
135
 
140
136
 
141
137
  def _is_router(func_wrapper: IndexifyFunctionWrapper) -> bool:
142
- return (
143
- str(type(func_wrapper.indexify_function))
144
- == "<class 'indexify.functions_sdk.indexify_functions.IndexifyRouter'>"
138
+ """Determines if the function is a router.
139
+
140
+ A function is a router if it is an instance of IndexifyRouter or if it is an IndexifyRouter class.
141
+ """
142
+ return str(
143
+ type(func_wrapper.indexify_function)
144
+ ) == "<class 'indexify.functions_sdk.indexify_functions.IndexifyRouter'>" or isinstance(
145
+ func_wrapper.indexify_function, IndexifyRouter
145
146
  )
146
147
 
147
148
 
@@ -0,0 +1,170 @@
1
+ import queue
2
+ import threading
3
+ from typing import Any, Iterator, Optional
4
+
5
+ from indexify.functions_sdk.object_serializer import (
6
+ CloudPickleSerializer,
7
+ get_serializer,
8
+ )
9
+
10
+ from ..proto.function_executor_pb2 import (
11
+ GetInvocationStateRequest,
12
+ InvocationStateRequest,
13
+ InvocationStateResponse,
14
+ SerializedObject,
15
+ SetInvocationStateRequest,
16
+ )
17
+ from .response_validator import ResponseValidator
18
+
19
+
20
+ class InvocationStateProxyServer:
21
+ """A gRPC server that proxies InvocationState calls to the gRPC client.
22
+
23
+ The gRPC client is responsible for the actual implementation of the InvocationState.
24
+ We do the proxying to remove authorization logic and credentials from Function Executor.
25
+ This improves security posture of Function Executor because it may run untrusted code.
26
+ """
27
+
28
+ def __init__(
29
+ self, client_responses: Iterator[InvocationStateResponse], logger: Any
30
+ ):
31
+ self._client_responses: Iterator[InvocationStateResponse] = client_responses
32
+ self._logger: Any = logger.bind(module=__name__)
33
+ self._reciever_thread: threading.Thread = threading.Thread(
34
+ target=self._reciever
35
+ )
36
+ self._request_queue: queue.SimpleQueue = queue.SimpleQueue()
37
+ # This lock protects everything below.
38
+ self._lock: threading.Lock = threading.Lock()
39
+ # Python supports big integers natively so we don't need
40
+ # to be worried about interger overflows.
41
+ self._request_seq_num: int = 0
42
+ # Request ID -> Client Response.
43
+ self._response_map: dict[str, InvocationStateResponse] = {}
44
+ self._new_response: threading.Condition = threading.Condition(self._lock)
45
+
46
+ def run(self) -> Iterator[InvocationStateRequest]:
47
+ # There's no need to implement shutdown of the server and its threads because
48
+ # the server lives while the Function Executor process lives.
49
+ self._reciever_thread.start()
50
+ yield from self._sender()
51
+
52
+ def _reciever(self) -> None:
53
+ self._logger.info("reciever thread started")
54
+ try:
55
+ for response in self._client_responses:
56
+ validator = ResponseValidator(response)
57
+ try:
58
+ validator.check()
59
+ except ValueError as e:
60
+ self._logger.error("invalid response from the client", exc_info=e)
61
+ continue
62
+
63
+ with self._lock:
64
+ self._response_map[response.request_id] = response
65
+ self._new_response.notify_all()
66
+ except Exception as e:
67
+ self._logger.error("error in reciever thread, exiting", exc_info=e)
68
+
69
+ def _sender(self) -> Iterator[InvocationStateRequest]:
70
+ while True:
71
+ yield self._request_queue.get()
72
+ with self._lock:
73
+ # Wait until we get a response for the request.
74
+ # This allows to ensure a serialized order of reads and writes so
75
+ # we can avoid a read returning not previously written value.
76
+ self._new_response.wait()
77
+
78
+ def set(self, task_id: str, key: str, value: Any) -> None:
79
+ with self._lock:
80
+ request_id: str = str(self._request_seq_num)
81
+ self._request_seq_num += 1
82
+
83
+ # We currently use CloudPickleSerializer for function inputs,
84
+ # outputs and invocation state values. This provides consistent UX.
85
+ request = InvocationStateRequest(
86
+ request_id=request_id,
87
+ task_id=task_id,
88
+ set=SetInvocationStateRequest(
89
+ key=key,
90
+ value=SerializedObject(
91
+ content_type=CloudPickleSerializer.content_type,
92
+ bytes=CloudPickleSerializer.serialize(value),
93
+ ),
94
+ ),
95
+ )
96
+ self._request_queue.put(request)
97
+ while request_id not in self._response_map:
98
+ self._new_response.wait()
99
+
100
+ response: InvocationStateResponse = self._response_map.pop(request_id)
101
+ if response.request_id != request_id:
102
+ self._logger.error(
103
+ "response request_id doesn't match actual request_id",
104
+ request_id=request_id,
105
+ response=response,
106
+ )
107
+ raise RuntimeError(
108
+ "response request_id doesn't match actual request_id"
109
+ )
110
+ if not response.HasField("set"):
111
+ self._logger.error(
112
+ "set response is missing in the client response",
113
+ request_id=request_id,
114
+ response=response,
115
+ )
116
+ raise RuntimeError("set response is missing in the client response")
117
+ if not response.success:
118
+ self._logger.error(
119
+ "failed to set the invocation state for key",
120
+ key=key,
121
+ )
122
+ raise RuntimeError("failed to set the invocation state for key")
123
+
124
+ def get(self, task_id: str, key: str) -> Optional[Any]:
125
+ with self._lock:
126
+ request_id: str = str(self._request_seq_num)
127
+ self._request_seq_num += 1
128
+
129
+ request = InvocationStateRequest(
130
+ request_id=request_id,
131
+ task_id=task_id,
132
+ get=GetInvocationStateRequest(
133
+ key=key,
134
+ ),
135
+ )
136
+ self._request_queue.put(request)
137
+ while request_id not in self._response_map:
138
+ self._new_response.wait()
139
+
140
+ response: InvocationStateResponse = self._response_map.pop(request_id)
141
+ if response.request_id != request_id:
142
+ self._logger.error(
143
+ "response request_id doesn't match actual request_id",
144
+ request_id=request_id,
145
+ response=response,
146
+ )
147
+ raise RuntimeError(
148
+ "response request_id doesn't match actual request_id"
149
+ )
150
+ if not response.HasField("get"):
151
+ self._logger.error(
152
+ "get response is missing in the client response",
153
+ request_id=request_id,
154
+ response=response,
155
+ )
156
+ raise RuntimeError("get response is missing in the client response")
157
+ if not response.success:
158
+ self._logger.error(
159
+ "failed to get the invocation state for key",
160
+ key=key,
161
+ )
162
+ raise RuntimeError("failed to get the invocation state for key")
163
+ if not response.get.HasField("value"):
164
+ return None
165
+
166
+ return get_serializer(response.get.value.content_type).deserialize(
167
+ response.get.value.bytes
168
+ if response.get.value.HasField("bytes")
169
+ else response.get.value.string
170
+ )
@@ -0,0 +1,24 @@
1
+ from typing import Any, Optional
2
+
3
+ from indexify.functions_sdk.invocation_state.invocation_state import (
4
+ InvocationState,
5
+ )
6
+
7
+ from .invocation_state_proxy_server import InvocationStateProxyServer
8
+
9
+
10
+ class ProxiedInvocationState(InvocationState):
11
+ """InvocationState that proxies the calls via InvocationStateProxyServer."""
12
+
13
+ def __init__(self, task_id: str, proxy_server: InvocationStateProxyServer):
14
+ self._task_id: str = task_id
15
+ self._proxy_server: InvocationStateProxyServer = proxy_server
16
+
17
+ def set(self, key: str, value: Any) -> None:
18
+ """Set a key-value pair."""
19
+ self._proxy_server.set(self._task_id, key, value)
20
+
21
+ def get(self, key: str, default: Optional[Any] = None) -> Optional[Any]:
22
+ """Get a value by key. If the key does not exist, return the default value."""
23
+ value: Optional[Any] = self._proxy_server.get(self._task_id, key)
24
+ return default if value is None else value
@@ -0,0 +1,29 @@
1
+ from ..proto.function_executor_pb2 import InvocationStateResponse
2
+ from ..proto.message_validator import MessageValidator
3
+
4
+
5
+ class ResponseValidator(MessageValidator):
6
+ def __init__(self, response: InvocationStateResponse):
7
+ self._response = response
8
+
9
+ def check(self):
10
+ """Validates the request.
11
+
12
+ Raises: ValueError: If the response is invalid.
13
+ """
14
+ (
15
+ MessageValidator(self._response)
16
+ .required_field("request_id")
17
+ .required_field("success")
18
+ )
19
+
20
+ if self._response.HasField("set"):
21
+ pass
22
+ elif self._response.HasField("get"):
23
+ (
24
+ MessageValidator(self._response.get)
25
+ .required_field("key")
26
+ .optional_serialized_object("value")
27
+ )
28
+ else:
29
+ raise ValueError(f"Unknown response type: {self._response}")
@@ -33,6 +33,49 @@ message InitializeResponse {
33
33
  optional bool success = 1;
34
34
  }
35
35
 
36
+ message SetInvocationStateRequest {
37
+ optional string key = 1;
38
+ optional SerializedObject value = 2;
39
+ }
40
+
41
+ message SetInvocationStateResponse {}
42
+
43
+ message GetInvocationStateRequest {
44
+ optional string key = 1;
45
+ }
46
+
47
+ message GetInvocationStateResponse {
48
+ optional string key = 1;
49
+ optional SerializedObject value = 2;
50
+ }
51
+
52
+ // InvocationStateRequest is sent by RPC Server to the client
53
+ // to perform actions on a task's graph invocation state.
54
+ message InvocationStateRequest {
55
+ // The ID of the request sent by the client.
56
+ // Must be unique per Function Executor.
57
+ optional string request_id = 1;
58
+ // The ID of the task initiated the request.
59
+ optional string task_id = 2;
60
+ oneof request {
61
+ SetInvocationStateRequest set = 3;
62
+ GetInvocationStateRequest get = 4;
63
+ }
64
+ }
65
+
66
+ // InvocationStateResponse is sent by RPC client to the Server.
67
+ // A response contains the result of the action performed on the
68
+ // task's graph invocation state.
69
+ message InvocationStateResponse {
70
+ // The id of the request this response is for.
71
+ optional string request_id = 1;
72
+ optional bool success = 2;
73
+ oneof response {
74
+ SetInvocationStateResponse set = 3;
75
+ GetInvocationStateResponse get = 4;
76
+ }
77
+ }
78
+
36
79
  message FunctionOutput {
37
80
  repeated SerializedObject outputs = 1;
38
81
  }
@@ -64,6 +107,10 @@ service FunctionExecutor {
64
107
  // once per Function Executor as it can only run a single function.
65
108
  // It should be called before calling RunTask for the function.
66
109
  rpc initialize(InitializeRequest) returns (InitializeResponse);
110
+ // Initializes a server that sends requests to the client to perform actions on
111
+ // a task's graph invocation state. This method is called only once per Function Executor
112
+ // It should be called before calling RunTask for the function.
113
+ rpc initialize_invocation_state_server(stream InvocationStateResponse) returns (stream InvocationStateRequest);
67
114
  // Executes the task defined in the request.
68
115
  // Multiple tasks can be running in parallel.
69
116
  rpc run_task(RunTaskRequest) returns (RunTaskResponse);
@@ -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(\x05H\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"6\n\x12InitializeResponse\x12\x14\n\x07success\x18\x01 \x01(\x08H\x00\x88\x01\x01\x42\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"\xb0\x02\n\x0eRunTaskRequest\x12 \n\x13graph_invocation_id\x18\x04 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x07task_id\x18\x06 \x01(\tH\x01\x88\x01\x01\x12H\n\x0e\x66unction_input\x18\t \x01(\x0b\x32+.function_executor_service.SerializedObjectH\x02\x88\x01\x01\x12M\n\x13\x66unction_init_value\x18\n \x01(\x0b\x32+.function_executor_service.SerializedObjectH\x03\x88\x01\x01\x42\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\xe0\x01\n\x10\x46unctionExecutor\x12i\n\ninitialize\x12,.function_executor_service.InitializeRequest\x1a-.function_executor_service.InitializeResponse\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(\x05H\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"6\n\x12InitializeResponse\x12\x14\n\x07success\x18\x01 \x01(\x08H\x00\x88\x01\x01\x42\n\n\x08_success"\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"\xb0\x02\n\x0eRunTaskRequest\x12 \n\x13graph_invocation_id\x18\x04 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x07task_id\x18\x06 \x01(\tH\x01\x88\x01\x01\x12H\n\x0e\x66unction_input\x18\t \x01(\x0b\x32+.function_executor_service.SerializedObjectH\x02\x88\x01\x01\x12M\n\x13\x66unction_init_value\x18\n \x01(\x0b\x32+.function_executor_service.SerializedObjectH\x03\x88\x01\x01\x42\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'
28
28
  )
29
29
 
30
30
  _globals = globals()
@@ -40,14 +40,26 @@ if not _descriptor._USE_C_DESCRIPTORS:
40
40
  _globals["_INITIALIZEREQUEST"]._serialized_end = 459
41
41
  _globals["_INITIALIZERESPONSE"]._serialized_start = 461
42
42
  _globals["_INITIALIZERESPONSE"]._serialized_end = 515
43
- _globals["_FUNCTIONOUTPUT"]._serialized_start = 517
44
- _globals["_FUNCTIONOUTPUT"]._serialized_end = 595
45
- _globals["_ROUTEROUTPUT"]._serialized_start = 597
46
- _globals["_ROUTEROUTPUT"]._serialized_end = 626
47
- _globals["_RUNTASKREQUEST"]._serialized_start = 629
48
- _globals["_RUNTASKREQUEST"]._serialized_end = 933
49
- _globals["_RUNTASKRESPONSE"]._serialized_start = 936
50
- _globals["_RUNTASKRESPONSE"]._serialized_end = 1305
51
- _globals["_FUNCTIONEXECUTOR"]._serialized_start = 1308
52
- _globals["_FUNCTIONEXECUTOR"]._serialized_end = 1532
43
+ _globals["_SETINVOCATIONSTATEREQUEST"]._serialized_start = 518
44
+ _globals["_SETINVOCATIONSTATEREQUEST"]._serialized_end = 646
45
+ _globals["_SETINVOCATIONSTATERESPONSE"]._serialized_start = 648
46
+ _globals["_SETINVOCATIONSTATERESPONSE"]._serialized_end = 676
47
+ _globals["_GETINVOCATIONSTATEREQUEST"]._serialized_start = 678
48
+ _globals["_GETINVOCATIONSTATEREQUEST"]._serialized_end = 731
49
+ _globals["_GETINVOCATIONSTATERESPONSE"]._serialized_start = 734
50
+ _globals["_GETINVOCATIONSTATERESPONSE"]._serialized_end = 863
51
+ _globals["_INVOCATIONSTATEREQUEST"]._serialized_start = 866
52
+ _globals["_INVOCATIONSTATEREQUEST"]._serialized_end = 1113
53
+ _globals["_INVOCATIONSTATERESPONSE"]._serialized_start = 1116
54
+ _globals["_INVOCATIONSTATERESPONSE"]._serialized_end = 1367
55
+ _globals["_FUNCTIONOUTPUT"]._serialized_start = 1369
56
+ _globals["_FUNCTIONOUTPUT"]._serialized_end = 1447
57
+ _globals["_ROUTEROUTPUT"]._serialized_start = 1449
58
+ _globals["_ROUTEROUTPUT"]._serialized_end = 1478
59
+ _globals["_RUNTASKREQUEST"]._serialized_start = 1481
60
+ _globals["_RUNTASKREQUEST"]._serialized_end = 1785
61
+ _globals["_RUNTASKRESPONSE"]._serialized_start = 1788
62
+ _globals["_RUNTASKRESPONSE"]._serialized_end = 2157
63
+ _globals["_FUNCTIONEXECUTOR"]._serialized_start = 2160
64
+ _globals["_FUNCTIONEXECUTOR"]._serialized_end = 2530
53
65
  # @@protoc_insertion_point(module_scope)