indexify 0.4.22__py3-none-any.whl → 0.4.23__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 (35) hide show
  1. indexify/cli/executor.py +2 -9
  2. indexify/executor/blob_store/blob_store.py +110 -26
  3. indexify/executor/blob_store/local_fs_blob_store.py +41 -1
  4. indexify/executor/blob_store/metrics/blob_store.py +87 -15
  5. indexify/executor/blob_store/s3_blob_store.py +112 -1
  6. indexify/executor/function_executor/function_executor.py +32 -56
  7. indexify/executor/function_executor/invocation_state_client.py +10 -3
  8. indexify/executor/function_executor/server/function_executor_server_factory.py +0 -1
  9. indexify/executor/function_executor_controller/create_function_executor.py +129 -116
  10. indexify/executor/function_executor_controller/downloads.py +34 -86
  11. indexify/executor/function_executor_controller/events.py +13 -7
  12. indexify/executor/function_executor_controller/finalize_task.py +184 -0
  13. indexify/executor/function_executor_controller/function_executor_controller.py +121 -78
  14. indexify/executor/function_executor_controller/message_validators.py +10 -3
  15. indexify/executor/function_executor_controller/metrics/downloads.py +8 -52
  16. indexify/executor/function_executor_controller/metrics/finalize_task.py +20 -0
  17. indexify/executor/function_executor_controller/metrics/prepare_task.py +18 -0
  18. indexify/executor/function_executor_controller/prepare_task.py +232 -14
  19. indexify/executor/function_executor_controller/run_task.py +77 -61
  20. indexify/executor/function_executor_controller/task_info.py +4 -7
  21. indexify/executor/function_executor_controller/task_input.py +21 -0
  22. indexify/executor/function_executor_controller/task_output.py +26 -35
  23. indexify/executor/function_executor_controller/terminate_function_executor.py +6 -1
  24. indexify/executor/logging.py +69 -0
  25. indexify/executor/monitoring/metrics.py +22 -0
  26. indexify/proto/executor_api.proto +11 -3
  27. indexify/proto/executor_api_pb2.py +54 -54
  28. indexify/proto/executor_api_pb2.pyi +8 -1
  29. {indexify-0.4.22.dist-info → indexify-0.4.23.dist-info}/METADATA +6 -7
  30. {indexify-0.4.22.dist-info → indexify-0.4.23.dist-info}/RECORD +32 -30
  31. indexify/executor/function_executor_controller/function_executor_startup_output.py +0 -21
  32. indexify/executor/function_executor_controller/metrics/upload_task_output.py +0 -39
  33. indexify/executor/function_executor_controller/upload_task_output.py +0 -274
  34. {indexify-0.4.22.dist-info → indexify-0.4.23.dist-info}/WHEEL +0 -0
  35. {indexify-0.4.22.dist-info → indexify-0.4.23.dist-info}/entry_points.txt +0 -0
@@ -1,50 +1,52 @@
1
1
  indexify/cli/__init__.py,sha256=ELFLx_Z_oWm30jwOpYjbD6Ori3Nzz4ldkvmGVK7QMgw,426
2
2
  indexify/cli/build_image.py,sha256=QLhhYz8WZtOL_xm1LvviTNJucGnYjksnMIVmeEQ8zzA,2505
3
3
  indexify/cli/deploy.py,sha256=0cs68KXH4Cw0KmYAoqYZ60oErQ5J9LI0KbohSG_Mj8g,1847
4
- indexify/cli/executor.py,sha256=UgKPQaObIJVFht0niLEsw8y4k8XBF3siWpTig2_nV8U,6584
4
+ indexify/cli/executor.py,sha256=0go8YUPFCwg77pYbCaoWuPraqW7KBgZ6Fyx6sQzT4aM,6286
5
5
  indexify/executor/README.md,sha256=ozC6_hMkhQQNVCMEpBxwiUALz6lwErPQxNxQfQDqnG4,2029
