indexify 0.3.11__py3-none-any.whl → 0.3.13__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.
indexify/cli/cli.py CHANGED
@@ -7,6 +7,7 @@ from tensorlake.utils.logging import (
7
7
  configure_logging_early()
8
8
 
9
9
  import os
10
+ import re
10
11
  import shutil
11
12
  import signal
12
13
  import subprocess
@@ -185,6 +186,9 @@ def executor(
185
186
  executor_cache: Optional[str] = typer.Option(
186
187
  "~/.indexify/executor_cache", help="Path to the executor cache directory"
187
188
  ),
189
+ executor_id: Optional[str] = typer.Option(
190
+ None, help="ID of the executor, if not provided, a random ID will be generated"
191
+ ),
188
192
  # Registred ports range ends at 49151.
189
193
  ports: Tuple[int, int] = typer.Option(
190
194
  (50000, 51000),
@@ -221,9 +225,15 @@ def executor(
221
225
  "At least one function must be specified when not running in development mode"
222
226
  )
223
227
 
228
+ if executor_id is None:
229
+ executor_id = nanoid.generate()
230
+ elif not re.compile(r"^[a-zA-Z0-9_-]{10,}$").match(executor_id):
231
+ raise typer.BadParameter(
232
+ "--executor-id should be at least 10 characters long and only include characters _-[0-9][a-z][A-Z]"
233
+ )
234
+
224
235
  executor_version = version("indexify")
225
- id = nanoid.generate()
226
- logger = structlog.get_logger(module=__name__, executor_id=id)
236
+ logger = structlog.get_logger(module=__name__, executor_id=executor_id)
227
237
 
228
238
  logger.info(
229
239
  "starting executor",
@@ -262,7 +272,7 @@ def executor(
262
272
  )
263
273
 
264
274
  Executor(
265
- id=id,
275
+ id=executor_id,
266
276
  version=executor_version,
267
277
  health_checker=GenericHealthChecker(),
268
278
  code_path=executor_cache,
@@ -1,5 +1,6 @@
1
1
  import asyncio
2
2
  import signal
3
+ import time
3
4
  from pathlib import Path
4
5
  from socket import gethostname
5
6
  from typing import Any, Dict, List, Optional
@@ -23,6 +24,7 @@ from .metrics.executor import (
23
24
  METRIC_TASKS_COMPLETED_OUTCOME_SUCCESS,
24
25
  metric_executor_info,
25
26
  metric_executor_state,
27
+ metric_task_completion_latency,
26
28
  metric_task_outcome_report_latency,
27
29
  metric_task_outcome_report_retries,
28
30
  metric_task_outcome_reports,
@@ -157,26 +159,12 @@ class Executor:
157
159
  """Runs the supplied task.
158
160
 
159
161
  Doesn't raise any Exceptions. All errors are reported to the server."""
162
+ start_time: float = time.monotonic()
160
163
  logger = self._task_logger(task)
161
164
  output: Optional[TaskOutput] = None
162
165
 
163
166
  try:
164
- graph: SerializedObject = await self._downloader.download_graph(task)
165
- input: SerializedObject = await self._downloader.download_input(task)
166
- init_value: Optional[SerializedObject] = (
167
- None
168
- if task.reducer_output_id is None
169
- else (await self._downloader.download_init_value(task))
170
- )
171
- output: TaskOutput = await self._task_runner.run(
172
- TaskInput(
173
- task=task,
174
- graph=graph,
175
- input=input,
176
- init_value=init_value,
177
- ),
178
- logger=logger,
179
- )
167
+ output = await self._run_task_and_get_output(task, logger)
180
168
  logger.info("task execution finished", success=output.success)
181
169
  except Exception as e:
182
170
  output = TaskOutput.internal_error(task)
@@ -189,6 +177,26 @@ class Executor:
189
177
  metric_task_outcome_reports.inc()
190
178
  await self._report_task_outcome(output=output, logger=logger)
191
179
 
180
+ metric_task_completion_latency.observe(time.monotonic() - start_time)
181
+
182
+ async def _run_task_and_get_output(self, task: Task, logger: Any) -> TaskOutput:
183
+ graph: SerializedObject = await self._downloader.download_graph(task)
184
+ input: SerializedObject = await self._downloader.download_input(task)
185
+ init_value: Optional[SerializedObject] = (
186
+ None
187
+ if task.reducer_output_id is None
188
+ else (await self._downloader.download_init_value(task))
189
+ )
190
+ return await self._task_runner.run(
191
+ TaskInput(
192
+ task=task,
193
+ graph=graph,
194
+ input=input,
195
+ init_value=init_value,
196
+ ),
197
+ logger=logger,
198
+ )
199
+
192
200
  async def _report_task_outcome(self, output: TaskOutput, logger: Any) -> None:
193
201
  """Reports the task with the given output to the server.
194
202
 
@@ -3,12 +3,15 @@ from typing import Any, Optional
3
3
 
4
4
  import grpc
5
5
  from tensorlake.function_executor.proto.function_executor_pb2 import (
6
+ InfoRequest,
7
+ InfoResponse,
6
8
  InitializeRequest,
7
9
  InitializeResponse,
8
10
  )
9
11
  from tensorlake.function_executor.proto.function_executor_pb2_grpc import (
10
12
  FunctionExecutorStub,
11
13
  )
14
+ from tensorlake.function_executor.proto.message_validator import MessageValidator
12
15
  from tensorlake.utils.http_client import get_httpx_client
13
16
 
14
17
  from .health_checker import HealthChecker
@@ -36,7 +39,10 @@ from .metrics.function_executor import (
36
39
  metric_destroys,
37
40
  metric_establish_channel_errors,
38
41
  metric_establish_channel_latency,
42
+ metric_function_executor_infos,
39
43
  metric_function_executors_count,
44
+ metric_get_info_rpc_errors,
45
+ metric_get_info_rpc_latency,
40
46
  metric_initialize_rpc_errors,
41
47
  metric_initialize_rpc_latency,
42
48
  )
@@ -96,6 +102,7 @@ class FunctionExecutor:
96
102
  await self._create_server(config)
97
103
  await self._establish_channel()
98
104
  stub: FunctionExecutorStub = FunctionExecutorStub(self._channel)
105
+ await _collect_server_info(stub)
99
106
  await _initialize_server(stub, initialize_request)
100
107
  await self._create_invocation_state_client(
101
108
  stub=stub,
@@ -262,9 +269,29 @@ class FunctionExecutor:
262
269
  self._health_checker = None
263
270
 
264
271
 
272
+ async def _collect_server_info(stub: FunctionExecutorStub) -> None:
273
+ with (
274
+ metric_get_info_rpc_errors.count_exceptions(),
275
+ metric_get_info_rpc_latency.time(),
276
+ ):
277
+ info: InfoResponse = await stub.get_info(InfoRequest())
278
+ validator = MessageValidator(info)
279
+ validator.required_field("version")
280
+ validator.required_field("sdk_version")
281
+ validator.required_field("sdk_language")
282
+ validator.required_field("sdk_language_version")
283
+
284
+ metric_function_executor_infos.labels(
285
+ version=info.version,
286
+ sdk_version=info.sdk_version,
287
+ sdk_language=info.sdk_language,
288
+ sdk_language_version=info.sdk_language_version,
289
+ ).inc()
290
+
291
+
265
292
  async def _initialize_server(
266
293
  stub: FunctionExecutorStub, initialize_request: InitializeRequest
267
- ):
294
+ ) -> None:
268
295
  with (
269
296
  metric_initialize_rpc_errors.count_exceptions(),
270
297
  metric_initialize_rpc_latency.time(),
@@ -78,6 +78,22 @@ metric_destroy_channel_errors: prometheus_client.Counter = prometheus_client.Cou
78
78
  "Number of Function Executor channel destruction errors",
79
79
  )
80
80
 
81
+ # FE get_info RPC metrics.
82
+ metric_get_info_rpc_latency: prometheus_client.Histogram = (
83
+ latency_metric_for_fast_operation(
84
+ "function_executor_get_info_rpc", "Function Executor get_info RPC"
85
+ )
86
+ )
87
+ metric_get_info_rpc_errors: prometheus_client.Counter = prometheus_client.Counter(
88
+ "function_executor_get_info_rpc_errors",
89
+ "Number of Function Executor get_info RPC errors",
90
+ )
91
+ metric_function_executor_infos: prometheus_client.Counter = prometheus_client.Counter(
92
+ "function_executor_infos",
93
+ "Number of Function Executors with particular info",
94
+ ["version", "sdk_version", "sdk_language", "sdk_language_version"],
95
+ )
96
+
81
97
  # FE initialization RPC metrics.
82
98
  metric_initialize_rpc_latency: prometheus_client.Histogram = (
83
99
  latency_metric_for_customer_controlled_operation(
@@ -1,6 +1,9 @@
1
1
  import prometheus_client
2
2
 
3
- from ..monitoring.metrics import latency_metric_for_fast_operation
3
+ from ..monitoring.metrics import (
4
+ latency_metric_for_customer_controlled_operation,
5
+ latency_metric_for_fast_operation,
6
+ )
4
7
 
5
8
  # This file contains all metrics used by Executor.
6
9
 
@@ -31,6 +34,12 @@ metric_tasks_completed.labels(
31
34
  outcome=METRIC_TASKS_COMPLETED_OUTCOME_ERROR_CUSTOMER_CODE
32
35
  )
33
36
  metric_tasks_completed.labels(outcome=METRIC_TASKS_COMPLETED_OUTCOME_ERROR_PLATFORM)
37
+ metric_task_completion_latency: prometheus_client.Histogram = (
38
+ latency_metric_for_customer_controlled_operation(
39
+ "task_completion",
40
+ "task completion from the moment it got fetched until its outcome got reported",
41
+ )
42
+ )
34
43
 
35
44
  # Task outcome reporting metrics.
36
45
  metric_task_outcome_reports: prometheus_client.Counter = prometheus_client.Counter(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: indexify
3
- Version: 0.3.11
3
+ Version: 0.3.13
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
@@ -15,16 +15,9 @@ Classifier: Programming Language :: Python :: 3.11
15
15
  Classifier: Programming Language :: Python :: 3.12
16
16
  Classifier: Programming Language :: Python :: 3.13
17
17
  Requires-Dist: aiohttp (>=3.11.0,<4.0.0)
18
- Requires-Dist: grpcio (==1.70.0)
19
- Requires-Dist: httpx-sse (>=0.4.0,<0.5.0)
20
- Requires-Dist: httpx[http2] (>=0.27,<0.28)
21
- Requires-Dist: nanoid (>=2.0.0,<3.0.0)
22
18
  Requires-Dist: prometheus-client (>=0.21.1,<0.22.0)
23
- Requires-Dist: pydantic (==2.10.4)
24
- Requires-Dist: pyyaml (>=6,<7)
25
19
  Requires-Dist: rich (>=13.9.2,<14.0.0)
26
- Requires-Dist: structlog (>=24.4.0,<25.0.0)
27
- Requires-Dist: tensorlake (>=0.1.20)
20
+ Requires-Dist: tensorlake (>=0.1)
28
21
  Requires-Dist: typer (>=0.12,<0.13)
29
22
  Project-URL: Repository, https://github.com/tensorlakeai/indexify
30
23
  Description-Content-Type: text/markdown
@@ -1,14 +1,14 @@
1
- indexify/cli/cli.py,sha256=c2Y2ozNXKhEtLaAL6wrovGSkN9dGn-MA5F56MPcxwT4,10509
1
+ indexify/cli/cli.py,sha256=SQOVWIr-ZNPilFAVQw1qRC42V8T0VEbVt_E1eClsnpM,10946
2
2
  indexify/executor/README.md,sha256=ozC6_hMkhQQNVCMEpBxwiUALz6lwErPQxNxQfQDqnG4,2029
3
3
  indexify/executor/api_objects.py,sha256=TaYwDoo7EjuLBusxH512-KdvAJRtBwbEP2IObWraabU,1100
4
4
  indexify/executor/downloader.py,sha256=XjaGCzsGM3ex2HxbKGkIsB50OhlXgdnywWGCjUBdW2k,8127
5
- indexify/executor/executor.py,sha256=hij_EkB1_lueakCf3pfi_0O3wxSLYFBkvbpwlsMm-ao,9784
6
- indexify/executor/function_executor/function_executor.py,sha256=BxNhsW0uXxKjdKT5ixMAyIo5F7otdIT2c0FMQ09Lsrs,10234
5
+ indexify/executor/executor.py,sha256=r5do6k7Izp9SIzebgOn2psYjzKZmNqHx3iuTW3vWrhg,10035
6
+ indexify/executor/function_executor/function_executor.py,sha256=sQ5FOdrjybDDsjagghlfjV06IXTpWWDBSTHqQXI-w9M,11245
7
7
  indexify/executor/function_executor/function_executor_state.py,sha256=IWPLWa7LaN0Eq8PDu-0kFzkuKJB0COShu7wCO1oyiNA,3141
8
8
  indexify/executor/function_executor/function_executor_states_container.py,sha256=x4hlF7ZBWswk9dkA06Rvgeqar6H9TWjZ7Etyy2CzBDE,2682
9
9
  indexify/executor/function_executor/health_checker.py,sha256=YT24ajPLdYOpdt2UNyJGviGbivCDXIJpeQOxQofcl50,3258
10
10
  indexify/executor/function_executor/invocation_state_client.py,sha256=p-xgM4__cHR1ApvMV9hShrGWee_Je0VDhICZUGjpQY4,9644
11
- indexify/executor/function_executor/metrics/function_executor.py,sha256=y36cgZEp949HPCBVFF6uFQIxk2PvnSI74QSbxcAalI4,5659
11
+ indexify/executor/function_executor/metrics/function_executor.py,sha256=KHzf4cMh_HqnOAE7TZ6_oIqUH3nExOkt9LSekAEN69w,6304
12
12
  indexify/executor/function_executor/metrics/function_executor_state.py,sha256=M7cMA7JY8_8FW9xjuSqtp6o2xxUgB31LJowo7kzcexg,352
13
13
  indexify/executor/function_executor/metrics/function_executor_state_container.py,sha256=6rrAfml-TivjkHatCM4BLY7jmVs523Wzb6QIysncc-0,302
14
14
  indexify/executor/function_executor/metrics/health_checker.py,sha256=EaeIYJPrQ-qqNMGZVGkvjPoeQSCl4FzPKXEv3Cly1NE,456
@@ -23,7 +23,7 @@ indexify/executor/function_executor/single_task_runner.py,sha256=Td28vF6pNemKZEs
23
23
  indexify/executor/function_executor/task_input.py,sha256=wSrHR4m0juiGClQyeVdhRC37QzDt6Rrjq-ZXJkfBi9k,584
24
24
  indexify/executor/function_executor/task_output.py,sha256=Qg7vojYi0WmeHRf2qlC-5h46jcwis13EgiN63OmWpcg,1229
25
25
  indexify/executor/metrics/downloader.py,sha256=lctPh8xjkXeLEFJnl1hNrD1yEhLhIl5sggsR4Yoe_Zc,2746
26
- indexify/executor/metrics/executor.py,sha256=UkJTa8NerbdC1p7jrwl4obLUS2xGSzEYwONPZpoKhT0,2064
26
+ indexify/executor/metrics/executor.py,sha256=ua-Vv_k1CB4juJdF7tEBQbBMksqWAA3iXKKMKXZUCLk,2369
27
27
  indexify/executor/metrics/task_fetcher.py,sha256=iJEwCLzYr2cuz7hRvNiqaa2nvQP4OrA0hm0iJY0YKG0,736
28
28
  indexify/executor/metrics/task_reporter.py,sha256=zUA9RpkSgx5lG_ZqDDuela5VuhtsnC0IKoQcEvHND0Y,730
29
29
  indexify/executor/metrics/task_runner.py,sha256=o5ERNePKPmVKknFoSZUr-r597dEOOWvWn3ocbiL2jxI,1699
@@ -40,7 +40,7 @@ indexify/executor/runtime_probes.py,sha256=bo6Dq6AGZpJH099j0DHtVSDEH80tv3j9MXf3V
40
40
  indexify/executor/task_fetcher.py,sha256=NpFfHgaY99bSL-K2D5kcDAMNUG2FArq0-qF_mgF-LBQ,3375
41
41
  indexify/executor/task_reporter.py,sha256=t7FzSKV7fG1fEtxyTcFh-sTWZ8WXApbG-qXaAMq_gRQ,7363
42
42
  indexify/executor/task_runner.py,sha256=tcS7hHdSVQexC3XEIsDTwX51bJK4iSQqC8CbWeNoud0,5590
43
- indexify-0.3.11.dist-info/METADATA,sha256=Uv7Dj89CyUGzdH4_c7YKSQNMxJSGzkXCXt3huTC4pR8,1428
44
- indexify-0.3.11.dist-info/WHEEL,sha256=RaoafKOydTQ7I_I3JTrPCg6kUmTgtm4BornzOqyEfJ8,88
45
- indexify-0.3.11.dist-info/entry_points.txt,sha256=GU9wmsgvN7nQw3N2X0PMYn1RSvF6CrhH9RuC2D8d3Gk,53
46
- indexify-0.3.11.dist-info/RECORD,,
43
+ indexify-0.3.13.dist-info/METADATA,sha256=zaJ7N4LFWTxgUPXsA01xxhPY8Dvo8D7tcPwNH6x2F5U,1158
44
+ indexify-0.3.13.dist-info/WHEEL,sha256=RaoafKOydTQ7I_I3JTrPCg6kUmTgtm4BornzOqyEfJ8,88
45
+ indexify-0.3.13.dist-info/entry_points.txt,sha256=GU9wmsgvN7nQw3N2X0PMYn1RSvF6CrhH9RuC2D8d3Gk,53
46
+ indexify-0.3.13.dist-info/RECORD,,