chalkcompute 2.3.0__tar.gz → 2.3.2__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.
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/PKG-INFO +1 -1
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/__init__.py +6 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/__init__.pyi +6 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_cls.py +33 -8
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_function.py +49 -12
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_image.py +82 -4
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_local_dev.py +1 -1
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_remote_call_client.py +43 -5
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_scaling_group.py +38 -13
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/pyproject.toml +1 -1
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/rust/chalkcompute_native/Cargo.lock +9 -9
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/rust/chalkcompute_native/Cargo.toml +1 -1
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/rust/chalkcompute_native/src/lib.rs +3 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/README.md +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_arrow_schema.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_auth_config.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_concurrency.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_connect.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_container.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/__init__.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/buf/__init__.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/buf/validate/__init__.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/buf/validate/validate_pb2.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/buf/validate/validate_pb2.pyi +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/buf/validate/validate_pb2_grpc.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/buf/validate/validate_pb2_grpc.pyi +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/__init__.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/argo/__init__.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/argo/v1/__init__.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/argo/v1/workflow_pb2.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/argo/v1/workflow_pb2.pyi +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/argo/v1/workflow_pb2_grpc.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/argo/v1/workflow_pb2_grpc.pyi +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/arrow/__init__.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/arrow/v1/__init__.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/arrow/v1/arrow_pb2.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/arrow/v1/arrow_pb2.pyi +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/arrow/v1/arrow_pb2_grpc.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/arrow/v1/arrow_pb2_grpc.pyi +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/auth/__init__.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/auth/v1/__init__.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/auth/v1/agent_pb2.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/auth/v1/agent_pb2.pyi +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/auth/v1/agent_pb2_grpc.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/auth/v1/agent_pb2_grpc.pyi +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/auth/v1/audit_pb2.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/auth/v1/audit_pb2.pyi +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/auth/v1/audit_pb2_grpc.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/auth/v1/audit_pb2_grpc.pyi +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/auth/v1/displayagent_pb2.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/auth/v1/displayagent_pb2.pyi +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/auth/v1/displayagent_pb2_grpc.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/auth/v1/displayagent_pb2_grpc.pyi +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/auth/v1/featurepermission_pb2.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/auth/v1/featurepermission_pb2.pyi +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/auth/v1/featurepermission_pb2_grpc.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/auth/v1/featurepermission_pb2_grpc.pyi +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/auth/v1/permissions_pb2.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/auth/v1/permissions_pb2.pyi +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/auth/v1/permissions_pb2_grpc.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/auth/v1/permissions_pb2_grpc.pyi +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/common/__init__.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/common/v1/__init__.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/common/v1/chalk_error_pb2.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/common/v1/chalk_error_pb2.pyi +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/common/v1/chalk_error_pb2_grpc.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/common/v1/chalk_error_pb2_grpc.pyi +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/common/v1/log_pb2.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/common/v1/log_pb2.pyi +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/common/v1/log_pb2_grpc.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/common/v1/log_pb2_grpc.pyi +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/container/__init__.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/container/v1/__init__.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/container/v1/service_pb2.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/container/v1/service_pb2.pyi +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/container/v1/service_pb2_grpc.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/container/v1/service_pb2_grpc.pyi +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/externalfunctioncatalog/__init__.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/externalfunctioncatalog/v1/__init__.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/externalfunctioncatalog/v1/service_pb2.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/externalfunctioncatalog/v1/service_pb2.pyi +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/externalfunctioncatalog/v1/service_pb2_grpc.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/externalfunctioncatalog/v1/service_pb2_grpc.pyi +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/flags/__init__.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/flags/v1/__init__.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/flags/v1/flags_pb2.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/flags/v1/flags_pb2.pyi +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/flags/v1/flags_pb2_grpc.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/flags/v1/flags_pb2_grpc.pyi +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/runtime/__init__.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/runtime/v1/__init__.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/runtime/v1/remote_python_call_pb2.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/runtime/v1/remote_python_call_pb2.pyi +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/runtime/v1/remote_python_call_pb2_grpc.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/runtime/v1/remote_python_call_pb2_grpc.pyi +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/sandbox/__init__.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/sandbox/v1/__init__.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/sandbox/v1/service_pb2.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/sandbox/v1/service_pb2.pyi +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/sandbox/v1/service_pb2_grpc.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/sandbox/v1/service_pb2_grpc.pyi +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/scalinggroup/__init__.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/scalinggroup/v1/__init__.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/scalinggroup/v1/service_pb2.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/scalinggroup/v1/service_pb2.pyi +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/scalinggroup/v1/service_pb2_grpc.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/scalinggroup/v1/service_pb2_grpc.pyi +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/utils/__init__.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/utils/v1/__init__.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/utils/v1/encoding_pb2.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/utils/v1/encoding_pb2.pyi +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/utils/v1/encoding_pb2_grpc.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/utils/v1/encoding_pb2_grpc.pyi +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/utils/v1/field_change_pb2.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/utils/v1/field_change_pb2.pyi +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/utils/v1/field_change_pb2_grpc.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/utils/v1/field_change_pb2_grpc.pyi +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/utils/v1/sensitive_pb2.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/utils/v1/sensitive_pb2.pyi +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/utils/v1/sensitive_pb2_grpc.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/utils/v1/sensitive_pb2_grpc.pyi +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/volume/__init__.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/volume/v1/__init__.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/volume/v1/volume_pb2.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/volume/v1/volume_pb2.pyi +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/volume/v1/volume_pb2_grpc.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/volume/v1/volume_pb2_grpc.pyi +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/volume/v2/__init__.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/volume/v2/volume_pb2.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/volume/v2/volume_pb2.pyi +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/volume/v2/volume_pb2_grpc.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_gen/chalk/volume/v2/volume_pb2_grpc.pyi +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_ignore.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_logging.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_model.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_naming.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_native.pyi +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_progress.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_proto_pool.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_quota.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_rate_limit.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_remote_function_tracing.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_retry.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_sandbox.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_secret.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_tracing.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_volume.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/_vvolume.py +0 -0
- {chalkcompute-2.3.0 → chalkcompute-2.3.2}/chalkcompute/py.typed +0 -0
|
@@ -45,6 +45,12 @@ _LAZY_MAP: dict[str, tuple[str, str]] = {
|
|
|
45
45
|
"TracingPolicy",
|
|
46
46
|
),
|
|
47
47
|
"function": ("chalkcompute._function", "function"),
|
|
48
|
+
# _remote_call_client.py
|
|
49
|
+
"RemoteCallClient": ("chalkcompute._remote_call_client", "RemoteCallClient"),
|
|
50
|
+
"RemoteCallClientUnavailable": (
|
|
51
|
+
"chalkcompute._remote_call_client",
|
|
52
|
+
"RemoteCallClientUnavailable",
|
|
53
|
+
),
|
|
48
54
|
# _model.py
|
|
49
55
|
"bind_model": ("chalkcompute._model", "bind_model"),
|
|
50
56
|
"ModelError": ("chalkcompute._model", "ModelError"),
|
|
@@ -37,6 +37,10 @@ from chalkcompute._model import ModelNotFoundError as ModelNotFoundError
|
|
|
37
37
|
from chalkcompute._model import bind_model as bind_model
|
|
38
38
|
from chalkcompute._quota import QueuePolicy as QueuePolicy
|
|
39
39
|
from chalkcompute._rate_limit import RateLimitPolicy as RateLimitPolicy
|
|
40
|
+
from chalkcompute._remote_call_client import RemoteCallClient as RemoteCallClient
|
|
41
|
+
from chalkcompute._remote_call_client import (
|
|
42
|
+
RemoteCallClientUnavailable as RemoteCallClientUnavailable,
|
|
43
|
+
)
|
|
40
44
|
from chalkcompute._remote_function_tracing import TracingPolicy as TracingPolicy
|
|
41
45
|
from chalkcompute._retry import RetryPolicy as RetryPolicy
|
|
42
46
|
from chalkcompute._sandbox import ExecError as ExecError
|
|
@@ -105,6 +109,8 @@ __all__ = [
|
|
|
105
109
|
"QueuePolicy",
|
|
106
110
|
"TracingPolicy",
|
|
107
111
|
"function",
|
|
112
|
+
"RemoteCallClient",
|
|
113
|
+
"RemoteCallClientUnavailable",
|
|
108
114
|
"bind_model",
|
|
109
115
|
"ModelError",
|
|
110
116
|
"ModelNotFoundError",
|
|
@@ -74,6 +74,7 @@ from chalkcompute._function import (
|
|
|
74
74
|
_make_base_image,
|
|
75
75
|
_make_container_spec,
|
|
76
76
|
_normalize_schedule,
|
|
77
|
+
_scaling_group_is_ready,
|
|
77
78
|
_should_strip_source,
|
|
78
79
|
_source_text_for_deploy,
|
|
79
80
|
_write_temp,
|
|
@@ -1179,7 +1180,6 @@ class RemoteClass:
|
|
|
1179
1180
|
sg_data = data.get("scalingGroup")
|
|
1180
1181
|
web_url = sg_data.get("webUrl") if sg_data else None
|
|
1181
1182
|
port = sg_data.get("port", 6666) if sg_data else 6666
|
|
1182
|
-
sg_status = sg_data.get("status", "") if sg_data else ""
|
|
1183
1183
|
|
|
1184
1184
|
# Build a RemoteFunction proxy for calling this method
|
|
1185
1185
|
rf = RemoteFunction._make_empty()
|
|
@@ -1198,17 +1198,24 @@ class RemoteClass:
|
|
|
1198
1198
|
)
|
|
1199
1199
|
progress_done(mkey)
|
|
1200
1200
|
|
|
1201
|
-
if
|
|
1201
|
+
if not (
|
|
1202
|
+
sg_data
|
|
1203
|
+
and _scaling_group_is_ready(
|
|
1204
|
+
sg_data,
|
|
1205
|
+
require_ready_replicas=self._min_replicas > 0,
|
|
1206
|
+
)
|
|
1207
|
+
):
|
|
1202
1208
|
sg_names.append(version_info.scaling_group_name)
|
|
1203
1209
|
|
|
1204
|
-
# 5. Poll scaling groups until
|
|
1210
|
+
# 5. Poll scaling groups until their backing pods can serve.
|
|
1205
1211
|
if sg_names:
|
|
1206
1212
|
progress_update(ckey, detail="waiting for ready")
|
|
1207
1213
|
for sg_name in sg_names:
|
|
1208
|
-
self.
|
|
1214
|
+
self._poll_until_ready(
|
|
1209
1215
|
sg_name=sg_name,
|
|
1210
1216
|
poll_interval=poll_interval,
|
|
1211
1217
|
timeout=ready_timeout,
|
|
1218
|
+
require_ready_replicas=self._min_replicas > 0,
|
|
1212
1219
|
)
|
|
1213
1220
|
|
|
1214
1221
|
self._deployed = True
|
|
@@ -1221,20 +1228,26 @@ class RemoteClass:
|
|
|
1221
1228
|
|
|
1222
1229
|
return self
|
|
1223
1230
|
|
|
1224
|
-
def
|
|
1231
|
+
def _poll_until_ready(
|
|
1225
1232
|
self,
|
|
1226
1233
|
*,
|
|
1227
1234
|
sg_name: str,
|
|
1228
1235
|
poll_interval: float,
|
|
1229
1236
|
timeout: float,
|
|
1237
|
+
require_ready_replicas: bool = True,
|
|
1230
1238
|
) -> None:
|
|
1231
1239
|
deadline = time.monotonic() + timeout
|
|
1240
|
+
status = "unknown"
|
|
1241
|
+
ready_replicas = 0
|
|
1242
|
+
available_replicas = 0
|
|
1232
1243
|
while time.monotonic() < deadline:
|
|
1233
1244
|
resp = self._client.rpc(_SG_SVC, "GetScalingGroup", {"name": sg_name})
|
|
1234
1245
|
if resp.status_code != 200:
|
|
1235
1246
|
raise ClassError(f"GetScalingGroup failed: {resp.text}")
|
|
1236
1247
|
sg = resp.json()["scalingGroup"]
|
|
1237
1248
|
status = sg.get("status", "")
|
|
1249
|
+
ready_replicas = sg.get("readyReplicas", 0)
|
|
1250
|
+
available_replicas = sg.get("availableReplicas", 0)
|
|
1238
1251
|
|
|
1239
1252
|
# Update the web_url on the corresponding RemoteFunction
|
|
1240
1253
|
web_url = sg.get("webUrl")
|
|
@@ -1247,13 +1260,25 @@ class RemoteClass:
|
|
|
1247
1260
|
):
|
|
1248
1261
|
rf._web_url = web_url
|
|
1249
1262
|
|
|
1250
|
-
logger.info(
|
|
1251
|
-
|
|
1263
|
+
logger.info(
|
|
1264
|
+
" poll %s: status=%s ready=%s available=%s",
|
|
1265
|
+
sg_name,
|
|
1266
|
+
status,
|
|
1267
|
+
ready_replicas,
|
|
1268
|
+
available_replicas,
|
|
1269
|
+
)
|
|
1270
|
+
if _scaling_group_is_ready(
|
|
1271
|
+
sg, require_ready_replicas=require_ready_replicas
|
|
1272
|
+
):
|
|
1252
1273
|
return
|
|
1253
1274
|
if status in ("Failed", "Error"):
|
|
1254
1275
|
raise ClassError(f"Scaling group {sg_name} entered {status} state")
|
|
1255
1276
|
time.sleep(poll_interval)
|
|
1256
|
-
raise ClassError(
|
|
1277
|
+
raise ClassError(
|
|
1278
|
+
f"Scaling group {sg_name} not ready after {timeout}s "
|
|
1279
|
+
f"(status={status}, ready={ready_replicas}, "
|
|
1280
|
+
f"available={available_replicas})"
|
|
1281
|
+
)
|
|
1257
1282
|
|
|
1258
1283
|
def wait_ready(
|
|
1259
1284
|
self,
|
|
@@ -95,7 +95,7 @@ F = TypeVar("F", bound=Callable[..., Any])
|
|
|
95
95
|
_RemoteCallResult = TypeVar("_RemoteCallResult")
|
|
96
96
|
KnowledgeCutoff = str | _dt.datetime
|
|
97
97
|
|
|
98
|
-
_REMOTE_ENV_VAR = "
|
|
98
|
+
_REMOTE_ENV_VAR = "CHALK_REMOTE_FUNCTION"
|
|
99
99
|
_REQUEST_TIMESTAMP_HEADER = "x-chalk-request-timestamp"
|
|
100
100
|
_NO_ARGS_SENTINEL_COLUMN = "__chalk_no_args__"
|
|
101
101
|
_LOCAL_COPY_ENV_VAR = _local_dev._LOCAL_COPY_ENV_VAR
|
|
@@ -461,6 +461,29 @@ def _has_running_event_loop() -> bool:
|
|
|
461
461
|
return True
|
|
462
462
|
|
|
463
463
|
|
|
464
|
+
def _replica_count(value: Any) -> int:
|
|
465
|
+
if value is None:
|
|
466
|
+
return 0
|
|
467
|
+
try:
|
|
468
|
+
return int(value)
|
|
469
|
+
except (TypeError, ValueError):
|
|
470
|
+
return 0
|
|
471
|
+
|
|
472
|
+
|
|
473
|
+
def _scaling_group_is_ready(
|
|
474
|
+
sg: dict[str, Any], *, require_ready_replicas: bool = True
|
|
475
|
+
) -> bool:
|
|
476
|
+
status = sg.get("status", "")
|
|
477
|
+
if not require_ready_replicas:
|
|
478
|
+
return status in ("Running", "Available", "ScaledToZero")
|
|
479
|
+
if status not in ("Running", "Available"):
|
|
480
|
+
return False
|
|
481
|
+
return (
|
|
482
|
+
_replica_count(sg.get("readyReplicas")) > 0
|
|
483
|
+
and _replica_count(sg.get("availableReplicas")) > 0
|
|
484
|
+
)
|
|
485
|
+
|
|
486
|
+
|
|
464
487
|
def _format_knowledge_cutoff(value: KnowledgeCutoff) -> str:
|
|
465
488
|
if isinstance(value, str):
|
|
466
489
|
if not value:
|
|
@@ -1996,10 +2019,8 @@ class RemoteFunction:
|
|
|
1996
2019
|
if sg_data:
|
|
1997
2020
|
self._web_url = sg_data.get("webUrl")
|
|
1998
2021
|
self._port = sg_data.get("port", 6666)
|
|
1999
|
-
sg_status = sg_data.get("status", "")
|
|
2000
2022
|
else:
|
|
2001
2023
|
self._port = 6666
|
|
2002
|
-
sg_status = ""
|
|
2003
2024
|
|
|
2004
2025
|
logger.info(
|
|
2005
2026
|
"Function version created: %s v%s (id=%s)",
|
|
@@ -2009,13 +2030,20 @@ class RemoteFunction:
|
|
|
2009
2030
|
)
|
|
2010
2031
|
progress_done(rkey)
|
|
2011
2032
|
|
|
2012
|
-
# 7. Poll
|
|
2013
|
-
if
|
|
2033
|
+
# 7. Poll until the backing pod is actually ready to serve.
|
|
2034
|
+
if not (
|
|
2035
|
+
sg_data
|
|
2036
|
+
and _scaling_group_is_ready(
|
|
2037
|
+
sg_data,
|
|
2038
|
+
require_ready_replicas=self._min_replicas > 0,
|
|
2039
|
+
)
|
|
2040
|
+
):
|
|
2014
2041
|
progress_update(fkey, detail="waiting for ready")
|
|
2015
|
-
self.
|
|
2042
|
+
self._poll_until_ready(
|
|
2016
2043
|
sg_name=self._version_info.scaling_group_name,
|
|
2017
2044
|
poll_interval=poll_interval,
|
|
2018
2045
|
timeout=ready_timeout,
|
|
2046
|
+
require_ready_replicas=self._min_replicas > 0,
|
|
2019
2047
|
)
|
|
2020
2048
|
|
|
2021
2049
|
progress_done(fkey, detail="ready")
|
|
@@ -2033,14 +2061,19 @@ class RemoteFunction:
|
|
|
2033
2061
|
extras.append("tracing")
|
|
2034
2062
|
return _remote_call_runtime_package_with_extras(extras)
|
|
2035
2063
|
|
|
2036
|
-
def
|
|
2064
|
+
def _poll_until_ready(
|
|
2037
2065
|
self,
|
|
2038
2066
|
*,
|
|
2039
2067
|
sg_name: str,
|
|
2040
2068
|
poll_interval: float,
|
|
2041
2069
|
timeout: float,
|
|
2070
|
+
require_ready_replicas: bool = True,
|
|
2042
2071
|
) -> None:
|
|
2043
2072
|
deadline = time.monotonic() + timeout
|
|
2073
|
+
status = "unknown"
|
|
2074
|
+
status_message = ""
|
|
2075
|
+
ready_replicas = 0
|
|
2076
|
+
available_replicas = 0
|
|
2044
2077
|
while time.monotonic() < deadline:
|
|
2045
2078
|
resp = self._client.rpc(
|
|
2046
2079
|
_SG_SVC,
|
|
@@ -2062,7 +2095,9 @@ class RemoteFunction:
|
|
|
2062
2095
|
available_replicas,
|
|
2063
2096
|
status_message or "",
|
|
2064
2097
|
)
|
|
2065
|
-
if
|
|
2098
|
+
if _scaling_group_is_ready(
|
|
2099
|
+
sg, require_ready_replicas=require_ready_replicas
|
|
2100
|
+
):
|
|
2066
2101
|
return
|
|
2067
2102
|
if status in ("Failed", "Error"):
|
|
2068
2103
|
message = f"Scaling group {sg_name} entered {status} state"
|
|
@@ -2070,7 +2105,11 @@ class RemoteFunction:
|
|
|
2070
2105
|
message = f"{message}: {status_message}"
|
|
2071
2106
|
raise FunctionError(message)
|
|
2072
2107
|
time.sleep(poll_interval)
|
|
2073
|
-
message =
|
|
2108
|
+
message = (
|
|
2109
|
+
f"Scaling group {sg_name} not ready after {timeout}s "
|
|
2110
|
+
f"(status={status}, ready={ready_replicas}, "
|
|
2111
|
+
f"available={available_replicas})"
|
|
2112
|
+
)
|
|
2074
2113
|
if status_message:
|
|
2075
2114
|
message = f"{message}: {status_message}"
|
|
2076
2115
|
raise FunctionError(message)
|
|
@@ -2114,9 +2153,7 @@ class RemoteFunction:
|
|
|
2114
2153
|
self._web_url = sg.get("webUrl")
|
|
2115
2154
|
if status in ("Failed", "Error"):
|
|
2116
2155
|
raise FunctionError(f"Scaling group {sg_name} entered {status} state")
|
|
2117
|
-
|
|
2118
|
-
available = sg.get("availableReplicas", 0)
|
|
2119
|
-
if status in ("Running", "Available") and ready > 0 and available > 0:
|
|
2156
|
+
if _scaling_group_is_ready(sg):
|
|
2120
2157
|
return self
|
|
2121
2158
|
time.sleep(poll_interval)
|
|
2122
2159
|
|
|
@@ -43,6 +43,11 @@ _ARCHIVE_CHUNK_PREFIX = "_archive.part."
|
|
|
43
43
|
# PutFile payload is base64-encoded, so keep raw chunks under ~6 MB
|
|
44
44
|
# to stay well within typical HTTP body limits after encoding (~8 MB).
|
|
45
45
|
_ARCHIVE_CHUNK_SIZE = 6 * 1024 * 1024
|
|
46
|
+
# Copy content is base64-embedded into the image spec and generated Dockerfile.
|
|
47
|
+
# A 32 KiB raw budget stays under Argo object-size pressure after encoding and
|
|
48
|
+
# leaves headroom under Kaniko's 65,535-byte Dockerfile line limit.
|
|
49
|
+
_COPY_FILE_SIZE_LIMIT_BYTES = 32 * 1024
|
|
50
|
+
_COPY_TOTAL_SIZE_LIMIT_BYTES = 32 * 1024
|
|
46
51
|
|
|
47
52
|
|
|
48
53
|
@dataclass(frozen=True)
|
|
@@ -388,7 +393,9 @@ class Image:
|
|
|
388
393
|
- ``0o400`` (``r--------``) — immutable/read-only files the
|
|
389
394
|
workload must not mutate.
|
|
390
395
|
strategy
|
|
391
|
-
``"copy"`` inlines the file contents into the image spec
|
|
396
|
+
``"copy"`` inlines the file contents into the image spec and
|
|
397
|
+
rejects files larger than 32 KiB or additions that would make
|
|
398
|
+
the image's total copied content exceed 32 KiB.
|
|
392
399
|
``"volume"`` (default) defers the file as a lazy reference
|
|
393
400
|
to be mounted at sandbox start.
|
|
394
401
|
|
|
@@ -397,6 +404,12 @@ class Image:
|
|
|
397
404
|
Image
|
|
398
405
|
A new ``Image`` with the file registered for inclusion.
|
|
399
406
|
|
|
407
|
+
Raises
|
|
408
|
+
------
|
|
409
|
+
ValueError
|
|
410
|
+
If ``strategy="copy"`` exceeds the per-file or total copied-content
|
|
411
|
+
limit.
|
|
412
|
+
|
|
400
413
|
Examples
|
|
401
414
|
--------
|
|
402
415
|
>>> from chalkcompute import Image
|
|
@@ -429,7 +442,13 @@ class Image:
|
|
|
429
442
|
LazyLocalFile(src=str(src_path), dest=dest, mode=mode)
|
|
430
443
|
)
|
|
431
444
|
else:
|
|
432
|
-
|
|
445
|
+
src_path = Path(src)
|
|
446
|
+
content = _read_copy_file(src_path)
|
|
447
|
+
_validate_copy_total_size(
|
|
448
|
+
current_size=_inline_copy_size(new._steps),
|
|
449
|
+
added_size=len(content),
|
|
450
|
+
path=src_path,
|
|
451
|
+
)
|
|
433
452
|
step = pb.AddFileStep(destination=dest, content=content)
|
|
434
453
|
if mode is not None:
|
|
435
454
|
step.mode = mode
|
|
@@ -464,7 +483,9 @@ class Image:
|
|
|
464
483
|
dest
|
|
465
484
|
Absolute destination directory in the image.
|
|
466
485
|
strategy
|
|
467
|
-
``"copy"`` inlines all file contents into the image spec
|
|
486
|
+
``"copy"`` inlines all file contents into the image spec and
|
|
487
|
+
rejects files larger than 32 KiB or additions that would make
|
|
488
|
+
the image's total copied content exceed 32 KiB.
|
|
468
489
|
``"volume"`` (default) defers each file as a lazy reference
|
|
469
490
|
to be mounted at sandbox start.
|
|
470
491
|
exclude
|
|
@@ -481,6 +502,12 @@ class Image:
|
|
|
481
502
|
Image
|
|
482
503
|
A new ``Image`` with the directory registered for inclusion.
|
|
483
504
|
|
|
505
|
+
Raises
|
|
506
|
+
------
|
|
507
|
+
ValueError
|
|
508
|
+
If ``strategy="copy"`` exceeds the per-file or total copied-content
|
|
509
|
+
limit.
|
|
510
|
+
|
|
484
511
|
Examples
|
|
485
512
|
--------
|
|
486
513
|
>>> from chalkcompute import Image
|
|
@@ -548,6 +575,7 @@ class Image:
|
|
|
548
575
|
)
|
|
549
576
|
return new
|
|
550
577
|
|
|
578
|
+
copy_size = _inline_copy_size(new._steps)
|
|
551
579
|
for file_path in _iter_files():
|
|
552
580
|
rel = file_path.relative_to(src_path)
|
|
553
581
|
file_dest = os.path.join(dest, str(rel))
|
|
@@ -558,7 +586,12 @@ class Image:
|
|
|
558
586
|
)
|
|
559
587
|
)
|
|
560
588
|
else:
|
|
561
|
-
content = file_path
|
|
589
|
+
content = _read_copy_file(file_path)
|
|
590
|
+
copy_size = _validate_copy_total_size(
|
|
591
|
+
current_size=copy_size,
|
|
592
|
+
added_size=len(content),
|
|
593
|
+
path=file_path,
|
|
594
|
+
)
|
|
562
595
|
new._steps.append(
|
|
563
596
|
pb.BuildStep(
|
|
564
597
|
add_file=pb.AddFileStep(destination=file_dest, content=content)
|
|
@@ -856,3 +889,48 @@ def _validate_lazy_local_file_destination(
|
|
|
856
889
|
"Use strategy='copy' or place files under a subdirectory "
|
|
857
890
|
"(for example '/app/app.py')."
|
|
858
891
|
)
|
|
892
|
+
|
|
893
|
+
|
|
894
|
+
def _read_copy_file(path: Path) -> bytes:
|
|
895
|
+
"""Read a file only when its actual content fits the copy-file budget."""
|
|
896
|
+
with path.open("rb") as file:
|
|
897
|
+
content = file.read(_COPY_FILE_SIZE_LIMIT_BYTES + 1)
|
|
898
|
+
if len(content) > _COPY_FILE_SIZE_LIMIT_BYTES:
|
|
899
|
+
raise ValueError(
|
|
900
|
+
f"{path} is at least {len(content)} bytes, which exceeds the "
|
|
901
|
+
f"{_format_size_limit(_COPY_FILE_SIZE_LIMIT_BYTES)} per-file limit "
|
|
902
|
+
"for strategy='copy'. "
|
|
903
|
+
"Use strategy='volume' for larger files."
|
|
904
|
+
)
|
|
905
|
+
return content
|
|
906
|
+
|
|
907
|
+
|
|
908
|
+
def _inline_copy_size(steps: Sequence[pb.BuildStep]) -> int:
|
|
909
|
+
"""Return the raw bytes already embedded by copy-based add-file steps."""
|
|
910
|
+
return sum(
|
|
911
|
+
len(step.add_file.content)
|
|
912
|
+
for step in steps
|
|
913
|
+
if step.WhichOneof("step") == "add_file"
|
|
914
|
+
)
|
|
915
|
+
|
|
916
|
+
|
|
917
|
+
def _validate_copy_total_size(*, current_size: int, added_size: int, path: Path) -> int:
|
|
918
|
+
"""Return the new copied-content size or reject an oversized image spec."""
|
|
919
|
+
total_size = current_size + added_size
|
|
920
|
+
if total_size > _COPY_TOTAL_SIZE_LIMIT_BYTES:
|
|
921
|
+
raise ValueError(
|
|
922
|
+
f"adding {path} would inline {total_size} bytes, which exceeds the "
|
|
923
|
+
f"{_format_size_limit(_COPY_TOTAL_SIZE_LIMIT_BYTES)} total limit "
|
|
924
|
+
"for strategy='copy'. "
|
|
925
|
+
"Use strategy='volume' for larger files or directories."
|
|
926
|
+
)
|
|
927
|
+
return total_size
|
|
928
|
+
|
|
929
|
+
|
|
930
|
+
def _format_size_limit(size: int) -> str:
|
|
931
|
+
"""Format a byte limit using the largest exact binary unit."""
|
|
932
|
+
if size % (1024 * 1024) == 0:
|
|
933
|
+
return f"{size // (1024 * 1024)} MiB"
|
|
934
|
+
if size % 1024 == 0:
|
|
935
|
+
return f"{size // 1024} KiB"
|
|
936
|
+
return f"{size} bytes"
|
|
@@ -38,7 +38,7 @@ _LOCAL_SDK_RUNTIME_PACKAGES = [
|
|
|
38
38
|
|
|
39
39
|
|
|
40
40
|
def running_in_function_pod() -> bool:
|
|
41
|
-
return bool(os.environ.get("
|
|
41
|
+
return bool(os.environ.get("CHALK_REMOTE_FUNCTION"))
|
|
42
42
|
|
|
43
43
|
|
|
44
44
|
def build_function_runtime_image(*, python_version: str = "3.11") -> object:
|
|
@@ -7,6 +7,7 @@ import importlib.machinery
|
|
|
7
7
|
import importlib.util
|
|
8
8
|
import logging
|
|
9
9
|
import sys
|
|
10
|
+
import time
|
|
10
11
|
from dataclasses import dataclass
|
|
11
12
|
from pathlib import Path
|
|
12
13
|
from typing import Any, Sequence
|
|
@@ -155,6 +156,7 @@ class RemoteCallClient:
|
|
|
155
156
|
auth_rpc_timeout_ms,
|
|
156
157
|
auth_retry_interval_ms,
|
|
157
158
|
)
|
|
159
|
+
start = time.monotonic()
|
|
158
160
|
try:
|
|
159
161
|
self._inner = _NativeRemoteCallClient(
|
|
160
162
|
endpoint,
|
|
@@ -191,6 +193,12 @@ class RemoteCallClient:
|
|
|
191
193
|
self._supports_per_call_metadata: bool = (
|
|
192
194
|
self._detect_per_call_metadata_support()
|
|
193
195
|
)
|
|
196
|
+
logger.debug(
|
|
197
|
+
"RemoteCallClient connected endpoint=%s use_tls=%s elapsed_ms=%.1f",
|
|
198
|
+
endpoint,
|
|
199
|
+
use_tls,
|
|
200
|
+
(time.monotonic() - start) * 1000,
|
|
201
|
+
)
|
|
194
202
|
|
|
195
203
|
def _inner_or_raise(self) -> Any:
|
|
196
204
|
if self._closed:
|
|
@@ -238,11 +246,21 @@ class RemoteCallClient:
|
|
|
238
246
|
input_ipc: bytes,
|
|
239
247
|
metadata: Sequence[tuple[str, str]] = (),
|
|
240
248
|
) -> list[bytes]:
|
|
249
|
+
start = time.monotonic()
|
|
241
250
|
if metadata and self._supports_per_call_metadata:
|
|
242
|
-
|
|
251
|
+
chunks = list(
|
|
243
252
|
self._inner_or_raise().call_ipc(name, input_ipc, list(metadata))
|
|
244
253
|
)
|
|
245
|
-
|
|
254
|
+
else:
|
|
255
|
+
chunks = list(self._inner_or_raise().call_ipc(name, input_ipc))
|
|
256
|
+
logger.debug(
|
|
257
|
+
"RemoteCallClient call_ipc function=%s request_bytes=%d chunks=%d elapsed_ms=%.1f",
|
|
258
|
+
name,
|
|
259
|
+
len(input_ipc),
|
|
260
|
+
len(chunks),
|
|
261
|
+
(time.monotonic() - start) * 1000,
|
|
262
|
+
)
|
|
263
|
+
return chunks
|
|
246
264
|
|
|
247
265
|
def enqueue(
|
|
248
266
|
self,
|
|
@@ -250,9 +268,20 @@ class RemoteCallClient:
|
|
|
250
268
|
input_ipc: bytes,
|
|
251
269
|
metadata: Sequence[tuple[str, str]] = (),
|
|
252
270
|
) -> str:
|
|
271
|
+
start = time.monotonic()
|
|
253
272
|
if metadata and self._supports_per_call_metadata:
|
|
254
|
-
|
|
255
|
-
|
|
273
|
+
call_id = str(
|
|
274
|
+
self._inner_or_raise().enqueue(name, input_ipc, list(metadata))
|
|
275
|
+
)
|
|
276
|
+
else:
|
|
277
|
+
call_id = str(self._inner_or_raise().enqueue(name, input_ipc))
|
|
278
|
+
logger.debug(
|
|
279
|
+
"RemoteCallClient enqueue function=%s request_bytes=%d elapsed_ms=%.1f",
|
|
280
|
+
name,
|
|
281
|
+
len(input_ipc),
|
|
282
|
+
(time.monotonic() - start) * 1000,
|
|
283
|
+
)
|
|
284
|
+
return call_id
|
|
256
285
|
|
|
257
286
|
def poll_next(
|
|
258
287
|
self,
|
|
@@ -260,8 +289,9 @@ class RemoteCallClient:
|
|
|
260
289
|
cursor: str = "",
|
|
261
290
|
poll_count: int = 0,
|
|
262
291
|
) -> RemoteCallPollResult:
|
|
292
|
+
start = time.monotonic()
|
|
263
293
|
raw = self._inner_or_raise().poll_next(call_id, cursor, poll_count)
|
|
264
|
-
|
|
294
|
+
result = RemoteCallPollResult(
|
|
265
295
|
status=int(raw["status"]),
|
|
266
296
|
status_name=str(raw["status_name"]),
|
|
267
297
|
chunks=list(raw["chunks"]),
|
|
@@ -269,6 +299,14 @@ class RemoteCallClient:
|
|
|
269
299
|
error_message=raw["error_message"],
|
|
270
300
|
poll_count=int(raw["poll_count"]),
|
|
271
301
|
)
|
|
302
|
+
logger.debug(
|
|
303
|
+
"RemoteCallClient poll_next call_id=%s status=%s chunks=%d elapsed_ms=%.1f",
|
|
304
|
+
call_id,
|
|
305
|
+
result.status_name,
|
|
306
|
+
len(result.chunks),
|
|
307
|
+
(time.monotonic() - start) * 1000,
|
|
308
|
+
)
|
|
309
|
+
return result
|
|
272
310
|
|
|
273
311
|
def get_result_chunks(self, call_id: str) -> list[bytes]:
|
|
274
312
|
return list(self._inner_or_raise().get_result_chunks(call_id))
|
|
@@ -293,7 +293,8 @@ class ScalingGroup:
|
|
|
293
293
|
build_timeout
|
|
294
294
|
Maximum seconds to wait for the image build to complete.
|
|
295
295
|
ready_timeout
|
|
296
|
-
Maximum seconds to wait for the scaling group to
|
|
296
|
+
Maximum seconds to wait for the scaling group to have at least one
|
|
297
|
+
ready and available replica.
|
|
297
298
|
|
|
298
299
|
Returns
|
|
299
300
|
-------
|
|
@@ -420,23 +421,29 @@ class ScalingGroup:
|
|
|
420
421
|
self._scaling_group_info.status,
|
|
421
422
|
)
|
|
422
423
|
|
|
423
|
-
# 4. Poll until
|
|
424
|
-
if
|
|
425
|
-
self.
|
|
424
|
+
# 4. Poll until the backing pod is actually ready to serve.
|
|
425
|
+
if not _scaling_group_info_is_ready(
|
|
426
|
+
self._scaling_group_info,
|
|
427
|
+
require_ready_replicas=self._min_replicas > 0,
|
|
428
|
+
):
|
|
429
|
+
self._poll_until_ready(
|
|
426
430
|
poll_interval=poll_interval,
|
|
427
431
|
timeout=ready_timeout,
|
|
432
|
+
require_ready_replicas=self._min_replicas > 0,
|
|
428
433
|
)
|
|
429
434
|
|
|
430
435
|
return self
|
|
431
436
|
|
|
432
|
-
def
|
|
437
|
+
def _poll_until_ready(
|
|
433
438
|
self,
|
|
434
439
|
*,
|
|
435
440
|
poll_interval: float,
|
|
436
441
|
timeout: float,
|
|
442
|
+
require_ready_replicas: bool = True,
|
|
437
443
|
) -> None:
|
|
438
444
|
assert self._scaling_group_id is not None
|
|
439
445
|
deadline = time.monotonic() + timeout
|
|
446
|
+
info = self._scaling_group_info
|
|
440
447
|
while time.monotonic() < deadline:
|
|
441
448
|
resp = self._client.rpc(
|
|
442
449
|
_SVC, "GetScalingGroup", {"id": self._scaling_group_id}
|
|
@@ -445,16 +452,28 @@ class ScalingGroup:
|
|
|
445
452
|
raise ScalingGroupError(f"GetScalingGroup failed: {resp.text}")
|
|
446
453
|
info = _parse_scaling_group_response(resp.json()["scalingGroup"])
|
|
447
454
|
self._scaling_group_info = info
|
|
448
|
-
logger.info(
|
|
449
|
-
|
|
455
|
+
logger.info(
|
|
456
|
+
" poll: status=%s ready=%s available=%s",
|
|
457
|
+
info.status,
|
|
458
|
+
info.ready_replicas,
|
|
459
|
+
info.available_replicas,
|
|
460
|
+
)
|
|
461
|
+
if _scaling_group_info_is_ready(
|
|
462
|
+
info,
|
|
463
|
+
require_ready_replicas=require_ready_replicas,
|
|
464
|
+
):
|
|
450
465
|
return
|
|
451
466
|
if info.status in ("Failed", "Error"):
|
|
452
467
|
raise ScalingGroupError(
|
|
453
468
|
f"Scaling group {self._scaling_group_id} entered {info.status} state"
|
|
454
469
|
)
|
|
455
470
|
time.sleep(poll_interval)
|
|
471
|
+
status = info.status if info else "unknown"
|
|
472
|
+
ready = info.ready_replicas if info else 0
|
|
473
|
+
available = info.available_replicas if info else 0
|
|
456
474
|
raise ScalingGroupError(
|
|
457
|
-
f"Scaling group {self._scaling_group_id}
|
|
475
|
+
f"Scaling group {self._scaling_group_id} not ready after {timeout}s "
|
|
476
|
+
f"(status={status}, ready={ready}, available={available})"
|
|
458
477
|
)
|
|
459
478
|
|
|
460
479
|
@_instrumented("chalkcompute.scaling_group.call")
|
|
@@ -542,11 +561,7 @@ class ScalingGroup:
|
|
|
542
561
|
raise ScalingGroupError(
|
|
543
562
|
f"Scaling group {self._scaling_group_id} entered {info.status} state"
|
|
544
563
|
)
|
|
545
|
-
if (
|
|
546
|
-
info.status in ("Running", "Available")
|
|
547
|
-
and info.ready_replicas > 0
|
|
548
|
-
and info.available_replicas > 0
|
|
549
|
-
):
|
|
564
|
+
if _scaling_group_info_is_ready(info):
|
|
550
565
|
return self
|
|
551
566
|
time.sleep(poll_interval)
|
|
552
567
|
|
|
@@ -639,3 +654,13 @@ def _parse_scaling_group_response(data: dict) -> ScalingGroupInfo:
|
|
|
639
654
|
ready_replicas=data.get("readyReplicas", 0),
|
|
640
655
|
available_replicas=data.get("availableReplicas", 0),
|
|
641
656
|
)
|
|
657
|
+
|
|
658
|
+
|
|
659
|
+
def _scaling_group_info_is_ready(
|
|
660
|
+
info: ScalingGroupInfo, *, require_ready_replicas: bool = True
|
|
661
|
+
) -> bool:
|
|
662
|
+
if not require_ready_replicas:
|
|
663
|
+
return info.status in ("Running", "Available", "ScaledToZero")
|
|
664
|
+
if info.status not in ("Running", "Available"):
|
|
665
|
+
return False
|
|
666
|
+
return info.ready_replicas > 0 and info.available_replicas > 0
|