6
- indexify/executor/blob_store/blob_store.py,sha256=XViw_KRfFSNqwcFYwMZixZF-EYCjXK2AQHdt0xh4UVo,2368
7
- indexify/executor/blob_store/local_fs_blob_store.py,sha256=6LexqMBGXp8f6Ka95R6xMIUyDutrZJABOMNcp-ssa98,1809
8
- indexify/executor/blob_store/metrics/blob_store.py,sha256=5_xiPREeHWFtxFh1NupDsF8zP4pmUPgLNNn-UE9Uzvc,1008
9
- indexify/executor/blob_store/s3_blob_store.py,sha256=G3B_V3gUE7XbUY42lDtBczUKuA7q8S7MD43tx1aHrJo,3445
6
+ indexify/executor/blob_store/blob_store.py,sha256=lrSGTZa_H4Cs1BFwADp-aluvD3LpmE1XO76ZJMX5alU,5798
7
+ indexify/executor/blob_store/local_fs_blob_store.py,sha256=nRFawLMbOCCFlCIx2ccmhalFjpfdhMRL2pE2rgV0VH8,3245
8
+ indexify/executor/blob_store/metrics/blob_store.py,sha256=3lmLU8q4Yx87RIYcy56nmFiNQTPY94pB12ht7X6MyhA,3811
9
+ indexify/executor/blob_store/s3_blob_store.py,sha256=wJlDBTTaq48Vp1I0LvP2958b1Xe8esvarkr5PVRawU0,7609
10
10
  indexify/executor/channel_manager.py,sha256=ihKfWJmUqQvh4UKXewZLzyJWW_f50P4fnwPqPonrozw,6651
11
11
  indexify/executor/executor.py,sha256=rM7BmJDqC_YwdwPfDGFGiFO2WxOW3Nj8Z7rwRw8UcFk,6353
12
12
  indexify/executor/function_allowlist.py,sha256=PCelCW6qIe_2sH11BCKr7LDqarRV5kwNsrfB2EV7Zwo,1772
13
- indexify/executor/function_executor/function_executor.py,sha256=Hz_dT_2i1m9akUGfULWQpDlMsn0CI1AX4Mdt7-oOknI,13598
13
+ indexify/executor/function_executor/function_executor.py,sha256=MrbGEqsxpzLVOrEAH_Kmm3cW3q_5h-GYUIkenuPvyT4,12288
14
14
  indexify/executor/function_executor/health_checker.py,sha256=IxE0jnC99K_lvnizFLjXqS1942H8-FNAN4AlhLIjg2Y,6373
15
- indexify/executor/function_executor/invocation_state_client.py,sha256=YYJElvpkCH-OmBXplLJX59dwMZELMHXSDR4VLLn1u7k,10939
15
+ indexify/executor/function_executor/invocation_state_client.py,sha256=q3YWnoTCYCtBrCOBlLd3b13_vwzibFOJJBHHami28Yc,11269
16
16
  indexify/executor/function_executor/metrics/function_executor.py,sha256=TDksxLRJr-P9ZKhF2Orsaxzzb4lVIBxFEjd_9Zv53Ng,6313
17
17
  indexify/executor/function_executor/metrics/health_checker.py,sha256=EaeIYJPrQ-qqNMGZVGkvjPoeQSCl4FzPKXEv3Cly1NE,456
18
18
  indexify/executor/function_executor/metrics/invocation_state_client.py,sha256=6FCW6rXHVZZSmwLquZdpjgQPSmE_99naDLke5rZiwMI,1867
19
19
  indexify/executor/function_executor/server/client_configuration.py,sha256=gOywMus0cotlX6NKIadEJwvOmBE-LbGE_wvoMi5-HzY,994
20
20
  indexify/executor/function_executor/server/function_executor_server.py,sha256=_DLivLDikupZusRk8gVWDk7fWPT9XjZ4un1yWSlOObs,883
21
- indexify/executor/function_executor/server/function_executor_server_factory.py,sha256=tAyBDNq6UXmHM4PUXqBkXCSGer6MgMaV1_zoruHPJVg,1843
21
+ indexify/executor/function_executor/server/function_executor_server_factory.py,sha256=pZ3tQoaeWP2NDaR-A0PUYmzrBz768U2b9ENBFQG1INg,1814
22
22
  indexify/executor/function_executor/server/subprocess_function_executor_server.py,sha256=JekDOqF7oFD4J6zcN3xB0Dxd1cgpEXMOsb_rKZOeBlI,668
23
23
  indexify/executor/function_executor/server/subprocess_function_executor_server_factory.py,sha256=w5aGQPHWLpixlP9-BbZu6oL_muMA95-hr7WKVxiEL7Q,4303
24
24
  indexify/executor/function_executor_controller/__init__.py,sha256=VPuuBEYOKf7OWyPPjy-jGOv-d5xJqHvkJfFT_oj-AsE,492
25
25
  indexify/executor/function_executor_controller/completed_task_metrics.py,sha256=MhnC-ddgmTK4yTsuZxgTKnqZ-YSVeWn2EhbbiggsSKk,3664
