indexify 0.4.6__tar.gz → 0.4.8__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 (71) hide show
  1. {indexify-0.4.6 → indexify-0.4.8}/PKG-INFO +2 -2
  2. {indexify-0.4.6 → indexify-0.4.8}/pyproject.toml +2 -2
  3. {indexify-0.4.6 → indexify-0.4.8}/src/indexify/executor/function_executor/function_executor.py +30 -25
  4. {indexify-0.4.6 → indexify-0.4.8}/src/indexify/executor/function_executor_controller/__init__.py +7 -4
  5. indexify-0.4.8/src/indexify/executor/function_executor_controller/create_function_executor.py +256 -0
  6. {indexify-0.4.6 → indexify-0.4.8}/src/indexify/executor/function_executor_controller/destroy_function_executor.py +1 -1
  7. {indexify-0.4.6 → indexify-0.4.8}/src/indexify/executor/function_executor_controller/events.py +10 -14
  8. {indexify-0.4.6 → indexify-0.4.8}/src/indexify/executor/function_executor_controller/function_executor_controller.py +108 -66
  9. indexify-0.4.8/src/indexify/executor/function_executor_controller/function_executor_startup_output.py +21 -0
  10. indexify-0.4.8/src/indexify/executor/function_executor_controller/loggers.py +105 -0
  11. {indexify-0.4.6 → indexify-0.4.8}/src/indexify/executor/function_executor_controller/message_validators.py +16 -1
  12. {indexify-0.4.6 → indexify-0.4.8}/src/indexify/executor/function_executor_controller/prepare_task.py +3 -3
  13. {indexify-0.4.6 → indexify-0.4.8}/src/indexify/executor/function_executor_controller/run_task.py +19 -27
  14. {indexify-0.4.6 → indexify-0.4.8}/src/indexify/executor/function_executor_controller/task_info.py +2 -3
  15. {indexify-0.4.6 → indexify-0.4.8}/src/indexify/executor/function_executor_controller/task_output.py +12 -24
  16. {indexify-0.4.6 → indexify-0.4.8}/src/indexify/executor/function_executor_controller/upload_task_output.py +7 -7
  17. {indexify-0.4.6 → indexify-0.4.8}/src/indexify/executor/state_reconciler.py +5 -33
  18. {indexify-0.4.6 → indexify-0.4.8}/src/indexify/executor/state_reporter.py +46 -56
  19. {indexify-0.4.6 → indexify-0.4.8}/src/indexify/proto/executor_api.proto +34 -17
  20. indexify-0.4.8/src/indexify/proto/executor_api_pb2.py +90 -0
  21. {indexify-0.4.6 → indexify-0.4.8}/src/indexify/proto/executor_api_pb2.pyi +50 -8
  22. indexify-0.4.6/src/indexify/executor/function_executor_controller/create_function_executor.py +0 -158
  23. indexify-0.4.6/src/indexify/executor/function_executor_controller/loggers.py +0 -57
  24. indexify-0.4.6/src/indexify/proto/executor_api_pb2.py +0 -86
  25. {indexify-0.4.6 → indexify-0.4.8}/README.md +0 -0
  26. {indexify-0.4.6 → indexify-0.4.8}/src/indexify/cli/__init__.py +0 -0
  27. {indexify-0.4.6 → indexify-0.4.8}/src/indexify/cli/build_image.py +0 -0
  28. {indexify-0.4.6 → indexify-0.4.8}/src/indexify/cli/deploy.py +0 -0
  29. {indexify-0.4.6 → indexify-0.4.8}/src/indexify/cli/executor.py +0 -0
  30. {indexify-0.4.6 → indexify-0.4.8}/src/indexify/executor/README.md +0 -0
  31. {indexify-0.4.6 → indexify-0.4.8}/src/indexify/executor/blob_store/blob_store.py +0 -0
  32. {indexify-0.4.6 → indexify-0.4.8}/src/indexify/executor/blob_store/local_fs_blob_store.py +0 -0
  33. {indexify-0.4.6 → indexify-0.4.8}/src/indexify/executor/blob_store/metrics/blob_store.py +0 -0
  34. {indexify-0.4.6 → indexify-0.4.8}/src/indexify/executor/blob_store/s3_blob_store.py +0 -0
  35. {indexify-0.4.6 → indexify-0.4.8}/src/indexify/executor/channel_manager.py +0 -0
  36. {indexify-0.4.6 → indexify-0.4.8}/src/indexify/executor/executor.py +0 -0
  37. {indexify-0.4.6 → indexify-0.4.8}/src/indexify/executor/function_allowlist.py +0 -0
  38. {indexify-0.4.6 → indexify-0.4.8}/src/indexify/executor/function_executor/health_checker.py +0 -0
  39. {indexify-0.4.6 → indexify-0.4.8}/src/indexify/executor/function_executor/invocation_state_client.py +0 -0
  40. {indexify-0.4.6 → indexify-0.4.8}/src/indexify/executor/function_executor/metrics/function_executor.py +0 -0
  41. {indexify-0.4.6 → indexify-0.4.8}/src/indexify/executor/function_executor/metrics/health_checker.py +0 -0
  42. {indexify-0.4.6 → indexify-0.4.8}/src/indexify/executor/function_executor/metrics/invocation_state_client.py +0 -0
  43. {indexify-0.4.6 → indexify-0.4.8}/src/indexify/executor/function_executor/server/client_configuration.py +0 -0
  44. {indexify-0.4.6 → indexify-0.4.8}/src/indexify/executor/function_executor/server/function_executor_server.py +0 -0
  45. {indexify-0.4.6 → indexify-0.4.8}/src/indexify/executor/function_executor/server/function_executor_server_factory.py +0 -0
  46. {indexify-0.4.6 → indexify-0.4.8}/src/indexify/executor/function_executor/server/subprocess_function_executor_server.py +0 -0
  47. {indexify-0.4.6 → indexify-0.4.8}/src/indexify/executor/function_executor/server/subprocess_function_executor_server_factory.py +0 -0
  48. {indexify-0.4.6 → indexify-0.4.8}/src/indexify/executor/function_executor_controller/completed_task_metrics.py +0 -0
  49. {indexify-0.4.6 → indexify-0.4.8}/src/indexify/executor/function_executor_controller/debug_event_loop.py +0 -0
  50. {indexify-0.4.6 → indexify-0.4.8}/src/indexify/executor/function_executor_controller/downloads.py +0 -0
  51. {indexify-0.4.6 → indexify-0.4.8}/src/indexify/executor/function_executor_controller/metrics/completed_task_metrics.py +0 -0
  52. {indexify-0.4.6 → indexify-0.4.8}/src/indexify/executor/function_executor_controller/metrics/downloads.py +0 -0
  53. {indexify-0.4.6 → indexify-0.4.8}/src/indexify/executor/function_executor_controller/metrics/function_executor_controller.py +0 -0
  54. {indexify-0.4.6 → indexify-0.4.8}/src/indexify/executor/function_executor_controller/metrics/run_task.py +0 -0
  55. {indexify-0.4.6 → indexify-0.4.8}/src/indexify/executor/function_executor_controller/metrics/upload_task_output.py +0 -0
  56. {indexify-0.4.6 → indexify-0.4.8}/src/indexify/executor/host_resources/host_resources.py +0 -0
  57. {indexify-0.4.6 → indexify-0.4.8}/src/indexify/executor/host_resources/nvidia_gpu.py +0 -0
  58. {indexify-0.4.6 → indexify-0.4.8}/src/indexify/executor/host_resources/nvidia_gpu_allocator.py +0 -0
  59. {indexify-0.4.6 → indexify-0.4.8}/src/indexify/executor/metrics/channel_manager.py +0 -0
  60. {indexify-0.4.6 → indexify-0.4.8}/src/indexify/executor/metrics/executor.py +0 -0
  61. {indexify-0.4.6 → indexify-0.4.8}/src/indexify/executor/metrics/state_reconciler.py +0 -0
  62. {indexify-0.4.6 → indexify-0.4.8}/src/indexify/executor/metrics/state_reporter.py +0 -0
  63. {indexify-0.4.6 → indexify-0.4.8}/src/indexify/executor/monitoring/handler.py +0 -0
  64. {indexify-0.4.6 → indexify-0.4.8}/src/indexify/executor/monitoring/health_check_handler.py +0 -0
  65. {indexify-0.4.6 → indexify-0.4.8}/src/indexify/executor/monitoring/health_checker/generic_health_checker.py +0 -0
  66. {indexify-0.4.6 → indexify-0.4.8}/src/indexify/executor/monitoring/health_checker/health_checker.py +0 -0
  67. {indexify-0.4.6 → indexify-0.4.8}/src/indexify/executor/monitoring/metrics.py +0 -0
  68. {indexify-0.4.6 → indexify-0.4.8}/src/indexify/executor/monitoring/prometheus_metrics_handler.py +0 -0
  69. {indexify-0.4.6 → indexify-0.4.8}/src/indexify/executor/monitoring/server.py +0 -0
  70. {indexify-0.4.6 → indexify-0.4.8}/src/indexify/executor/monitoring/startup_probe_handler.py +0 -0
  71. {indexify-0.4.6 → indexify-0.4.8}/src/indexify/proto/executor_api_pb2_grpc.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: indexify
