chalk-remote-call-python 1.8.1__tar.gz → 1.8.3__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.
- {chalk_remote_call_python-1.8.1/chalk_remote_call_python.egg-info → chalk_remote_call_python-1.8.3}/PKG-INFO +1 -1
- {chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk-remote-call-rs/Cargo.lock +32 -19
- {chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk-remote-call-rs/chalk-remote-call-server/Cargo.toml +4 -4
- {chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk-remote-call-rs/chalk-remote-call-server/src/async_service.rs +21 -3
- {chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk-remote-call-rs/chalk-remote-call-server/src/coalesce.rs +65 -4
- {chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk-remote-call-rs/chalk-remote-call-server/src/lib.rs +34 -17
- {chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk-remote-call-rs/chalk-remote-call-server/src/metrics.rs +6 -4
- {chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk-remote-call-rs/chalk-remote-call-server/src/python_bridge.rs +33 -9
- {chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk-remote-call-rs/chalk-remote-call-server/src/self_consumer.rs +81 -22
- {chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk-remote-call-rs/chalk-remote-call-server/src/server.rs +24 -8
- {chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk-remote-call-rs/chalk-remote-call-server/src/service.rs +42 -3
- chalk_remote_call_python-1.8.3/chalk_remote_call/_version.py +1 -0
- {chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3/chalk_remote_call_python.egg-info}/PKG-INFO +1 -1
- chalk_remote_call_python-1.8.1/chalk_remote_call/_version.py +0 -1
- {chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/MANIFEST.in +0 -0
- {chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/README.md +0 -0
- {chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk-remote-call-rs/Cargo.toml +0 -0
- {chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk-remote-call-rs/chalk-remote-call-proto/Cargo.toml +0 -0
- {chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/chalk.auth.v1.rs +0 -0
- {chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/chalk.common.v1.rs +0 -0
- {chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/chalk.runtime.v1.rs +0 -0
- {chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/chalk.runtime.v1.tonic.rs +0 -0
- {chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/chalk.utils.v1.rs +0 -0
- {chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/descriptor.bin +0 -0
- {chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk-remote-call-rs/chalk-remote-call-proto/src/lib.rs +0 -0
- {chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk-remote-call-rs/rust-toolchain.toml +0 -0
- {chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk_remote_call/__init__.py +0 -0
- {chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk_remote_call/__main__.py +0 -0
- {chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk_remote_call/_gen/__init__.py +0 -0
- {chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk_remote_call/_gen/chalk/__init__.py +0 -0
- {chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk_remote_call/_gen/chalk/auth/__init__.py +0 -0
- {chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk_remote_call/_gen/chalk/auth/v1/__init__.py +0 -0
- {chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk_remote_call/_gen/chalk/auth/v1/permissions_pb2.py +0 -0
- {chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk_remote_call/_gen/chalk/auth/v1/permissions_pb2_grpc.py +0 -0
- {chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk_remote_call/_gen/chalk/common/__init__.py +0 -0
- {chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk_remote_call/_gen/chalk/common/v1/__init__.py +0 -0
- {chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk_remote_call/_gen/chalk/common/v1/chalk_error_pb2.py +0 -0
- {chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk_remote_call/_gen/chalk/common/v1/chalk_error_pb2_grpc.py +0 -0
- {chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk_remote_call/_gen/chalk/runtime/__init__.py +0 -0
- {chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk_remote_call/_gen/chalk/runtime/v1/__init__.py +0 -0
- {chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk_remote_call/_gen/chalk/runtime/v1/remote_python_call_pb2.py +0 -0
- {chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk_remote_call/_gen/chalk/runtime/v1/remote_python_call_pb2_grpc.py +0 -0
- {chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk_remote_call/_gen/chalk/utils/__init__.py +0 -0
- {chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk_remote_call/_gen/chalk/utils/v1/__init__.py +0 -0
- {chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk_remote_call/_gen/chalk/utils/v1/encoding_pb2.py +0 -0
- {chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk_remote_call/_gen/chalk/utils/v1/encoding_pb2_grpc.py +0 -0
- {chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk_remote_call/_gen/chalk/utils/v1/field_change_pb2.py +0 -0
- {chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk_remote_call/_gen/chalk/utils/v1/field_change_pb2_grpc.py +0 -0
- {chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk_remote_call/_gen/chalk/utils/v1/sensitive_pb2.py +0 -0
- {chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk_remote_call/_gen/chalk/utils/v1/sensitive_pb2_grpc.py +0 -0
- {chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk_remote_call/_native.pyi +0 -0
- {chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk_remote_call/arrow_utils.py +0 -0
- {chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk_remote_call/cli.py +0 -0
- {chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk_remote_call/handler_loader.py +0 -0
- {chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk_remote_call/input_transform.py +0 -0
- {chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk_remote_call/server.py +0 -0
- {chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk_remote_call/servicer.py +0 -0
- {chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk_remote_call/tracing.py +0 -0
- {chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk_remote_call_python.egg-info/SOURCES.txt +0 -0
- {chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk_remote_call_python.egg-info/dependency_links.txt +0 -0
- {chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk_remote_call_python.egg-info/entry_points.txt +0 -0
- {chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk_remote_call_python.egg-info/requires.txt +0 -0
- {chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk_remote_call_python.egg-info/top_level.txt +0 -0
- {chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/pyproject.toml +0 -0
- {chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/setup.cfg +0 -0
- {chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/setup.py +0 -0
{chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk-remote-call-rs/Cargo.lock
RENAMED
|
@@ -483,8 +483,8 @@ dependencies = [
|
|
|
483
483
|
|
|
484
484
|
[[package]]
|
|
485
485
|
name = "chalk-core"
|
|
486
|
-
version = "0.
|
|
487
|
-
source = "git+https://github.com/chalk-ai/chalk-private.git?rev=
|
|
486
|
+
version = "0.0.0"
|
|
487
|
+
source = "git+https://github.com/chalk-ai/chalk-private.git?rev=ae8e792f746f77253f682ee0c56b246cfdd88f51#ae8e792f746f77253f682ee0c56b246cfdd88f51"
|
|
488
488
|
dependencies = [
|
|
489
489
|
"anyhow",
|
|
490
490
|
"envconfig",
|
|
@@ -498,8 +498,8 @@ dependencies = [
|
|
|
498
498
|
|
|
499
499
|
[[package]]
|
|
500
500
|
name = "chalk-protos"
|
|
501
|
-
version = "0.
|
|
502
|
-
source = "git+https://github.com/chalk-ai/chalk-private.git?rev=
|
|
501
|
+
version = "0.0.0"
|
|
502
|
+
source = "git+https://github.com/chalk-ai/chalk-private.git?rev=ae8e792f746f77253f682ee0c56b246cfdd88f51#ae8e792f746f77253f682ee0c56b246cfdd88f51"
|
|
503
503
|
dependencies = [
|
|
504
504
|
"prost 0.11.9",
|
|
505
505
|
"prost-build",
|
|
@@ -510,8 +510,8 @@ dependencies = [
|
|
|
510
510
|
|
|
511
511
|
[[package]]
|
|
512
512
|
name = "chalk-queue"
|
|
513
|
-
version = "0.
|
|
514
|
-
source = "git+https://github.com/chalk-ai/chalk-private.git?rev=
|
|
513
|
+
version = "0.0.0"
|
|
514
|
+
source = "git+https://github.com/chalk-ai/chalk-private.git?rev=ae8e792f746f77253f682ee0c56b246cfdd88f51#ae8e792f746f77253f682ee0c56b246cfdd88f51"
|
|
515
515
|
dependencies = [
|
|
516
516
|
"base64 0.22.1",
|
|
517
517
|
"redis",
|
|
@@ -559,8 +559,8 @@ dependencies = [
|
|
|
559
559
|
|
|
560
560
|
[[package]]
|
|
561
561
|
name = "chalk_metric_definitions"
|
|
562
|
-
version = "0.
|
|
563
|
-
source = "git+https://github.com/chalk-ai/chalk-private.git?rev=
|
|
562
|
+
version = "0.0.0"
|
|
563
|
+
source = "git+https://github.com/chalk-ai/chalk-private.git?rev=ae8e792f746f77253f682ee0c56b246cfdd88f51#ae8e792f746f77253f682ee0c56b246cfdd88f51"
|
|
564
564
|
dependencies = [
|
|
565
565
|
"chalk-protos",
|
|
566
566
|
"metrics-generator",
|
|
@@ -569,8 +569,8 @@ dependencies = [
|
|
|
569
569
|
|
|
570
570
|
[[package]]
|
|
571
571
|
name = "chalk_metrics"
|
|
572
|
-
version = "0.
|
|
573
|
-
source = "git+https://github.com/chalk-ai/chalk-private.git?rev=
|
|
572
|
+
version = "0.0.0"
|
|
573
|
+
source = "git+https://github.com/chalk-ai/chalk-private.git?rev=ae8e792f746f77253f682ee0c56b246cfdd88f51#ae8e792f746f77253f682ee0c56b246cfdd88f51"
|
|
574
574
|
dependencies = [
|
|
575
575
|
"async-trait",
|
|
576
576
|
"chalk-core",
|
|
@@ -603,8 +603,8 @@ dependencies = [
|
|
|
603
603
|
|
|
604
604
|
[[package]]
|
|
605
605
|
name = "chalk_statsd"
|
|
606
|
-
version = "0.
|
|
607
|
-
source = "git+https://github.com/chalk-ai/chalk-private.git?rev=
|
|
606
|
+
version = "0.0.0"
|
|
607
|
+
source = "git+https://github.com/chalk-ai/chalk-private.git?rev=ae8e792f746f77253f682ee0c56b246cfdd88f51#ae8e792f746f77253f682ee0c56b246cfdd88f51"
|
|
608
608
|
dependencies = [
|
|
609
609
|
"chalk-core",
|
|
610
610
|
"chrono",
|
|
@@ -874,7 +874,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
|
874
874
|
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
|
875
875
|
dependencies = [
|
|
876
876
|
"libc",
|
|
877
|
-
"windows-sys 0.
|
|
877
|
+
"windows-sys 0.61.2",
|
|
878
878
|
]
|
|
879
879
|
|
|
880
880
|
[[package]]
|
|
@@ -1472,7 +1472,7 @@ dependencies = [
|
|
|
1472
1472
|
"libc",
|
|
1473
1473
|
"percent-encoding",
|
|
1474
1474
|
"pin-project-lite",
|
|
1475
|
-
"socket2 0.
|
|
1475
|
+
"socket2 0.6.3",
|
|
1476
1476
|
"tokio",
|
|
1477
1477
|
"tower-service",
|
|
1478
1478
|
"tracing",
|
|
@@ -1867,7 +1867,7 @@ dependencies = [
|
|
|
1867
1867
|
[[package]]
|
|
1868
1868
|
name = "metrics-generator"
|
|
1869
1869
|
version = "0.1.0"
|
|
1870
|
-
source = "git+https://github.com/chalk-ai/chalk-private.git?rev=
|
|
1870
|
+
source = "git+https://github.com/chalk-ai/chalk-private.git?rev=ae8e792f746f77253f682ee0c56b246cfdd88f51#ae8e792f746f77253f682ee0c56b246cfdd88f51"
|
|
1871
1871
|
dependencies = [
|
|
1872
1872
|
"codegen",
|
|
1873
1873
|
"serde",
|
|
@@ -2100,7 +2100,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
|
2100
2100
|
checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967"
|
|
2101
2101
|
dependencies = [
|
|
2102
2102
|
"libc",
|
|
2103
|
-
"windows-sys 0.
|
|
2103
|
+
"windows-sys 0.61.2",
|
|
2104
2104
|
]
|
|
2105
2105
|
|
|
2106
2106
|
[[package]]
|
|
@@ -2761,7 +2761,7 @@ dependencies = [
|
|
|
2761
2761
|
"errno",
|
|
2762
2762
|
"libc",
|
|
2763
2763
|
"linux-raw-sys 0.12.1",
|
|
2764
|
-
"windows-sys 0.
|
|
2764
|
+
"windows-sys 0.61.2",
|
|
2765
2765
|
]
|
|
2766
2766
|
|
|
2767
2767
|
[[package]]
|
|
@@ -3128,10 +3128,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
|
3128
3128
|
checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
|
|
3129
3129
|
dependencies = [
|
|
3130
3130
|
"fastrand",
|
|
3131
|
-
"getrandom 0.
|
|
3131
|
+
"getrandom 0.4.2",
|
|
3132
3132
|
"once_cell",
|
|
3133
3133
|
"rustix 1.1.4",
|
|
3134
|
-
"windows-sys 0.
|
|
3134
|
+
"windows-sys 0.61.2",
|
|
3135
3135
|
]
|
|
3136
3136
|
|
|
3137
3137
|
[[package]]
|
|
@@ -3607,6 +3607,16 @@ dependencies = [
|
|
|
3607
3607
|
"tracing-core",
|
|
3608
3608
|
]
|
|
3609
3609
|
|
|
3610
|
+
[[package]]
|
|
3611
|
+
name = "tracing-serde"
|
|
3612
|
+
version = "0.2.0"
|
|
3613
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
3614
|
+
checksum = "704b1aeb7be0d0a84fc9828cae51dab5970fee5088f83d1dd7ee6f6246fc6ff1"
|
|
3615
|
+
dependencies = [
|
|
3616
|
+
"serde",
|
|
3617
|
+
"tracing-core",
|
|
3618
|
+
]
|
|
3619
|
+
|
|
3610
3620
|
[[package]]
|
|
3611
3621
|
name = "tracing-subscriber"
|
|
3612
3622
|
version = "0.3.23"
|
|
@@ -3617,12 +3627,15 @@ dependencies = [
|
|
|
3617
3627
|
"nu-ansi-term",
|
|
3618
3628
|
"once_cell",
|
|
3619
3629
|
"regex-automata",
|
|
3630
|
+
"serde",
|
|
3631
|
+
"serde_json",
|
|
3620
3632
|
"sharded-slab",
|
|
3621
3633
|
"smallvec",
|
|
3622
3634
|
"thread_local",
|
|
3623
3635
|
"tracing",
|
|
3624
3636
|
"tracing-core",
|
|
3625
3637
|
"tracing-log",
|
|
3638
|
+
"tracing-serde",
|
|
3626
3639
|
]
|
|
3627
3640
|
|
|
3628
3641
|
[[package]]
|
|
@@ -19,7 +19,7 @@ tokio = { version = "1", features = ["macros", "rt-multi-thread", "sync", "signa
|
|
|
19
19
|
tokio-stream = "0.1"
|
|
20
20
|
arrow = { version = "54", default-features = false, features = ["ipc"] }
|
|
21
21
|
tracing = "0.1"
|
|
22
|
-
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
|
22
|
+
tracing-subscriber = { version = "0.3", features = ["env-filter", "json"] }
|
|
23
23
|
bytes = "1"
|
|
24
24
|
uuid = { version = "1", features = ["v4"] }
|
|
25
25
|
dashmap = "5"
|
|
@@ -36,6 +36,6 @@ futures-util = "0.3"
|
|
|
36
36
|
# `[patch.crates-io]` here it builds against vanilla crates.io redis (it only
|
|
37
37
|
# uses standard APIs). Same rev across all three so cargo reuses one checkout.
|
|
38
38
|
[target.'cfg(target_os = "linux")'.dependencies]
|
|
39
|
-
chalk-queue = { git = "https://github.com/chalk-ai/chalk-private.git", rev = "
|
|
40
|
-
chalk_metrics = { git = "https://github.com/chalk-ai/chalk-private.git", rev = "
|
|
41
|
-
chalk_statsd = { git = "https://github.com/chalk-ai/chalk-private.git", rev = "
|
|
39
|
+
chalk-queue = { git = "https://github.com/chalk-ai/chalk-private.git", rev = "ae8e792f746f77253f682ee0c56b246cfdd88f51" }
|
|
40
|
+
chalk_metrics = { git = "https://github.com/chalk-ai/chalk-private.git", rev = "ae8e792f746f77253f682ee0c56b246cfdd88f51" }
|
|
41
|
+
chalk_statsd = { git = "https://github.com/chalk-ai/chalk-private.git", rev = "ae8e792f746f77253f682ee0c56b246cfdd88f51" }
|
|
@@ -10,7 +10,7 @@ use chalk_remote_call_proto::chalk::runtime::v1::{
|
|
|
10
10
|
PollRemoteCallResponse, PurgeQueueRequest, PurgeQueueResponse, RemoteCallStatus,
|
|
11
11
|
};
|
|
12
12
|
use tonic::{Request, Response, Status};
|
|
13
|
-
use tracing::{debug, error, info, instrument};
|
|
13
|
+
use tracing::{debug, error, info, instrument, warn};
|
|
14
14
|
|
|
15
15
|
use crate::python_bridge::{ChunkBuffer, PythonHandler};
|
|
16
16
|
use crate::service::extract_metadata;
|
|
@@ -87,10 +87,15 @@ pub struct AsyncRemoteCallServiceImpl {
|
|
|
87
87
|
/// the per-call dispatch does not satisfy, so async enqueue is refused
|
|
88
88
|
/// rather than silently calling the handler with the wrong shape.
|
|
89
89
|
batching_enabled: bool,
|
|
90
|
+
is_remote_function: bool,
|
|
90
91
|
}
|
|
91
92
|
|
|
92
93
|
impl AsyncRemoteCallServiceImpl {
|
|
93
|
-
pub fn new(
|
|
94
|
+
pub fn new(
|
|
95
|
+
python_handler: Arc<PythonHandler>,
|
|
96
|
+
batching_enabled: bool,
|
|
97
|
+
is_remote_function: bool,
|
|
98
|
+
) -> Self {
|
|
94
99
|
let registry: Registry = Arc::new(Mutex::new(HashMap::new()));
|
|
95
100
|
// Runs inside the server's tokio runtime (the service is constructed
|
|
96
101
|
// within `run_server`); reclaims orphaned terminal entries.
|
|
@@ -100,6 +105,7 @@ impl AsyncRemoteCallServiceImpl {
|
|
|
100
105
|
registry,
|
|
101
106
|
next_id: AtomicU64::new(1),
|
|
102
107
|
batching_enabled,
|
|
108
|
+
is_remote_function,
|
|
103
109
|
}
|
|
104
110
|
}
|
|
105
111
|
}
|
|
@@ -125,6 +131,13 @@ impl AsyncRemoteCallService for AsyncRemoteCallServiceImpl {
|
|
|
125
131
|
let req = request.into_inner();
|
|
126
132
|
let function_name = req.name;
|
|
127
133
|
|
|
134
|
+
if self.is_remote_function {
|
|
135
|
+
warn!(
|
|
136
|
+
function_name = %function_name,
|
|
137
|
+
"this enqueue is not fronted by the fnq server (CHALK_REMOTE_FUNCTION is set to true)"
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
128
141
|
// Only inline feather bytes are supported today; the object-store
|
|
129
142
|
// variant is reserved for large payloads and not wired up here.
|
|
130
143
|
let ipc_bytes = match req.args.and_then(|a| a.args) {
|
|
@@ -177,7 +190,12 @@ impl AsyncRemoteCallService for AsyncRemoteCallServiceImpl {
|
|
|
177
190
|
Err(e) => {
|
|
178
191
|
s.status = RemoteCallStatus::Failed;
|
|
179
192
|
s.error = Some(e.to_string());
|
|
180
|
-
error!(
|
|
193
|
+
error!(
|
|
194
|
+
call_id = %id,
|
|
195
|
+
exception.stacktrace = e.details(),
|
|
196
|
+
"async call failed: {}",
|
|
197
|
+
e.details()
|
|
198
|
+
);
|
|
181
199
|
}
|
|
182
200
|
}
|
|
183
201
|
}
|
|
@@ -13,6 +13,8 @@ use tokio::task::JoinHandle;
|
|
|
13
13
|
use tonic::Status;
|
|
14
14
|
use tracing::error;
|
|
15
15
|
|
|
16
|
+
#[cfg(target_os = "linux")]
|
|
17
|
+
use chalk_metrics::PublishingMetricsPipeline;
|
|
16
18
|
use chalk_remote_call_proto::chalk::runtime::v1::CallFunctionResponse;
|
|
17
19
|
|
|
18
20
|
use crate::python_bridge::{CallerInput, PythonHandler};
|
|
@@ -35,10 +37,17 @@ pub struct CoalescingQueue {
|
|
|
35
37
|
max_size: usize,
|
|
36
38
|
max_duration: Duration,
|
|
37
39
|
state: Mutex<QueueState>,
|
|
40
|
+
#[cfg(target_os = "linux")]
|
|
41
|
+
pipeline: Option<Arc<PublishingMetricsPipeline>>,
|
|
38
42
|
}
|
|
39
43
|
|
|
40
44
|
impl CoalescingQueue {
|
|
41
|
-
pub fn new(
|
|
45
|
+
pub fn new(
|
|
46
|
+
handler: Arc<PythonHandler>,
|
|
47
|
+
max_size: usize,
|
|
48
|
+
max_duration_ms: u64,
|
|
49
|
+
#[cfg(target_os = "linux")] pipeline: Option<Arc<PublishingMetricsPipeline>>,
|
|
50
|
+
) -> Arc<Self> {
|
|
42
51
|
Arc::new(Self {
|
|
43
52
|
handler,
|
|
44
53
|
max_size,
|
|
@@ -47,6 +56,8 @@ impl CoalescingQueue {
|
|
|
47
56
|
items: Vec::new(),
|
|
48
57
|
flush_task: None,
|
|
49
58
|
}),
|
|
59
|
+
#[cfg(target_os = "linux")]
|
|
60
|
+
pipeline,
|
|
50
61
|
})
|
|
51
62
|
}
|
|
52
63
|
|
|
@@ -90,17 +101,33 @@ impl CoalescingQueue {
|
|
|
90
101
|
// Dispatch off the queue's critical section — don't block further
|
|
91
102
|
// submissions while Python runs.
|
|
92
103
|
let handler = Arc::clone(&self.handler);
|
|
104
|
+
#[cfg(target_os = "linux")]
|
|
105
|
+
let pipeline = self.pipeline.clone();
|
|
93
106
|
tokio::spawn(async move {
|
|
94
|
-
Self::dispatch(
|
|
107
|
+
Self::dispatch(
|
|
108
|
+
handler,
|
|
109
|
+
items,
|
|
110
|
+
#[cfg(target_os = "linux")]
|
|
111
|
+
pipeline,
|
|
112
|
+
)
|
|
113
|
+
.await;
|
|
95
114
|
});
|
|
96
115
|
}
|
|
97
116
|
|
|
98
|
-
async fn dispatch(
|
|
117
|
+
async fn dispatch(
|
|
118
|
+
handler: Arc<PythonHandler>,
|
|
119
|
+
items: Vec<BufferedCall>,
|
|
120
|
+
#[cfg(target_os = "linux")] pipeline: Option<Arc<PublishingMetricsPipeline>>,
|
|
121
|
+
) {
|
|
99
122
|
// Split each BufferedCall into its input (for the bridge) and its
|
|
100
123
|
// response channel (for routing the reply back).
|
|
101
124
|
let n = items.len();
|
|
102
125
|
let mut callers: Vec<CallerInput> = Vec::with_capacity(n);
|
|
103
126
|
let mut response_txs = Vec::with_capacity(n);
|
|
127
|
+
#[cfg(target_os = "linux")]
|
|
128
|
+
let function_name = items[0].input.function_name.clone();
|
|
129
|
+
#[cfg(target_os = "linux")]
|
|
130
|
+
let started = std::time::Instant::now();
|
|
104
131
|
for item in items {
|
|
105
132
|
callers.push(item.input);
|
|
106
133
|
response_txs.push(item.response_tx);
|
|
@@ -116,11 +143,31 @@ impl CoalescingQueue {
|
|
|
116
143
|
n
|
|
117
144
|
);
|
|
118
145
|
error!("{}", msg);
|
|
146
|
+
#[cfg(target_os = "linux")]
|
|
147
|
+
if let Some(p) = &pipeline {
|
|
148
|
+
crate::metrics::record_completed_call(
|
|
149
|
+
p,
|
|
150
|
+
&function_name,
|
|
151
|
+
/* success = */ false,
|
|
152
|
+
started.elapsed(),
|
|
153
|
+
chalk_metrics::metrics::tags::Mode::Sync,
|
|
154
|
+
);
|
|
155
|
+
}
|
|
119
156
|
for tx in &response_txs {
|
|
120
157
|
let _ = tx.send(Err(Status::unknown(msg.clone()))).await;
|
|
121
158
|
}
|
|
122
159
|
return;
|
|
123
160
|
}
|
|
161
|
+
#[cfg(target_os = "linux")]
|
|
162
|
+
if let Some(p) = &pipeline {
|
|
163
|
+
crate::metrics::record_completed_call(
|
|
164
|
+
p,
|
|
165
|
+
&function_name,
|
|
166
|
+
/* success = */ true,
|
|
167
|
+
started.elapsed(),
|
|
168
|
+
chalk_metrics::metrics::tags::Mode::Sync,
|
|
169
|
+
);
|
|
170
|
+
}
|
|
124
171
|
for (tx, chunk) in response_txs.into_iter().zip(per_caller_bytes) {
|
|
125
172
|
let msg = CallFunctionResponse {
|
|
126
173
|
feather_stream: chunk.into(),
|
|
@@ -129,9 +176,23 @@ impl CoalescingQueue {
|
|
|
129
176
|
}
|
|
130
177
|
}
|
|
131
178
|
Err(e) => {
|
|
132
|
-
error!(
|
|
179
|
+
error!(
|
|
180
|
+
exception.stacktrace = e.details(),
|
|
181
|
+
"Coalesced handler error: {}",
|
|
182
|
+
e.details()
|
|
183
|
+
);
|
|
133
184
|
// IPC decode errors are already caught by the service's
|
|
134
185
|
// pre-validation; anything reaching here is a handler error.
|
|
186
|
+
#[cfg(target_os = "linux")]
|
|
187
|
+
if let Some(p) = &pipeline {
|
|
188
|
+
crate::metrics::record_completed_call(
|
|
189
|
+
p,
|
|
190
|
+
&function_name,
|
|
191
|
+
/* success = */ false,
|
|
192
|
+
started.elapsed(),
|
|
193
|
+
chalk_metrics::metrics::tags::Mode::Sync,
|
|
194
|
+
);
|
|
195
|
+
}
|
|
135
196
|
let status =
|
|
136
197
|
Status::unknown(format!("Exception raised during handler execution: {e}"));
|
|
137
198
|
for tx in response_txs {
|
|
@@ -44,8 +44,12 @@ fn start_server(
|
|
|
44
44
|
max_batching_size: usize,
|
|
45
45
|
max_buffer_duration_ms: u64,
|
|
46
46
|
) -> PyResult<()> {
|
|
47
|
-
//
|
|
47
|
+
// Kubernetes consumes stdout/stderr as a machine-readable log stream. Keep
|
|
48
|
+
// event fields flattened so collectors can read `level`, `target`,
|
|
49
|
+
// `message`, and fields such as `error` without parsing terminal output.
|
|
48
50
|
let _ = tracing_subscriber::fmt()
|
|
51
|
+
.json()
|
|
52
|
+
.flatten_event(true)
|
|
49
53
|
.with_env_filter(
|
|
50
54
|
EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info")),
|
|
51
55
|
)
|
|
@@ -53,16 +57,6 @@ fn start_server(
|
|
|
53
57
|
|
|
54
58
|
let python_handler = Arc::new(PythonHandler::new(handler, process_fn, arg_names));
|
|
55
59
|
|
|
56
|
-
let coalescing_queue = if max_batching_size > 0 {
|
|
57
|
-
Some(CoalescingQueue::new(
|
|
58
|
-
Arc::clone(&python_handler),
|
|
59
|
-
max_batching_size,
|
|
60
|
-
max_buffer_duration_ms,
|
|
61
|
-
))
|
|
62
|
-
} else {
|
|
63
|
-
None
|
|
64
|
-
};
|
|
65
|
-
|
|
66
60
|
// Release the GIL while the Rust server runs
|
|
67
61
|
py.detach(|| {
|
|
68
62
|
let rt = tokio::runtime::Builder::new_multi_thread()
|
|
@@ -75,12 +69,35 @@ fn start_server(
|
|
|
75
69
|
))
|
|
76
70
|
})?;
|
|
77
71
|
|
|
78
|
-
rt.block_on(
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
72
|
+
rt.block_on(async {
|
|
73
|
+
// Linux-only: the self-consumer + its metrics pipeline depend on
|
|
74
|
+
// chalk-queue / chalk_metrics, since these don't compile on macos
|
|
75
|
+
// and windows. Built inside the runtime since it needs to `.await`.
|
|
76
|
+
#[cfg(target_os = "linux")]
|
|
77
|
+
let pipeline = crate::metrics::init_pipeline().await;
|
|
78
|
+
|
|
79
|
+
let coalescing_queue = if max_batching_size > 0 {
|
|
80
|
+
Some(CoalescingQueue::new(
|
|
81
|
+
Arc::clone(&python_handler),
|
|
82
|
+
max_batching_size,
|
|
83
|
+
max_buffer_duration_ms,
|
|
84
|
+
#[cfg(target_os = "linux")]
|
|
85
|
+
pipeline.clone(),
|
|
86
|
+
))
|
|
87
|
+
} else {
|
|
88
|
+
None
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
server::run_server(
|
|
92
|
+
host,
|
|
93
|
+
port,
|
|
94
|
+
python_handler,
|
|
95
|
+
coalescing_queue,
|
|
96
|
+
#[cfg(target_os = "linux")]
|
|
97
|
+
pipeline,
|
|
98
|
+
)
|
|
99
|
+
.await
|
|
100
|
+
})
|
|
84
101
|
.map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(format!("Server error: {e}")))
|
|
85
102
|
})
|
|
86
103
|
}
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
use std::sync::Arc;
|
|
11
11
|
use std::time::Duration;
|
|
12
12
|
|
|
13
|
-
use chalk_metrics::metrics::tags::{FunctionName, Status as MetricStatus, TagValue};
|
|
13
|
+
use chalk_metrics::metrics::tags::{FunctionName, Mode, Status as MetricStatus, TagValue};
|
|
14
14
|
use chalk_metrics::metrics::well_known_metrics::WellKnownMetricName;
|
|
15
15
|
use chalk_metrics::{
|
|
16
16
|
make_pubsub_publisher, BatchConfig, BusPublisher, MetricsPipeline, PublishingMetricsPipeline,
|
|
@@ -30,7 +30,7 @@ const BATCH_MAX_LATENCY: Duration = Duration::from_millis(100);
|
|
|
30
30
|
/// The bus publisher is what reaches the metrics store (and thus the dashboard);
|
|
31
31
|
/// statsd alone only reaches Datadog. An empty `METRICS_BUS_TOPIC` therefore
|
|
32
32
|
/// means the dashboard won't move — surfaced as a warning, not an error.
|
|
33
|
-
pub async fn init_pipeline() -> Option<Arc<
|
|
33
|
+
pub async fn init_pipeline() -> Option<Arc<PublishingMetricsPipeline>> {
|
|
34
34
|
let environment_id = std::env::var("CHALK_ENVIRONMENT_ID").unwrap_or_default();
|
|
35
35
|
|
|
36
36
|
let statsd = Arc::new(
|
|
@@ -45,7 +45,7 @@ pub async fn init_pipeline() -> Option<Arc<dyn MetricsPipeline>> {
|
|
|
45
45
|
warn!("no metrics bus publisher configured; function call metrics will not reach the dashboard");
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
let metrics_pipeline: Arc<
|
|
48
|
+
let metrics_pipeline: Arc<PublishingMetricsPipeline> =
|
|
49
49
|
Arc::new(PublishingMetricsPipeline::new_with_env_var_defaults(
|
|
50
50
|
environment_id,
|
|
51
51
|
String::new(),
|
|
@@ -111,10 +111,11 @@ async fn build_bus_publisher() -> Option<Arc<dyn BusPublisher>> {
|
|
|
111
111
|
/// carry the scaling-group revision tag used by the legacy catalog consumer;
|
|
112
112
|
/// the dashboard filters by function name.
|
|
113
113
|
pub fn record_completed_call(
|
|
114
|
-
pipeline: &Arc<
|
|
114
|
+
pipeline: &Arc<PublishingMetricsPipeline>,
|
|
115
115
|
function_name: &str,
|
|
116
116
|
success: bool,
|
|
117
117
|
elapsed: Duration,
|
|
118
|
+
mode: Mode,
|
|
118
119
|
) {
|
|
119
120
|
let status = if success {
|
|
120
121
|
MetricStatus::Success
|
|
@@ -124,6 +125,7 @@ pub fn record_completed_call(
|
|
|
124
125
|
let tags = vec![
|
|
125
126
|
TagValue::FunctionName(FunctionName(function_name.to_string())),
|
|
126
127
|
TagValue::Status(status.clone()),
|
|
128
|
+
TagValue::Mode(mode.clone()),
|
|
127
129
|
];
|
|
128
130
|
|
|
129
131
|
if let Err(e) = pipeline.count(WellKnownMetricName::FunctionCallDequeued, 1.0, tags.clone()) {
|
|
@@ -74,7 +74,7 @@ impl PythonHandler {
|
|
|
74
74
|
})
|
|
75
75
|
})
|
|
76
76
|
.await
|
|
77
|
-
.map_err(|e| PythonError(format!("Task join error: {e}")))?
|
|
77
|
+
.map_err(|e| PythonError::new(format!("Task join error: {e}")))?
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
/// Non-batched streaming dispatch. Calls `process_batches(..., emit)` so
|
|
@@ -147,7 +147,7 @@ impl PythonHandler {
|
|
|
147
147
|
})
|
|
148
148
|
})
|
|
149
149
|
.await
|
|
150
|
-
.map_err(|e| PythonError(format!("Task join error: {e}")))?
|
|
150
|
+
.map_err(|e| PythonError::new(format!("Task join error: {e}")))?
|
|
151
151
|
}
|
|
152
152
|
}
|
|
153
153
|
|
|
@@ -305,13 +305,13 @@ fn invoke_process_fn(
|
|
|
305
305
|
let result_list: &Bound<'_, PyList> = result
|
|
306
306
|
.bind(py)
|
|
307
307
|
.cast::<PyList>()
|
|
308
|
-
.map_err(|e| PythonError(format!("bridge must return a list, got: {e}")))?;
|
|
308
|
+
.map_err(|e| PythonError::new(format!("bridge must return a list, got: {e}")))?;
|
|
309
309
|
|
|
310
310
|
let mut out = Vec::with_capacity(result_list.len());
|
|
311
311
|
for item in result_list.iter() {
|
|
312
|
-
let bytes_ref: &Bound<'_, PyBytes> = item
|
|
313
|
-
|
|
314
|
-
|
|
312
|
+
let bytes_ref: &Bound<'_, PyBytes> = item.cast::<PyBytes>().map_err(|e| {
|
|
313
|
+
PythonError::new(format!("bridge must return list[bytes], got item: {e}"))
|
|
314
|
+
})?;
|
|
315
315
|
out.push(bytes_ref.as_bytes().to_vec());
|
|
316
316
|
}
|
|
317
317
|
Ok(out)
|
|
@@ -319,14 +319,38 @@ fn invoke_process_fn(
|
|
|
319
319
|
|
|
320
320
|
/// Error type for Python handler invocations.
|
|
321
321
|
#[derive(Debug)]
|
|
322
|
-
pub struct PythonError
|
|
322
|
+
pub struct PythonError {
|
|
323
|
+
details: String,
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
impl PythonError {
|
|
327
|
+
fn new(message: String) -> Self {
|
|
328
|
+
Self { details: message }
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
pub fn details(&self) -> &str {
|
|
332
|
+
&self.details
|
|
333
|
+
}
|
|
334
|
+
}
|
|
323
335
|
|
|
324
336
|
impl std::fmt::Display for PythonError {
|
|
325
337
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
326
|
-
write!(f, "{}", self.
|
|
338
|
+
write!(f, "{}", self.details)
|
|
327
339
|
}
|
|
328
340
|
}
|
|
329
341
|
|
|
330
342
|
fn into_python_error(py: Python<'_>, err: PyErr) -> PythonError {
|
|
331
|
-
|
|
343
|
+
let summary = err.to_string();
|
|
344
|
+
let details = format_python_traceback(py, &err).unwrap_or_else(|_| summary.clone());
|
|
345
|
+
PythonError { details }
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
fn format_python_traceback(py: Python<'_>, err: &PyErr) -> PyResult<String> {
|
|
349
|
+
let traceback = py.import("traceback")?;
|
|
350
|
+
let formatted = traceback.call_method1(
|
|
351
|
+
"format_exception",
|
|
352
|
+
(err.get_type(py), err.value(py), err.traceback(py)),
|
|
353
|
+
)?;
|
|
354
|
+
let lines: Vec<String> = formatted.extract()?;
|
|
355
|
+
Ok(lines.concat().trim_end().to_string())
|
|
332
356
|
}
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
//! caller. The semaphore still admits one slot per call, so it remains the
|
|
29
29
|
//! producer of the concurrency the coalescer batches.
|
|
30
30
|
|
|
31
|
+
use std::collections::HashMap;
|
|
31
32
|
use std::panic::AssertUnwindSafe;
|
|
32
33
|
use std::sync::{Arc, Mutex};
|
|
33
34
|
use std::time::{Duration, Instant};
|
|
@@ -44,7 +45,8 @@ use tokio::sync::mpsc;
|
|
|
44
45
|
use tracing::{error, info, warn};
|
|
45
46
|
use uuid::Uuid;
|
|
46
47
|
|
|
47
|
-
use chalk_metrics::
|
|
48
|
+
use chalk_metrics::metrics::tags::Mode;
|
|
49
|
+
use chalk_metrics::PublishingMetricsPipeline;
|
|
48
50
|
|
|
49
51
|
use crate::coalesce::{BufferedCall, CoalescingQueue};
|
|
50
52
|
use crate::python_bridge::{CallerInput, ChunkBuffer, PythonHandler};
|
|
@@ -186,7 +188,7 @@ type LiveTokens = Arc<DashSet<String>>;
|
|
|
186
188
|
pub async fn run(
|
|
187
189
|
config: SelfConsumerConfig,
|
|
188
190
|
handler: Arc<PythonHandler>,
|
|
189
|
-
metrics_pipeline: Option<Arc<
|
|
191
|
+
metrics_pipeline: Option<Arc<PublishingMetricsPipeline>>,
|
|
190
192
|
coalescing_queue: Option<Arc<CoalescingQueue>>,
|
|
191
193
|
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
|
192
194
|
let client = Client::open(config.redis_url.clone())?;
|
|
@@ -310,7 +312,7 @@ async fn run_one_call(
|
|
|
310
312
|
lease: SemaphoreLease,
|
|
311
313
|
live_tokens: LiveTokens,
|
|
312
314
|
flush_timeout: Duration,
|
|
313
|
-
metrics_pipeline: Option<Arc<
|
|
315
|
+
metrics_pipeline: Option<Arc<PublishingMetricsPipeline>>,
|
|
314
316
|
) {
|
|
315
317
|
let call_id = work.call_id.clone();
|
|
316
318
|
match AssertUnwindSafe(execute_and_write(
|
|
@@ -362,7 +364,7 @@ async fn run_one_coalesced_call(
|
|
|
362
364
|
work: QueuedCall,
|
|
363
365
|
lease: SemaphoreLease,
|
|
364
366
|
live_tokens: LiveTokens,
|
|
365
|
-
metrics_pipeline: Option<Arc<
|
|
367
|
+
metrics_pipeline: Option<Arc<PublishingMetricsPipeline>>,
|
|
366
368
|
) {
|
|
367
369
|
let call_id = work.call_id.clone();
|
|
368
370
|
if let Err(e) = coalesced_execute_and_write(
|
|
@@ -397,13 +399,14 @@ async fn coalesced_execute_and_write(
|
|
|
397
399
|
queue: &FunctionQueue,
|
|
398
400
|
conn: &mut ConnectionManager,
|
|
399
401
|
work: QueuedCall,
|
|
400
|
-
metrics_pipeline: Option<&Arc<
|
|
402
|
+
metrics_pipeline: Option<&Arc<PublishingMetricsPipeline>>,
|
|
401
403
|
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
|
402
404
|
let call_uuid = Uuid::parse_str(&work.call_id)?;
|
|
403
405
|
let stream_key = queue.stream_key_for(call_uuid);
|
|
404
406
|
let ipc_bytes = work.call.feather_bytes()?;
|
|
405
407
|
let function_name = work.call.name.clone();
|
|
406
|
-
let metadata = work.metadata.otel_headers.clone();
|
|
408
|
+
let mut metadata = work.metadata.otel_headers.clone();
|
|
409
|
+
let processing_trace_id = ensure_traceparent(&mut metadata);
|
|
407
410
|
let started = Instant::now();
|
|
408
411
|
|
|
409
412
|
let _ = queue.update_status(call_uuid, "running").await;
|
|
@@ -430,7 +433,13 @@ async fn coalesced_execute_and_write(
|
|
|
430
433
|
// stream writes below — parity with the streaming path.
|
|
431
434
|
let success = matches!(response, Some(Ok(_)));
|
|
432
435
|
if let Some(p) = metrics_pipeline {
|
|
433
|
-
crate::metrics::record_completed_call(
|
|
436
|
+
crate::metrics::record_completed_call(
|
|
437
|
+
p,
|
|
438
|
+
&function_name,
|
|
439
|
+
success,
|
|
440
|
+
started.elapsed(),
|
|
441
|
+
Mode::Async,
|
|
442
|
+
);
|
|
434
443
|
}
|
|
435
444
|
|
|
436
445
|
match response {
|
|
@@ -438,7 +447,12 @@ async fn coalesced_execute_and_write(
|
|
|
438
447
|
stream_writer::write_chunk(conn, &stream_key, 1, &resp.feather_stream).await?;
|
|
439
448
|
stream_writer::write_end(conn, &stream_key, 2).await?;
|
|
440
449
|
let _ = queue
|
|
441
|
-
.complete_status(
|
|
450
|
+
.complete_status(
|
|
451
|
+
call_uuid,
|
|
452
|
+
"completed",
|
|
453
|
+
"1 chunk(s)",
|
|
454
|
+
Some(processing_trace_id),
|
|
455
|
+
)
|
|
442
456
|
.await;
|
|
443
457
|
info!(call_id = %work.call_id, "coalesced queued call completed");
|
|
444
458
|
}
|
|
@@ -455,7 +469,9 @@ async fn coalesced_execute_and_write(
|
|
|
455
469
|
)
|
|
456
470
|
.await?;
|
|
457
471
|
let summary: String = status.message().chars().take(200).collect();
|
|
458
|
-
let _ = queue
|
|
472
|
+
let _ = queue
|
|
473
|
+
.complete_status(call_uuid, "failed", &summary, Some(processing_trace_id))
|
|
474
|
+
.await;
|
|
459
475
|
error!(call_id = %work.call_id, error = %status, "coalesced queued call failed");
|
|
460
476
|
}
|
|
461
477
|
None => {
|
|
@@ -465,13 +481,31 @@ async fn coalesced_execute_and_write(
|
|
|
465
481
|
let msg = "coalesced dispatch produced no response";
|
|
466
482
|
stream_writer::write_error(conn, &stream_key, 1, "RemoteFunctionError", msg, None)
|
|
467
483
|
.await?;
|
|
468
|
-
let _ = queue
|
|
484
|
+
let _ = queue
|
|
485
|
+
.complete_status(call_uuid, "failed", msg, Some(processing_trace_id))
|
|
486
|
+
.await;
|
|
469
487
|
error!(call_id = %work.call_id, "{msg}");
|
|
470
488
|
}
|
|
471
489
|
}
|
|
472
490
|
Ok(())
|
|
473
491
|
}
|
|
474
492
|
|
|
493
|
+
// Insert trace ID into headers if none already present
|
|
494
|
+
fn ensure_traceparent(headers: &mut HashMap<String, String>) -> String {
|
|
495
|
+
if let Some(tp) = headers.get("traceparent") {
|
|
496
|
+
if let Some(trace_id) = tp.split('-').nth(1).filter(|s| s.len() == 32) {
|
|
497
|
+
return trace_id.to_string();
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
let trace_id = Uuid::new_v4().simple().to_string();
|
|
501
|
+
let span_id = &Uuid::new_v4().simple().to_string()[..16];
|
|
502
|
+
headers.insert(
|
|
503
|
+
"traceparent".to_string(),
|
|
504
|
+
format!("00-{trace_id}-{span_id}-01"),
|
|
505
|
+
);
|
|
506
|
+
trace_id
|
|
507
|
+
}
|
|
508
|
+
|
|
475
509
|
/// Decode the payload, invoke the handler, and write the outcome (chunks then
|
|
476
510
|
/// `end`, or `error`) to the call's result stream, updating meta status along
|
|
477
511
|
/// the way.
|
|
@@ -481,13 +515,14 @@ async fn execute_and_write(
|
|
|
481
515
|
conn: &mut ConnectionManager,
|
|
482
516
|
work: QueuedCall,
|
|
483
517
|
flush_timeout: Duration,
|
|
484
|
-
metrics_pipeline: Option<&Arc<
|
|
518
|
+
metrics_pipeline: Option<&Arc<PublishingMetricsPipeline>>,
|
|
485
519
|
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
|
486
520
|
let call_uuid = Uuid::parse_str(&work.call_id)?;
|
|
487
521
|
let stream_key = queue.stream_key_for(call_uuid);
|
|
488
522
|
let ipc_bytes = work.call.feather_bytes()?;
|
|
489
523
|
let function_name = work.call.name.clone();
|
|
490
|
-
let metadata = work.metadata.otel_headers.clone();
|
|
524
|
+
let mut metadata = work.metadata.otel_headers.clone();
|
|
525
|
+
let processing_trace_id = ensure_traceparent(&mut metadata);
|
|
491
526
|
let started = Instant::now();
|
|
492
527
|
|
|
493
528
|
let _ = queue.update_status(call_uuid, "running").await;
|
|
@@ -543,7 +578,13 @@ async fn execute_and_write(
|
|
|
543
578
|
// so every executed call is recorded regardless of downstream Redis write
|
|
544
579
|
// failure. Mirrors the catalog consumer's per-call emission.
|
|
545
580
|
if let Some(p) = metrics_pipeline {
|
|
546
|
-
crate::metrics::record_completed_call(
|
|
581
|
+
crate::metrics::record_completed_call(
|
|
582
|
+
p,
|
|
583
|
+
&function_name,
|
|
584
|
+
result.is_ok(),
|
|
585
|
+
started.elapsed(),
|
|
586
|
+
Mode::Async,
|
|
587
|
+
);
|
|
547
588
|
}
|
|
548
589
|
|
|
549
590
|
// Flush whatever the handler produced after the loop exits (on the fast path
|
|
@@ -558,19 +599,37 @@ async fn execute_and_write(
|
|
|
558
599
|
Ok(()) => {
|
|
559
600
|
stream_writer::write_end(conn, &stream_key, seq).await?;
|
|
560
601
|
let _ = queue
|
|
561
|
-
.complete_status(
|
|
602
|
+
.complete_status(
|
|
603
|
+
call_uuid,
|
|
604
|
+
"completed",
|
|
605
|
+
&format!("{flushed} chunk(s)"),
|
|
606
|
+
Some(processing_trace_id),
|
|
607
|
+
)
|
|
562
608
|
.await;
|
|
563
609
|
info!(call_id = %work.call_id, chunks = flushed, "queued call completed");
|
|
564
610
|
}
|
|
565
611
|
Err(e) => {
|
|
566
|
-
//
|
|
567
|
-
//
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
612
|
+
// The formatted Python traceback is available, but the concrete
|
|
613
|
+
// exception type is not carried separately across this boundary.
|
|
614
|
+
stream_writer::write_error(
|
|
615
|
+
conn,
|
|
616
|
+
&stream_key,
|
|
617
|
+
seq,
|
|
618
|
+
"RemoteFunctionError",
|
|
619
|
+
e.details(),
|
|
620
|
+
None,
|
|
621
|
+
)
|
|
622
|
+
.await?;
|
|
623
|
+
let summary: String = e.to_string().chars().take(200).collect();
|
|
624
|
+
let _ = queue
|
|
625
|
+
.complete_status(call_uuid, "failed", &summary, Some(processing_trace_id))
|
|
626
|
+
.await;
|
|
627
|
+
error!(
|
|
628
|
+
call_id = %work.call_id,
|
|
629
|
+
exception.stacktrace = e.details(),
|
|
630
|
+
"queued call failed: {}",
|
|
631
|
+
e.details()
|
|
632
|
+
);
|
|
574
633
|
}
|
|
575
634
|
}
|
|
576
635
|
Ok(())
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
use std::net::SocketAddr;
|
|
2
2
|
use std::sync::Arc;
|
|
3
3
|
|
|
4
|
+
#[cfg(target_os = "linux")]
|
|
5
|
+
use chalk_metrics::PublishingMetricsPipeline;
|
|
4
6
|
use chalk_remote_call_proto::chalk::runtime::v1::async_remote_call_service_server::AsyncRemoteCallServiceServer;
|
|
5
7
|
use chalk_remote_call_proto::chalk::runtime::v1::remote_call_service_server::RemoteCallServiceServer;
|
|
6
8
|
use chalk_remote_call_proto::chalk::runtime::v1::FILE_DESCRIPTOR_SET;
|
|
@@ -14,11 +16,14 @@ use crate::service::RemoteCallServiceImpl;
|
|
|
14
16
|
|
|
15
17
|
const MAX_MESSAGE_SIZE: usize = 32 * 1024 * 1024; // 32 MB
|
|
16
18
|
|
|
19
|
+
const ENV_REMOTE_FUNCTION: &str = "CHALK_REMOTE_FUNCTION";
|
|
20
|
+
|
|
17
21
|
pub async fn run_server(
|
|
18
22
|
host: String,
|
|
19
23
|
port: u16,
|
|
20
24
|
python_handler: Arc<PythonHandler>,
|
|
21
25
|
coalescing_queue: Option<Arc<CoalescingQueue>>,
|
|
26
|
+
#[cfg(target_os = "linux")] pipeline: Option<Arc<PublishingMetricsPipeline>>,
|
|
22
27
|
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
|
23
28
|
// Parse host — ensure IPv6 addresses are bracketed for SocketAddr parsing
|
|
24
29
|
let addr_str = if host.contains(':') && !host.starts_with('[') {
|
|
@@ -57,7 +62,14 @@ pub async fn run_server(
|
|
|
57
62
|
// coalescing flag before `coalescing_queue` is moved into the streaming
|
|
58
63
|
// service below; the async path refuses enqueue when batching is on.
|
|
59
64
|
let batching_enabled = coalescing_queue.is_some();
|
|
60
|
-
|
|
65
|
+
// Checks if remote function env var is set or not
|
|
66
|
+
let is_remote_function: bool = std::env::var(ENV_REMOTE_FUNCTION)
|
|
67
|
+
.is_ok_and(|v| v.eq_ignore_ascii_case("true") || v == "1");
|
|
68
|
+
let async_svc = AsyncRemoteCallServiceImpl::new(
|
|
69
|
+
python_handler.clone(),
|
|
70
|
+
batching_enabled,
|
|
71
|
+
is_remote_function,
|
|
72
|
+
);
|
|
61
73
|
|
|
62
74
|
// Self-consume the Redis fn queue when this pod was provisioned with a
|
|
63
75
|
// Redis URL (gRPC-only otherwise). The self-consumer shares the gRPC
|
|
@@ -65,20 +77,22 @@ pub async fn run_server(
|
|
|
65
77
|
// inbound gRPC calls feed the same queue so they batch together against the
|
|
66
78
|
// single batch-size/duration the user configured. When batching is off the
|
|
67
79
|
// queue is `None` and the self-consumer dispatches per-call (streaming).
|
|
68
|
-
|
|
69
|
-
// Linux-only: the self-consumer + its metrics pipeline depend on chalk-queue
|
|
70
|
-
// / chalk_metrics, since these don't compile on macos and windows.
|
|
80
|
+
|
|
71
81
|
#[cfg(target_os = "linux")]
|
|
72
82
|
if let Some(cfg) = crate::self_consumer::SelfConsumerConfig::from_env() {
|
|
73
83
|
// Build the chalk_metrics pipeline once (mirrors the fnq-server/cc
|
|
74
84
|
// main init) so the self-consumer can emit function-call metrics.
|
|
75
|
-
let pipeline = crate::metrics::init_pipeline().await;
|
|
76
85
|
let consumer_handler = python_handler.clone();
|
|
86
|
+
let consumer_pipeline = pipeline.clone();
|
|
77
87
|
let consumer_coalescing = coalescing_queue.clone();
|
|
78
88
|
tokio::spawn(async move {
|
|
79
|
-
if let Err(e) =
|
|
80
|
-
|
|
81
|
-
|
|
89
|
+
if let Err(e) = crate::self_consumer::run(
|
|
90
|
+
cfg,
|
|
91
|
+
consumer_handler,
|
|
92
|
+
consumer_pipeline,
|
|
93
|
+
consumer_coalescing,
|
|
94
|
+
)
|
|
95
|
+
.await
|
|
82
96
|
{
|
|
83
97
|
tracing::error!(error = %e, "fnq self-consumer failed to start");
|
|
84
98
|
}
|
|
@@ -89,6 +103,8 @@ pub async fn run_server(
|
|
|
89
103
|
let svc = RemoteCallServiceImpl {
|
|
90
104
|
python_handler,
|
|
91
105
|
coalescing_queue,
|
|
106
|
+
#[cfg(target_os = "linux")]
|
|
107
|
+
metrics_pipeline: pipeline,
|
|
92
108
|
};
|
|
93
109
|
|
|
94
110
|
info!("Server starting on {}", addr);
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
use std::collections::HashMap;
|
|
2
2
|
use std::sync::Arc;
|
|
3
|
+
#[cfg(target_os = "linux")]
|
|
4
|
+
use std::time::Instant;
|
|
3
5
|
|
|
6
|
+
#[cfg(target_os = "linux")]
|
|
7
|
+
use chalk_metrics::metrics::tags::Mode;
|
|
8
|
+
#[cfg(target_os = "linux")]
|
|
9
|
+
use chalk_metrics::PublishingMetricsPipeline;
|
|
4
10
|
use chalk_remote_call_proto::chalk::runtime::v1::remote_call_service_server::RemoteCallService;
|
|
5
11
|
use chalk_remote_call_proto::chalk::runtime::v1::{CallFunctionRequest, CallFunctionResponse};
|
|
6
12
|
use tokio_stream::wrappers::ReceiverStream;
|
|
@@ -18,6 +24,9 @@ pub struct RemoteCallServiceImpl {
|
|
|
18
24
|
/// Optional coalescing queue. When `Some`, incoming calls are buffered and
|
|
19
25
|
/// dispatched as a single batched handler invocation.
|
|
20
26
|
pub coalescing_queue: Option<Arc<CoalescingQueue>>,
|
|
27
|
+
// Linux-gated metrics pipeline
|
|
28
|
+
#[cfg(target_os = "linux")]
|
|
29
|
+
pub metrics_pipeline: Option<Arc<PublishingMetricsPipeline>>,
|
|
21
30
|
}
|
|
22
31
|
|
|
23
32
|
#[tonic::async_trait]
|
|
@@ -90,21 +99,51 @@ impl RemoteCallService for RemoteCallServiceImpl {
|
|
|
90
99
|
|
|
91
100
|
// Single-request path.
|
|
92
101
|
let handler = self.python_handler.clone();
|
|
102
|
+
#[cfg(target_os = "linux")]
|
|
103
|
+
let metrics_pipeline = self.metrics_pipeline.clone();
|
|
93
104
|
tokio::spawn(async move {
|
|
105
|
+
#[cfg(target_os = "linux")]
|
|
106
|
+
let started = Instant::now();
|
|
107
|
+
|
|
94
108
|
let error_tx = tx.clone();
|
|
95
109
|
match handler
|
|
96
|
-
.call_streaming(all_bytes, function_name, metadata, peer, tx)
|
|
110
|
+
.call_streaming(all_bytes, function_name.clone(), metadata, peer, tx)
|
|
97
111
|
.await
|
|
98
112
|
{
|
|
99
|
-
Ok(()) => {
|
|
113
|
+
Ok(()) => {
|
|
114
|
+
#[cfg(target_os = "linux")]
|
|
115
|
+
if let Some(p) = &metrics_pipeline {
|
|
116
|
+
crate::metrics::record_completed_call(
|
|
117
|
+
p,
|
|
118
|
+
&function_name,
|
|
119
|
+
/* success = */ true,
|
|
120
|
+
started.elapsed(),
|
|
121
|
+
Mode::Sync,
|
|
122
|
+
)
|
|
123
|
+
}
|
|
124
|
+
}
|
|
100
125
|
Err(e) => {
|
|
126
|
+
#[cfg(target_os = "linux")]
|
|
127
|
+
if let Some(p) = &metrics_pipeline {
|
|
128
|
+
crate::metrics::record_completed_call(
|
|
129
|
+
p,
|
|
130
|
+
&function_name,
|
|
131
|
+
/* success = */ false,
|
|
132
|
+
started.elapsed(),
|
|
133
|
+
Mode::Sync,
|
|
134
|
+
)
|
|
135
|
+
}
|
|
101
136
|
if error_tx.is_closed() {
|
|
102
137
|
// The only reason blocking_send fails is a dropped
|
|
103
138
|
// receiver — the client went away mid-stream. Not a
|
|
104
139
|
// handler failure; nothing to send.
|
|
105
140
|
debug!("client disconnected during streaming response");
|
|
106
141
|
} else {
|
|
107
|
-
error!(
|
|
142
|
+
error!(
|
|
143
|
+
exception.stacktrace = e.details(),
|
|
144
|
+
"Python handler error: {}",
|
|
145
|
+
e.details()
|
|
146
|
+
);
|
|
108
147
|
let _ = error_tx
|
|
109
148
|
.send(Err(Status::unknown(format!(
|
|
110
149
|
"Exception raised during handler execution: {e}"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "1.8.3"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "1.8.1"
|
|
File without changes
|
|
File without changes
|
{chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk-remote-call-rs/Cargo.toml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk_remote_call/__init__.py
RENAMED
|
File without changes
|
{chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk_remote_call/__main__.py
RENAMED
|
File without changes
|
{chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk_remote_call/_gen/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk_remote_call/_native.pyi
RENAMED
|
File without changes
|
{chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk_remote_call/arrow_utils.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk_remote_call/server.py
RENAMED
|
File without changes
|
{chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk_remote_call/servicer.py
RENAMED
|
File without changes
|
{chalk_remote_call_python-1.8.1 → chalk_remote_call_python-1.8.3}/chalk_remote_call/tracing.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|