26
- indexify/executor/function_executor_controller/create_function_executor.py,sha256=DA8niVgftxSE_OYARw7nLSJujVu8HDzHh1EW_cXquC0,9766
26
+ indexify/executor/function_executor_controller/create_function_executor.py,sha256=wCMlECcXZ9FMF0NlXqww6lbHRwy71w204q_Rh6nFoOE,10694
27
27
  indexify/executor/function_executor_controller/debug_event_loop.py,sha256=VJOKe_c9HjIDVCjhMY3Yqyeq1tAM1eVa2chZa6CMf-U,1016
28
- indexify/executor/function_executor_controller/downloads.py,sha256=XjCUmLY_jrI3AlnXC7aDwwKWTvsQjV7I9AXzrbIeY6c,7063
29
- indexify/executor/function_executor_controller/events.py,sha256=r2K3k9Nnkzh0j6HHZC0DxOdQ3HtCmzt4eN2DIwTa7NM,5456
30
- indexify/executor/function_executor_controller/function_executor_controller.py,sha256=Vsgfq8FocVjAhJOK2KHQg6jFwizUl9NJUME6r3wb0no,37595
31
- indexify/executor/function_executor_controller/function_executor_startup_output.py,sha256=PXg2r440kqHI3oHGZbb58ehuAuW_fmEdxLTAa-0V3p4,715
28
+ indexify/executor/function_executor_controller/downloads.py,sha256=B2dbaa6osp1_vCQ6WY_9znAca3Z2qqVzQAF2av3v8Pg,5304
29
+ indexify/executor/function_executor_controller/events.py,sha256=G5WX8Qgyv5LuY0IkiFWtvUmNWtuqih34Kt9qTFVfYVU,5639
30
+ indexify/executor/function_executor_controller/finalize_task.py,sha256=letfBqGXPTubvOfbRg7cvdgtvrwkSnSezx4XRknYvKM,6624
31
+ indexify/executor/function_executor_controller/function_executor_controller.py,sha256=uBlQgSGIuVlxAQnT7Qs-T-8c5vABeITFCu_oY9-vNmU,39132
32
32
  indexify/executor/function_executor_controller/loggers.py,sha256=zEY2nt15gboX3SX6Kh1xjeCljZJZSE4lp27qNrg8yPY,3637
33
- indexify/executor/function_executor_controller/message_validators.py,sha256=aNiZhYA87pnxUJtZKvKGDt40rfox-TYH2J6mW7o-Pkw,2981
33
+ indexify/executor/function_executor_controller/message_validators.py,sha256=7Hgu6GItKU5_zz_YHtHT5NlWIHnvof2P-o_21pto9vU,3128
34
34
  indexify/executor/function_executor_controller/metrics/completed_task_metrics.py,sha256=53EGBCLwCEV-RBBeyLPTElrtcveaEM0Fwxs9NmC1Hn8,2724
35
- indexify/executor/function_executor_controller/metrics/downloads.py,sha256=KOVTE2OZPCewnCooPyCK1maKe9ddMPvBFp7D_igqugQ,2708
35
+ indexify/executor/function_executor_controller/metrics/downloads.py,sha256=G8UUDfnzmiK_26OvZYTqH0KgNb3kI-0TgzGLFEuSEFc,892
36
+ indexify/executor/function_executor_controller/metrics/finalize_task.py,sha256=KlJ9o3DQ8VSWNBpMrugr0CT7sSZm2J1LH6lvZhzsQ6E,743
36
37
  indexify/executor/function_executor_controller/metrics/function_executor_controller.py,sha256=gyIZHbdsNSnrA6z4WDaRAunNolFrbbg1pa8JaL_ODNE,2666
38
+ indexify/executor/function_executor_controller/metrics/prepare_task.py,sha256=7nHuerFWGqRCRqtgpL3vJVs-DHwxwFhBZaGjrtfOlys,764
37
39
  indexify/executor/function_executor_controller/metrics/run_task.py,sha256=ZFv_nw5_pKUJoTaavSyzdglQKW4uvC2XyK8S6xi9xLQ,1064