3
- Version: 0.4.6
3
+ Version: 0.4.8
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,7 +17,7 @@ Requires-Dist: aiohttp (>=3.11.0,<4.0.0)
17
17
  Requires-Dist: boto3 (>=1.37.30,<2.0.0)
18
18
  Requires-Dist: prometheus-client (>=0.21.1,<0.22.0)
19
19
  Requires-Dist: psutil (>=7.0.0,<8.0.0)
20
- Requires-Dist: tensorlake (==0.2.0)
20
+ Requires-Dist: tensorlake (==0.2.6)
21
21
  Project-URL: Repository, https://github.com/tensorlakeai/indexify
22
22
  Description-Content-Type: text/markdown
23
23
 
@@ -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.4.6"
4
+ version = "0.4.8"
5
5
  description = "Open Source Indexify components and helper tools"
6
6
  authors = ["Tensorlake Inc. <support@tensorlake.ai>"]
7
7
  license = "Apache 2.0"
@@ -25,7 +25,7 @@ prometheus-client = "^0.21.1"
25
25
  psutil = "^7.0.0"
26
26
  # Adds function-executor binary, utils lib, sdk used in indexify-cli commands.
27
27
  # We need to specify the tensorlake version exactly because pip install doesn't respect poetry.lock files.
28
- tensorlake = "0.2.0"
28
+ tensorlake = "0.2.6"
29
29
  # Uncomment the next line to use local tensorlake package (only for development!)
30
30
  # tensorlake = { path = "../tensorlake", develop = true }
31
31
  # pydantic is provided by tensorlake
@@ -1,4 +1,6 @@
1
1
  import asyncio
2
+ from dataclasses import dataclass
3
+ from enum import Enum
2
4
  from typing import Any, Optional
3
5
 
4
6
  import grpc
@@ -56,12 +58,19 @@ from .server.function_executor_server_factory import (
56
58
  )
57
59
 
58
60
 
59
- class FunctionError(RuntimeError):
60
- pass
61
+ class FunctionExecutorInitializationError(Enum):
62
+ FUNCTION_TIMEOUT = 1
63
+ FUNCTION_ERROR = 2
61
64
 
62
65
 
63
- class FunctionTimeoutError(FunctionError):
64
- pass
66
+ @dataclass
67
+ class FunctionExecutorInitializationResult:
68
+ """Result of FunctionExecutor initialization."""
69
+
70
+ # None error means success.
71
+ error: Optional[FunctionExecutorInitializationError] = None
72
+ stdout: Optional[str] = None
73
+ stderr: Optional[str] = None
65
74
 
66
75
 
67
76
  class FunctionExecutor:
@@ -83,7 +92,6 @@ class FunctionExecutor:
83
92
  self._channel: Optional[grpc.aio.Channel] = None