38
- indexify/executor/function_executor_controller/metrics/upload_task_output.py,sha256=Ppf8NujCNbQzFelJiuh8Sutcjty7hnkFz1dWQLYIgQI,1464
39
- indexify/executor/function_executor_controller/prepare_task.py,sha256=AKbo_H_5pOKdxFKKkzdOb1WhQ0XT-4Qm9D3iIsukyMU,1247
40
- indexify/executor/function_executor_controller/run_task.py,sha256=2qpP6YwKoi_sEWDJIJ43EyBgO3Z3yYDVFpncB01DQf4,14586
41
- indexify/executor/function_executor_controller/task_info.py,sha256=ZEdypd8QVmYbrLt1186Ed9YEQwrO0Sx_hKH0QLg1DVY,1181
42
- indexify/executor/function_executor_controller/task_output.py,sha256=krwJC8GgVWYM_b_v5dHtVytsH3mHz3Qi7REiEM2D7nA,7358
43
- indexify/executor/function_executor_controller/terminate_function_executor.py,sha256=YLDlKoanfUBcy7A9ydCYdUsDwApjcTTn1o4tjNVN_QA,1281
44
- indexify/executor/function_executor_controller/upload_task_output.py,sha256=fEZm5eodx5rNLQYFhmdkMDD9qjX3_wKo64x4aUKTu34,10403
40
+ indexify/executor/function_executor_controller/prepare_task.py,sha256=9vUPrbaz5AQVSbe0LvTOD7lkn-02P_UvMQyjZmjhGHo,9170
41
+ indexify/executor/function_executor_controller/run_task.py,sha256=kbQGhopCzbARczzZ4zXAvWkgWLlI9kMXSVXSOOrssos,15483
42
+ indexify/executor/function_executor_controller/task_info.py,sha256=ufhb4PvQuXyY4JUlddNyN2bJQdUeGlMTMIRlKz_WzXc,1015
43
+ indexify/executor/function_executor_controller/task_input.py,sha256=PHCzqpjzTzw4TJTn6wncon3P08EiTVRJazEYRbTqDu8,876
44
+ indexify/executor/function_executor_controller/task_output.py,sha256=kDOm9wlzNHMaXp3er0SbWS1D5NI3mx9reVgqbmlCPA8,7130
45
+ indexify/executor/function_executor_controller/terminate_function_executor.py,sha256=2eqcpAo5brfMPFtYCpTlI3FJ6ESRU9xUzbXDFU0lcP4,1561
45
46
  indexify/executor/host_resources/host_resources.py,sha256=eUyP05EX7QdOtQ5vbX_KCpvnBS2B7fl06UWeF9Oigns,3813
46
47
  indexify/executor/host_resources/nvidia_gpu.py,sha256=uTCkLXnozZSpax8VApt0QMMM9YcBUK9eggYpwmLz09I,3308
47
48
  indexify/executor/host_resources/nvidia_gpu_allocator.py,sha256=AOcXKglLyRD-GrZzyCoi_oDRJoaOhFKWBSlUOxHeAP8,2114
49
+ indexify/executor/logging.py,sha256=e-Hs64bOy4mGVgAGHLohleqP-COy8h2JJhRv6RGjcV4,2256
48
50
  indexify/executor/metrics/channel_manager.py,sha256=1dU9bzF3xqBy1nY9Sc66GfQQWnWZSNip4lEH1vjoWdI,648
49
51
  indexify/executor/metrics/executor.py,sha256=8dJXmyGqKlBSrPuyWXW7O2I21uxQ687l-2dYTvz4fmk,398
50
52
  indexify/executor/metrics/state_reconciler.py,sha256=BSlRgvgtwih6QcYrsFU5P2ylaXAsC_X70DbzDuv9NsU,584
@@ -54,17 +56,17 @@ indexify/executor/monitoring/health_check_handler.py,sha256=e1pEtWFKaVs6H57Z4YLe
54
56
  indexify/executor/monitoring/health_checker/generic_health_checker.py,sha256=vJRV879GrdZFqwTnM9pRLA97LRMutGz2sWRy-KS-tfg,1493
55
57
  indexify/executor/monitoring/health_checker/health_checker.py,sha256=B-Q4KM1iEUSMA2fr9PBhBLdA7sYII_NuTRmPuRILGSo,665
56
58
  indexify/executor/monitoring/health_checker/metrics/health_checker.py,sha256=50JS4JaOdAgSk7iYaBV4J3tGXkRTzmIVR_jVOV66YOc,129