84
93
  self._invocation_state_client: Optional[InvocationStateClient] = None
85
94
  self._health_checker: Optional[HealthChecker] = None
86
- self._initialized = False
87
95
  metric_function_executors_count.inc()
88
96
 
89
97
  async def initialize(
@@ -93,10 +101,9 @@ class FunctionExecutor:
93
101
  base_url: str,
94
102
  config_path: Optional[str],
95
103
  customer_code_timeout_sec: Optional[float] = None,
96
- ):
104
+ ) -> FunctionExecutorInitializationResult:
97
105
  """Creates and initializes a FunctionExecutorServer and all resources associated with it.
98
106
 
99
- Raises FunctionError if the server failed to initialize due to an error in customer owned code or data.
100
107
  Raises an Exception if an internal error occured."""
101
108
  try:
102
109
  with (
@@ -108,9 +115,6 @@ class FunctionExecutor:
108
115
  await self._establish_channel()
109
116
  stub: FunctionExecutorStub = FunctionExecutorStub(self._channel)
110
117
  await _collect_server_info(stub)
111
- await _initialize_server(
112
- stub, initialize_request, customer_code_timeout_sec
113
- )
114
118
  await self._create_invocation_state_client(
115
119
  stub=stub,
116
120
  base_url=base_url,
@@ -118,21 +122,21 @@ class FunctionExecutor:
118
122
  initialize_request=initialize_request,
119
123
  )
120
124
  await self._create_health_checker(self._channel, stub)
121
- self._initialized = True
125
+
126
+ return await _initialize_server(
127
+ stub, initialize_request, customer_code_timeout_sec
128
+ )
122
129
  except Exception:
123
130
  await self.destroy()
124
131
  raise
125
132
 
126
133
  def channel(self) -> grpc.aio.Channel:
127
- self._check_initialized()
128
134
  return self._channel
129
135
 
130
136
  def invocation_state_client(self) -> InvocationStateClient:
131
- self._check_initialized()
132
137
  return self._invocation_state_client
133
138
 
134
139
  def health_checker(self) -> HealthChecker:
135
- self._check_initialized()
136
140
  return self._health_checker
137
141
 
138
142
  async def destroy(self):
@@ -158,10 +162,6 @@ class FunctionExecutor:
158
162
  exc_info=e,
159
163
  )
160
164
 
161
- def _check_initialized(self) -> None:
162
- if not self._initialized:
163
- raise RuntimeError("FunctionExecutor is not initialized")
164
-
165
165
  async def _create_server(self, config: FunctionExecutorServerConfiguration) -> None:
166
166
  with (
167
167
  metric_create_server_errors.count_exceptions(),
@@ -305,7 +305,7 @@ async def _initialize_server(
305
305
  stub: FunctionExecutorStub,
306
306
  initialize_request: InitializeRequest,
307
307
  customer_code_timeout_sec: Optional[float],
308
- ) -> None:
308
+ ) -> FunctionExecutorInitializationResult:
309
309
  with (
310
310
  metric_initialize_rpc_errors.count_exceptions(),
311
311
  metric_initialize_rpc_latency.time(),
@@ -315,15 +315,20 @@ async def _initialize_server(
315
315
  initialize_request,
316
316
  timeout=customer_code_timeout_sec,
317
317
  )
318
+ # TODO: set real stdout and stderr when their proper capturing on FE initialization is implemented.
318
319
  if initialize_response.success:
319
- return
320
- if initialize_response.HasField("customer_error"):
321
- raise FunctionError(initialize_response.customer_error)
320
+ return FunctionExecutorInitializationResult()
321
+ elif initialize_response.HasField("customer_error"):
322
+ return FunctionExecutorInitializationResult(
323
+ error=FunctionExecutorInitializationError.FUNCTION_ERROR,
324
+ stderr=initialize_response.customer_error,
325
+ )
322
326
  else:
323
327
  raise Exception("initialize RPC failed at function executor server")
324
328
  except grpc.aio.AioRpcError as e:
325
329
  if e.code() == grpc.StatusCode.DEADLINE_EXCEEDED:
326
- raise FunctionTimeoutError(
327
- f"Function initialization exceeded its configured timeout of {customer_code_timeout_sec:.3f} sec."
328
- ) from e
330
+ return FunctionExecutorInitializationResult(
331
+ error=FunctionExecutorInitializationError.FUNCTION_TIMEOUT,
332
+ stderr=f"Function initialization exceeded its configured timeout of {customer_code_timeout_sec:.3f} sec.",
333
+ )
329
334
  raise
@@ -1,13 +1,16 @@
1
1
  from .function_executor_controller import FunctionExecutorController
2
- from .loggers import function_executor_logger, task_logger
3
- from .message_validators import validate_function_executor_description, validate_task
2
+ from .loggers import function_executor_logger, task_allocation_logger
3
+ from .message_validators import (
4
+ validate_function_executor_description,
5
+ validate_task_allocation,
6
+ )
4
7
  from .task_output import TaskOutput
5
8
 
6
9
  __all__ = [
7
10
  "function_executor_logger",
8
- "task_logger",
11
+ "task_allocation_logger",
9
12
  "validate_function_executor_description",
10
- "validate_task",
13
+ "validate_task_allocation",
11
14
  "FunctionExecutorController",
12
15
  "TaskOutput",
13
16
  ]
@@ -0,0 +1,256 @@
1
+ import asyncio
2
+ from pathlib import Path
3
+ from typing import Any, Optional, Tuple
4
+
5
+ from tensorlake.function_executor.proto.function_executor_pb2 import (
6
+ InitializeRequest,
7
+ SerializedObject,
8
+ )
9
+
10
+ from indexify.executor.blob_store.blob_store import BLOBStore
11
+ from indexify.executor.function_executor.function_executor import (
12
+ FunctionExecutor,
13
+ FunctionExecutorInitializationError,
14
+ FunctionExecutorInitializationResult,
15
+ )
16
+ from indexify.executor.function_executor.server.function_executor_server_factory import (
17
+ FunctionExecutorServerConfiguration,
18
+ FunctionExecutorServerFactory,
19
+ )
20
+ from indexify.proto.executor_api_pb2 import (
21
+ DataPayload,
22
+ DataPayloadEncoding,
23
+ FunctionExecutorDescription,
24
+ FunctionExecutorTerminationReason,
25
+ )
26
+
27
+ from .downloads import download_graph
28
+ from .events import FunctionExecutorCreated
29
+ from .function_executor_startup_output import FunctionExecutorStartupOutput
30
+ from .upload_task_output import compute_hash
31
+
32
+
33
+ async def create_function_executor(
34
+ function_executor_description: FunctionExecutorDescription,
35
+ function_executor_server_factory: FunctionExecutorServerFactory,
36
+ blob_store: BLOBStore,
37
+ executor_id: str,
38
+ base_url: str,
39
+ config_path: str,
40
+ cache_path: Path,
41
+ logger: Any,
42
+ ) -> FunctionExecutorCreated:
43
+ """Creates a function executor.
44
+
45
+ Doesn't raise any exceptions.
46
+ """
47
+ logger = logger.bind(module=__name__)
48
+ try:
49
+ function_executor, result = await _create_function_executor(
50
+ function_executor_description=function_executor_description,
51
+ function_executor_server_factory=function_executor_server_factory,
52
+ blob_store=blob_store,
53
+ executor_id=executor_id,
54
+ base_url=base_url,
55
+ config_path=config_path,
56
+ cache_path=cache_path,
57
+ logger=logger,
58
+ )
59
+ if result.error is not None:
60
+ await function_executor.destroy()
61
+ function_executor = None
62
+
63
+ return FunctionExecutorCreated(
64
+ function_executor=function_executor,
65
+ output=await _initialization_result_to_fe_creation_output(
66
+ function_executor_description=function_executor_description,
67
+ result=result,
68
+ blob_store=blob_store,
69
+ logger=logger,
70
+ ),
71
+ )
72
+ except BaseException as e:
73
+ if isinstance(e, asyncio.CancelledError):
74
+ logger.info("function executor startup was cancelled")
75
+ return FunctionExecutorCreated(
76
+ function_executor=None,
77
+ output=FunctionExecutorStartupOutput(
78
+ function_executor_description=function_executor_description,
79
+ termination_reason=FunctionExecutorTerminationReason.FUNCTION_EXECUTOR_TERMINATION_REASON_REMOVED_FROM_DESIRED_STATE,
80
+ ),
81
+ )
82
+ else:
83
+ logger.error(
84
+ "failed to create function executor due to platform error",
85
+ exc_info=e,
86
+ )
87
+ return FunctionExecutorCreated(
88
+ function_executor=None,
89
+ output=FunctionExecutorStartupOutput(
90
+ function_executor_description=function_executor_description,
91
+ termination_reason=FunctionExecutorTerminationReason.FUNCTION_EXECUTOR_TERMINATION_REASON_STARTUP_FAILED_INTERNAL_ERROR,
92
+ ),
93
+ )
94
+
95
+
96
+ async def _initialization_result_to_fe_creation_output(
97
+ function_executor_description: FunctionExecutorDescription,
98
+ result: FunctionExecutorInitializationResult,
99
+ blob_store: BLOBStore,
100
+ logger: Any,
101
+ ) -> FunctionExecutorStartupOutput:
102
+ """Converts FunctionExecutorInitializationResult to FunctionExecutorCreationOutput.
103
+
104
+ Uploads stdout and stderr to blob store if they are present. Does only one attempt to do that.
105
+ Doesn't raise any exceptions."""
106
+ termination_reason: FunctionExecutorTerminationReason = None
107
+ if result.error is not None:
108
+ if result.error == FunctionExecutorInitializationError.FUNCTION_ERROR:
109
+ termination_reason = (
110
+ FunctionExecutorTerminationReason.FUNCTION_EXECUTOR_TERMINATION_REASON_STARTUP_FAILED_FUNCTION_ERROR
111
+ )
112
+ elif result.error == FunctionExecutorInitializationError.FUNCTION_TIMEOUT:
113
+ termination_reason = (
114
+ FunctionExecutorTerminationReason.FUNCTION_EXECUTOR_TERMINATION_REASON_STARTUP_FAILED_FUNCTION_TIMEOUT
115
+ )
116
+ else:
117
+ logger.error(
118
+ "unexpected function executor initialization error code",
119
+ error_code=FunctionExecutorInitializationError.name(result.error),
120
+ )
121
+ termination_reason = (
122
+ FunctionExecutorTerminationReason.FUNCTION_EXECUTOR_TERMINATION_REASON_STARTUP_FAILED_INTERNAL_ERROR
123
+ )
124
+
125
+ stdout: Optional[DataPayload] = None
126
+ if result.stdout is not None:
127
+ url = f"{function_executor_description.output_payload_uri_prefix}/{function_executor_description.id}/stdout"
128
+ stdout = await _upload_initialization_output(
129
+ output_name="stdout",
130
+ output=result.stdout,
131
+ output_url=url,
132
+ blob_store=blob_store,
133
+ logger=logger,
134
+ )
135
+
136
+ stderr: Optional[DataPayload] = None
137
+ if result.stderr is not None:
138
+ url = f"{function_executor_description.output_payload_uri_prefix}/{function_executor_description.id}/stderr"
139
+ stderr = await _upload_initialization_output(
140
+ output_name="stderr",
141
+ output=result.stderr,
142
+ output_url=url,
143
+ blob_store=blob_store,
144
+ logger=logger,
145
+ )
146
+
147
+ return FunctionExecutorStartupOutput(
148
+ function_executor_description=function_executor_description,
149
+ termination_reason=termination_reason,
150
+ stdout=stdout,
151
+ stderr=stderr,
152
+ )
153
+
154
+
155
+ async def _upload_initialization_output(
156
+ output_name: str, output: str, output_url: str, blob_store: BLOBStore, logger: Any
157
+ ) -> Optional[DataPayload]:
158
+ """Uploads text to blob store. Returns None if the upload fails.
159
+
160
+ Doesn't raise any exceptions.
161
+ """
162
+ try:
163
+ output_bytes: bytes = output.encode()
164
+ await blob_store.put(output_url, output_bytes, logger)
165
+ logger.info(
166
+ f"function executor initialization output {output_name} uploaded to blob store",
167
+ size=len(output_bytes),
168
+ )
169
+ return DataPayload(
170
+ uri=output_url,
171
+ size=len(output_bytes),
172
+ sha256_hash=compute_hash(output_bytes),
173
+ encoding=DataPayloadEncoding.DATA_PAYLOAD_ENCODING_UTF8_TEXT,
174
+ encoding_version=0,
175
+ )
176
+ except Exception as e:
177
+ logger.error(
178
+ f"failed to upload function executor initialization output {output_name} to blob store",
179
+ exc_info=e,
180
+ )
181
+ return None
182
+
183
+
184
+ async def _create_function_executor(
185
+ function_executor_description: FunctionExecutorDescription,
186
+ function_executor_server_factory: FunctionExecutorServerFactory,
187
+ blob_store: BLOBStore,
188
+ executor_id: str,
189
+ base_url: str,
190
+ config_path: str,
191
+ cache_path: Path,
192
+ logger: Any,
193
+ ) -> Tuple[FunctionExecutor, FunctionExecutorInitializationResult]:
194
+ """Creates a function executor.
195
+
196
+ Raises Exception on platform error.
197
+ """
198
+ graph: SerializedObject = await download_graph(
199
+ function_executor_description=function_executor_description,
200
+ cache_path=cache_path,
201
+ blob_store=blob_store,
202
+ logger=logger,
203
+ )
204
+
205
+ gpu_count: int = 0
206
+ if function_executor_description.resources.HasField("gpu"):
207
+ gpu_count = function_executor_description.resources.gpu.count
208
+
209
+ config: FunctionExecutorServerConfiguration = FunctionExecutorServerConfiguration(
210
+ executor_id=executor_id,
211
+ function_executor_id=function_executor_description.id,
212
+ namespace=function_executor_description.namespace,
213
+ graph_name=function_executor_description.graph_name,
214
+ graph_version=function_executor_description.graph_version,
215
+ function_name=function_executor_description.function_name,
216
+ image_uri=None,
217
+ secret_names=list(function_executor_description.secret_names),
218
+ cpu_ms_per_sec=function_executor_description.resources.cpu_ms_per_sec,
219
+ memory_bytes=function_executor_description.resources.memory_bytes,
220
+ disk_bytes=function_executor_description.resources.disk_bytes,
221
+ gpu_count=gpu_count,
222
+ )
223
+ if function_executor_description.HasField("image_uri"):
224
+ config.image_uri = function_executor_description.image_uri
225
+
226
+ initialize_request: InitializeRequest = InitializeRequest(
227
+ namespace=function_executor_description.namespace,
228
+ graph_name=function_executor_description.graph_name,
229
+ graph_version=function_executor_description.graph_version,
230
+ function_name=function_executor_description.function_name,
231
+ graph=graph,
232
+ )
233
+ customer_code_timeout_sec: Optional[float] = None
234
+ if function_executor_description.HasField("customer_code_timeout_ms"):
235
+ customer_code_timeout_sec = (
236
+ function_executor_description.customer_code_timeout_ms / 1000.0
237
+ )
238
+
239
+ function_executor: FunctionExecutor = FunctionExecutor(
240
+ server_factory=function_executor_server_factory, logger=logger
241
+ )
242
+
243
+ try:
244
+ result: FunctionExecutorInitializationResult = (
245
+ await function_executor.initialize(
246
+ config=config,
247
+ initialize_request=initialize_request,
248
+ base_url=base_url,
249
+ config_path=config_path,
250
+ customer_code_timeout_sec=customer_code_timeout_sec,
251
+ )
252
+ )
253
+ return (function_executor, result)
254
+ except BaseException: # includes asyncio.CancelledError and anything else
255
+ await function_executor.destroy()
256
+ raise
@@ -11,7 +11,7 @@ async def destroy_function_executor(
11
11
  termination_reason: FunctionExecutorTerminationReason,
12
12
  logger: Any,
13
13
  ) -> FunctionExecutorDestroyed:
14
- """Destroys a function executor.
14
+ """Destroys a function executor if it's not None.
15
15
 
16
16
  Doesn't raise any exceptions.
17
17
  """
@@ -2,11 +2,11 @@ from enum import Enum
2
2
  from typing import Optional
3
3
 
4
4
  from indexify.executor.function_executor.function_executor import (
5
- FunctionError,
6
5
  FunctionExecutor,
7
6
  )
8
7
  from indexify.proto.executor_api_pb2 import FunctionExecutorTerminationReason
9
8
 
9
+ from .function_executor_startup_output import FunctionExecutorStartupOutput
10
10
  from .task_info import TaskInfo
11
11
 
12
12
 
@@ -37,21 +37,17 @@ class FunctionExecutorCreated(BaseEvent):
37
37
  """
38
38
  Event indicating that Function Executor got created or failed.
39
39
 
40
- If the error is CustomerError, it indicates an error in customer code.
41
- The function_executor field is None if any errors happened.
40
+ The function_executor field is None if the function executor was not created.
42
41
  """
43
42
 
44
43
  def __init__(
45
44
  self,
45
+ output: FunctionExecutorStartupOutput,
46
46
  function_executor: Optional[FunctionExecutor] = None,
47
- function_error: Optional[FunctionError] = None,
48
- termination_reason: FunctionExecutorTerminationReason = None, # type: Optional[FunctionExecutorTerminationReason]
49
47
  ):
50
48
  super().__init__(EventType.FUNCTION_EXECUTOR_CREATED)
51
49
  self.function_executor: Optional[FunctionExecutor] = function_executor
52
- self.function_error: Optional[FunctionError] = function_error
53
- # Reason for FE termination if failed to create FE (.function_executor is None).
54
- self.termination_reason = termination_reason
50
+ self.output: FunctionExecutorStartupOutput = output
55
51
 
56
52
 
57
53
  class FunctionExecutorDestroyed(BaseEvent):
@@ -107,8 +103,8 @@ class TaskPreparationFinished(BaseEvent):
107
103
  def __str__(self) -> str:
108
104
  return (
109
105
  f"Event(type={self.event_type.name}, "
110
- f"task_id={self.task_info.task.id}, "
111
- f"allocation_id={self.task_info.allocation_id}), "
106
+ f"task_id={self.task_info.allocation.task.id}, "
107
+ f"allocation_id={self.task_info.allocation.allocation_id}), "
112
108
  f"is_success={self.is_success}"
113
109
  )
114
110
 
@@ -147,8 +143,8 @@ class TaskExecutionFinished(BaseEvent):
147
143
  )
148
144
  return (
149
145
  f"Event(type={self.event_type.name}, "
150
- f"task_id={self.task_info.task.id}, "
151
- f"allocation_id={self.task_info.allocation_id}), "
146
+ f"task_id={self.task_info.allocation.task.id}, "
147
+ f"allocation_id={self.task_info.allocation.allocation_id}), "
152
148
  f"function_executor_termination_reason={function_executor_termination_reason_str}"
153
149
  )
154
150
 
@@ -166,7 +162,7 @@ class TaskOutputUploadFinished(BaseEvent):
166
162
  def __str__(self) -> str:
167
163
  return (
168
164
  f"Event(type={self.event_type.name}, "
169
- f"task_id={self.task_info.task.id}, "
170
- f"allocation_id={self.task_info.allocation_id}), "
165
+ f"task_id={self.task_info.allocation.task.id}, "
166
+ f"allocation_id={self.task_info.allocation.allocation_id}), "
171
167
  f"is_success={self.is_success}"
172
168
  )