57
- indexify/executor/monitoring/metrics.py,sha256=Dx2wPcTKvbd5Y5rGOfeyscFtAQ2DZ16_s5BX6d4nhI8,6660
59
+ indexify/executor/monitoring/metrics.py,sha256=5BpNqDBDQiL2K962WDPQU2eSo5zD6I9vF2flGyBejts,7388
58
60
  indexify/executor/monitoring/prometheus_metrics_handler.py,sha256=KiGqSf7rkXTfbDwThyXFpFe2jnuZD5q-5SBP_0GDo8Y,591
59
61
  indexify/executor/monitoring/server.py,sha256=yzdYhcxnmY6uTQUMt3vatF5jilN52ZtfFseOmHyQpTo,1254
60
62
  indexify/executor/monitoring/startup_probe_handler.py,sha256=zXXsBU15SMlBx1bSFpxWDfed1VHtKKnwvLQ8-frpG98,425
61
63
  indexify/executor/state_reconciler.py,sha256=hPTjCUkXQV0HIwa5JczYpb5gvTGonQpkxqOvQXf-QU4,20057
62
64
  indexify/executor/state_reporter.py,sha256=zf5UBhBZVv9SQ1Ju_bY8w6D_t1hBZ5YVXhjeFMEgRms,15208
63
- indexify/proto/executor_api.proto,sha256=Zvx5eTz_QFHCX2udzOwHZD-ncJfDlXeKVbNBhsagChI,12168
64
- indexify/proto/executor_api_pb2.py,sha256=iSiO2Aw7vQV5Jp7IoSeIC8q32WjE-s-AZ0Hc5MwI0uI,16266
65
- indexify/proto/executor_api_pb2.pyi,sha256=oReajQdT2yNPS4kZd3gDMBdbseHv-gU15fpeT0v3ZBs,22819
65
+ indexify/proto/executor_api.proto,sha256=yYLx3LTx2hGDFJX8myCosS3_eAWUMWdsWt59PH7O-QQ,12864
66
+ indexify/proto/executor_api_pb2.py,sha256=ZrJg5ivsv-Kn8QRRsFWupdKCo6Eru6UNR8iYRqo5AhE,16458
67
+ indexify/proto/executor_api_pb2.pyi,sha256=YfKsaIYaePZkpLTk_toAsWfH50xOOYfjjU3aWsci9w0,23151
66
68
  indexify/proto/executor_api_pb2_grpc.py,sha256=u9GEQV4nm_GvApRxjVo806CkgBMBVReb5IVrcaDaliY,7520
67
- indexify-0.4.22.dist-info/METADATA,sha256=poiBPNliE54kWJyR5_FSISz-gWYYn6wXIzmgmn-Es3k,1354
68
- indexify-0.4.22.dist-info/WHEEL,sha256=RaoafKOydTQ7I_I3JTrPCg6kUmTgtm4BornzOqyEfJ8,88
69
- indexify-0.4.22.dist-info/entry_points.txt,sha256=rMJqbE5KPZIXTPIfAtVIM4zpUElqYVgEYd6i7N23zzg,49
70
- indexify-0.4.22.dist-info/RECORD,,
69
+ indexify-0.4.23.dist-info/METADATA,sha256=n3Un6Bc8Ei9Agb7QCV7juOL0PjJkW2suOlaUQIax4tw,1287
70
+ indexify-0.4.23.dist-info/WHEEL,sha256=RaoafKOydTQ7I_I3JTrPCg6kUmTgtm4BornzOqyEfJ8,88
71
+ indexify-0.4.23.dist-info/entry_points.txt,sha256=rMJqbE5KPZIXTPIfAtVIM4zpUElqYVgEYd6i7N23zzg,49
72
+ indexify-0.4.23.dist-info/RECORD,,
@@ -1,21 +0,0 @@
1
- from typing import Optional
2
-
3
- from indexify.proto.executor_api_pb2 import (
4
- DataPayload,
5
- FunctionExecutorDescription,
6
- FunctionExecutorTerminationReason,
7
- )
8
-
9
-
10
- class FunctionExecutorStartupOutput:
11
- def __init__(
12
- self,
13
- function_executor_description: FunctionExecutorDescription,
14
- termination_reason: FunctionExecutorTerminationReason = None, # None if FE created successfully (wasn't terminated)
15
- stdout: Optional[DataPayload] = None,
16
- stderr: Optional[DataPayload] = None,
17
- ):
18
- self.function_executor_description = function_executor_description
19
- self.termination_reason = termination_reason
20
- self.stdout = stdout
21
- self.stderr = stderr
@@ -1,39 +0,0 @@
1
- import prometheus_client
2
-
3
- from indexify.executor.monitoring.metrics import latency_metric_for_fast_operation
4
-
5
- # Task output upload metrics.
6
- metric_task_output_uploads: prometheus_client.Counter = prometheus_client.Counter(
7
- "task_output_uploads",
8
- "Number of task output uploads",
9
- )
10
- metric_tasks_uploading_outputs: prometheus_client.Gauge = prometheus_client.Gauge(
11
- "tasks_uploading_output",
12
- "Number of tasks currently uploading their outputs",
13
- )
14
- metric_task_output_upload_latency: prometheus_client.Histogram = (
15
- latency_metric_for_fast_operation("task_output_upload", "task output upload")
16
- )
17
- metric_task_output_upload_retries: prometheus_client.Counter = (
18
- prometheus_client.Counter(
19
- "tasks_output_upload_retries", "Number of task output upload retries"
20
- )
21
- )
22
-
23
- # Metrics for individual blob store operations.
24
- metric_task_output_blob_store_uploads: prometheus_client.Counter = (
25
- prometheus_client.Counter(
26
- "task_output_blob_store_uploads", "Number of task output uploads to blob store"
27
- )
28
- )
29
- metric_task_output_blob_store_upload_errors: prometheus_client.Counter = (
30
- prometheus_client.Counter(
31
- "task_output_blob_store_upload_errors",
32
- "Number of failed task output uploads to blob store",
33
- )
34
- )
35
- metric_task_output_blob_store_upload_latency: prometheus_client.Histogram = (
36
- latency_metric_for_fast_operation(
37
- "task_output_blob_store_upload", "Upload task output to blob store"
38
- )
39
- )
@@ -1,274 +0,0 @@
1
- import asyncio
2
- import hashlib
3
- import time
4
- from typing import Any, List
5
-
6
- from tensorlake.function_executor.proto.function_executor_pb2 import (
7
- SerializedObject,
8
- SerializedObjectEncoding,
9
- )
10
-
11
- from indexify.executor.blob_store.blob_store import BLOBStore
12
- from indexify.proto.executor_api_pb2 import (
13
- DataPayload,
14
- DataPayloadEncoding,
15
- )
16
-
17
- from .events import TaskOutputUploadFinished
18
- from .metrics.upload_task_output import (
19
- metric_task_output_blob_store_upload_errors,
20
- metric_task_output_blob_store_upload_latency,
21
- metric_task_output_blob_store_uploads,
22
- metric_task_output_upload_latency,
23
- metric_task_output_upload_retries,
24
- metric_task_output_uploads,
25
- metric_tasks_uploading_outputs,
26
- )
27
- from .task_info import TaskInfo
28
- from .task_output import TaskOutput
29
-
30
- _TASK_OUTPUT_UPLOAD_BACKOFF_SEC = 5.0
31
-
32
-
33
- async def upload_task_output(
34
- task_info: TaskInfo, blob_store: BLOBStore, logger: Any
35
- ) -> TaskOutputUploadFinished:
36
- """Uploads the task output to blob store.
37
-
38
- Doesn't raise any Exceptions. Runs till the reporting is successful.
39
- """
40
- logger = logger.bind(module=__name__)
41
-
42
- with (
43
- metric_tasks_uploading_outputs.track_inprogress(),
44
- metric_task_output_upload_latency.time(),
45
- ):
46
- metric_task_output_uploads.inc()
47
- await _upload_task_output_until_successful(
48
- output=task_info.output,
49
- blob_store=blob_store,
50
- logger=logger,
51
- )
52
- _log_function_metrics(output=task_info.output, logger=logger)
53
- return TaskOutputUploadFinished(task_info=task_info, is_success=True)
54
-
55
-
56
- async def _upload_task_output_until_successful(
57
- output: TaskOutput, blob_store: BLOBStore, logger: Any
58
- ) -> None:
59
- upload_retries: int = 0
60
-
61
- while True:
62
- logger = logger.bind(retries=upload_retries)
63
- try:
64
- await _upload_task_output_once(
65
- output=output, blob_store=blob_store, logger=logger
66
- )
67
- return
68
- except Exception as e:
69
- logger.error(
70
- "failed to upload task output",
71
- exc_info=e,
72
- )
73
- upload_retries += 1
74
- metric_task_output_upload_retries.inc()
75
- await asyncio.sleep(_TASK_OUTPUT_UPLOAD_BACKOFF_SEC)
76
-
77
-
78
- class _TaskOutputSummary:
79
- def __init__(self):
80
- self.output_count: int = 0
81
- self.output_total_bytes: int = 0
82
- self.next_functions_count: int = 0
83
- self.stdout_count: int = 0
84
- self.stdout_total_bytes: int = 0
85
- self.stderr_count: int = 0
86
- self.stderr_total_bytes: int = 0
87
- self.invocation_error_output_count: int = 0
88
- self.invocation_error_output_total_bytes: int = 0
89
- self.total_bytes: int = 0
90
-
91
-
92
- async def _upload_task_output_once(
93
- output: TaskOutput, blob_store: BLOBStore, logger: Any
94
- ) -> None:
95
- """Uploads the supplied task output to blob store.
96
-
97
- Raises an Exception if the upload fails.
98
- """
99
- output_summary: _TaskOutputSummary = _task_output_summary(output)
100
- logger.info(
101
- "uploading task output to blob store",
102
- total_bytes=output_summary.total_bytes,
103
- total_files=output_summary.output_count
104
- + output_summary.stdout_count
105
- + output_summary.stderr_count
106
- + output_summary.invocation_error_output_count,
107
- output_files=output_summary.output_count,
108
- output_bytes=output_summary.total_bytes,
109
- next_functions_count=output_summary.next_functions_count,
110
- stdout_bytes=output_summary.stdout_total_bytes,
111
- stderr_bytes=output_summary.stderr_total_bytes,
112
- invocation_error_output_bytes=output_summary.invocation_error_output_total_bytes,
113
- )
114
-
115
- start_time = time.time()
116
- with (
117
- metric_task_output_blob_store_upload_latency.time(),
118
- metric_task_output_blob_store_upload_errors.count_exceptions(),
119
- ):
120
- metric_task_output_blob_store_uploads.inc()
121
- await _upload_to_blob_store(
122
- task_output=output, blob_store=blob_store, logger=logger
123
- )
124
-
125
- logger.info(
126
- "files uploaded to blob store",
127
- duration=time.time() - start_time,
128
- )
129
-
130
-
131
- async def _upload_to_blob_store(
132
- task_output: TaskOutput, blob_store: BLOBStore, logger: Any
133
- ) -> None:
134
- if task_output.stdout is not None:
135
- stdout_url = f"{task_output.allocation.task.output_payload_uri_prefix}.{task_output.allocation.task.id}.stdout"
136
- stdout_bytes: bytes = task_output.stdout.encode()
137
- await blob_store.put(stdout_url, stdout_bytes, logger)
138
- task_output.uploaded_stdout = DataPayload(
139
- uri=stdout_url,
140
- size=len(stdout_bytes),
141
- sha256_hash=compute_hash(stdout_bytes),
142
- encoding=DataPayloadEncoding.DATA_PAYLOAD_ENCODING_UTF8_TEXT,
143
- encoding_version=0,
144
- )
145
- # stdout is uploaded, free the memory used for it and don't upload again if we retry overall output upload again.
146
- task_output.stdout = None
147
-
148
- if task_output.stderr is not None:
149
- stderr_url = f"{task_output.allocation.task.output_payload_uri_prefix}.{task_output.allocation.task.id}.stderr"
150
- stderr_bytes: bytes = task_output.stderr.encode()
151
- await blob_store.put(stderr_url, stderr_bytes, logger)
152
- task_output.uploaded_stderr = DataPayload(
153
- uri=stderr_url,
154
- size=len(stderr_bytes),
155
- sha256_hash=compute_hash(stderr_bytes),
156
- encoding=DataPayloadEncoding.DATA_PAYLOAD_ENCODING_UTF8_TEXT,
157
- encoding_version=0,
158
- )
159
- # stderr is uploaded, free the memory used for it and don't upload again if we retry overall output upload again.
160
- task_output.stderr = None
161
-
162
- if task_output.invocation_error_output is not None:
163
- invocation_error_output_url = (
164
- f"{task_output.allocation.task.output_payload_uri_prefix}.inverr."
165
- f"{task_output.allocation.task.graph_invocation_id}"
166
- )
167
- invocation_error_output_bytes: bytes = task_output.invocation_error_output.data
168
- await blob_store.put(
169
- invocation_error_output_url, invocation_error_output_bytes, logger
170
- )
171
- task_output.uploaded_invocation_error_output = DataPayload(
172
- uri=invocation_error_output_url,
173
- size=len(invocation_error_output_bytes),
174
- sha256_hash=compute_hash(invocation_error_output_bytes),
175
- encoding=_to_grpc_data_payload_encoding(
176
- task_output.invocation_error_output.encoding, logger
177
- ),
178
- encoding_version=0,
179
- )
180
- # Invocation error output is uploaded, free the memory used for it and don't upload again if we retry overall output upload again.
181
- task_output.invocation_error_output = None
182
-
183
- # We can't use the default empty list output.uploaded_data_payloads because it's a singleton.
184
- uploaded_data_payloads: List[DataPayload] = []
185
- for output in task_output.function_outputs:
186
- output: SerializedObject
187
- output_ix: int = len(uploaded_data_payloads)
188
- output_url: str = (
189
- f"{task_output.allocation.task.output_payload_uri_prefix}.{task_output.allocation.task.id}.{output_ix}"
190
- )
191
- await blob_store.put(output_url, output.data, logger)
192
- uploaded_data_payloads.append(
193
- DataPayload(
194
- uri=output_url,
195
- size=len(output.data),
196
- sha256_hash=compute_hash(output.data),
197
- encoding=_to_grpc_data_payload_encoding(output.encoding, logger),
198
- encoding_version=0,
199
- )
200
- )
201
-
202
- task_output.uploaded_data_payloads = uploaded_data_payloads
203
- # The output is uploaded, free the memory used for it and don't upload again if we retry overall output upload again.
204
- task_output.function_outputs = []
205
-
206
-
207
- def _task_output_summary(task_output: TaskOutput) -> _TaskOutputSummary:
208
- summary: _TaskOutputSummary = _TaskOutputSummary()
209
-
210
- if task_output.stdout is not None:
211
- summary.stdout_count += 1
212
- summary.stdout_total_bytes += len(task_output.stdout)
213
-
214
- if task_output.stderr is not None:
215
- summary.stderr_count += 1
216
- summary.stderr_total_bytes += len(task_output.stderr)
217
-
218
- if task_output.invocation_error_output is not None:
219
- summary.invocation_error_output_count += 1
220
- summary.invocation_error_output_total_bytes += len(
221
- task_output.invocation_error_output.data
222
- )
223
-
224
- for output in task_output.function_outputs:
225
- output: SerializedObject
226
- output_len: bytes = len(output.data)
227
- summary.output_count += 1
228
- summary.output_total_bytes += output_len
229
-
230
- summary.next_functions_count = len(task_output.next_functions)
231
-
232
- summary.total_bytes = (
233
- summary.output_total_bytes
234
- + summary.stdout_total_bytes
235
- + summary.stderr_total_bytes
236
- )
237
- return summary
238
-
239
-
240
- def _to_grpc_data_payload_encoding(
241
- encoding: SerializedObjectEncoding, logger: Any
242
- ) -> DataPayloadEncoding:
243
- if encoding == SerializedObjectEncoding.SERIALIZED_OBJECT_ENCODING_BINARY_PICKLE:
244
- return DataPayloadEncoding.DATA_PAYLOAD_ENCODING_BINARY_PICKLE
245
- elif encoding == SerializedObjectEncoding.SERIALIZED_OBJECT_ENCODING_UTF8_JSON:
246
- return DataPayloadEncoding.DATA_PAYLOAD_ENCODING_UTF8_JSON
247
- elif encoding == SerializedObjectEncoding.SERIALIZED_OBJECT_ENCODING_UTF8_TEXT:
248
- return DataPayloadEncoding.DATA_PAYLOAD_ENCODING_UTF8_TEXT
249
- else:
250
- logger.error(
251
- "Unexpected encoding for SerializedObject",
252
- encoding=SerializedObjectEncoding.Name(encoding),
253
- )
254
- return DataPayloadEncoding.DATA_PAYLOAD_ENCODING_UNKNOWN
255
-
256
-
257
- def compute_hash(data: bytes) -> str:
258
- hasher = hashlib.sha256(usedforsecurity=False)
259
- hasher.update(data)
260
- return hasher.hexdigest()
261
-
262
-
263
- # Temporary workaround is logging customer metrics until we store them somewhere
264
- # for future retrieval and processing.
265
- def _log_function_metrics(output: TaskOutput, logger: Any):
266
- if output.metrics is None:
267
- return
268
-
269
- for counter_name, counter_value in output.metrics.counters.items():
270
- logger.info(
271
- "function_metric", counter_name=counter_name, counter_value=counter_value
272
- )
273
- for timer_name, timer_value in output.metrics.timers.items():
274
- logger.info("function_metric", timer_name=timer_name, timer_value=timer_value)