chalk-remote-call-python 1.8.0__tar.gz → 1.8.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.
Files changed (66) hide show
  1. {chalk_remote_call_python-1.8.0 → chalk_remote_call_python-1.8.2}/PKG-INFO +1 -1
  2. {chalk_remote_call_python-1.8.0 → chalk_remote_call_python-1.8.2}/chalk-remote-call-rs/Cargo.lock +19 -19
  3. {chalk_remote_call_python-1.8.0 → chalk_remote_call_python-1.8.2}/chalk-remote-call-rs/chalk-remote-call-server/Cargo.toml +3 -3
  4. {chalk_remote_call_python-1.8.0 → chalk_remote_call_python-1.8.2}/chalk-remote-call-rs/chalk-remote-call-server/src/async_service.rs +15 -2
  5. {chalk_remote_call_python-1.8.0 → chalk_remote_call_python-1.8.2}/chalk-remote-call-rs/chalk-remote-call-server/src/coalesce.rs +60 -3
  6. {chalk_remote_call_python-1.8.0 → chalk_remote_call_python-1.8.2}/chalk-remote-call-rs/chalk-remote-call-server/src/lib.rs +29 -16
  7. {chalk_remote_call_python-1.8.0 → chalk_remote_call_python-1.8.2}/chalk-remote-call-rs/chalk-remote-call-server/src/metrics.rs +33 -20
  8. {chalk_remote_call_python-1.8.0 → chalk_remote_call_python-1.8.2}/chalk-remote-call-rs/chalk-remote-call-server/src/self_consumer.rs +72 -23
  9. {chalk_remote_call_python-1.8.0 → chalk_remote_call_python-1.8.2}/chalk-remote-call-rs/chalk-remote-call-server/src/server.rs +25 -9
  10. {chalk_remote_call_python-1.8.0 → chalk_remote_call_python-1.8.2}/chalk-remote-call-rs/chalk-remote-call-server/src/service.rs +37 -2
  11. chalk_remote_call_python-1.8.2/chalk_remote_call/_version.py +1 -0
  12. {chalk_remote_call_python-1.8.0 → chalk_remote_call_python-1.8.2}/chalk_remote_call_python.egg-info/PKG-INFO +1 -1
  13. chalk_remote_call_python-1.8.0/chalk_remote_call/_version.py +0 -1
  14. {chalk_remote_call_python-1.8.0 → chalk_remote_call_python-1.8.2}/MANIFEST.in +0 -0
  15. {chalk_remote_call_python-1.8.0 → chalk_remote_call_python-1.8.2}/README.md +0 -0
  16. {chalk_remote_call_python-1.8.0 → chalk_remote_call_python-1.8.2}/chalk-remote-call-rs/Cargo.toml +0 -0
  17. {chalk_remote_call_python-1.8.0 → chalk_remote_call_python-1.8.2}/chalk-remote-call-rs/chalk-remote-call-proto/Cargo.toml +0 -0
  18. {chalk_remote_call_python-1.8.0 → chalk_remote_call_python-1.8.2}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/chalk.auth.v1.rs +0 -0
  19. {chalk_remote_call_python-1.8.0 → chalk_remote_call_python-1.8.2}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/chalk.common.v1.rs +0 -0
  20. {chalk_remote_call_python-1.8.0 → chalk_remote_call_python-1.8.2}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/chalk.runtime.v1.rs +0 -0
  21. {chalk_remote_call_python-1.8.0 → chalk_remote_call_python-1.8.2}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/chalk.runtime.v1.tonic.rs +0 -0
  22. {chalk_remote_call_python-1.8.0 → chalk_remote_call_python-1.8.2}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/chalk.utils.v1.rs +0 -0
  23. {chalk_remote_call_python-1.8.0 → chalk_remote_call_python-1.8.2}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/descriptor.bin +0 -0
  24. {chalk_remote_call_python-1.8.0 → chalk_remote_call_python-1.8.2}/chalk-remote-call-rs/chalk-remote-call-proto/src/lib.rs +0 -0
  25. {chalk_remote_call_python-1.8.0 → chalk_remote_call_python-1.8.2}/chalk-remote-call-rs/chalk-remote-call-server/src/python_bridge.rs +0 -0
  26. {chalk_remote_call_python-1.8.0 → chalk_remote_call_python-1.8.2}/chalk-remote-call-rs/rust-toolchain.toml +0 -0
  27. {chalk_remote_call_python-1.8.0 → chalk_remote_call_python-1.8.2}/chalk_remote_call/__init__.py +0 -0
  28. {chalk_remote_call_python-1.8.0 → chalk_remote_call_python-1.8.2}/chalk_remote_call/__main__.py +0 -0
  29. {chalk_remote_call_python-1.8.0 → chalk_remote_call_python-1.8.2}/chalk_remote_call/_gen/__init__.py +0 -0
  30. {chalk_remote_call_python-1.8.0 → chalk_remote_call_python-1.8.2}/chalk_remote_call/_gen/chalk/__init__.py +0 -0
  31. {chalk_remote_call_python-1.8.0 → chalk_remote_call_python-1.8.2}/chalk_remote_call/_gen/chalk/auth/__init__.py +0 -0
  32. {chalk_remote_call_python-1.8.0 → chalk_remote_call_python-1.8.2}/chalk_remote_call/_gen/chalk/auth/v1/__init__.py +0 -0
  33. {chalk_remote_call_python-1.8.0 → chalk_remote_call_python-1.8.2}/chalk_remote_call/_gen/chalk/auth/v1/permissions_pb2.py +0 -0
  34. {chalk_remote_call_python-1.8.0 → chalk_remote_call_python-1.8.2}/chalk_remote_call/_gen/chalk/auth/v1/permissions_pb2_grpc.py +0 -0
  35. {chalk_remote_call_python-1.8.0 → chalk_remote_call_python-1.8.2}/chalk_remote_call/_gen/chalk/common/__init__.py +0 -0
  36. {chalk_remote_call_python-1.8.0 → chalk_remote_call_python-1.8.2}/chalk_remote_call/_gen/chalk/common/v1/__init__.py +0 -0
  37. {chalk_remote_call_python-1.8.0 → chalk_remote_call_python-1.8.2}/chalk_remote_call/_gen/chalk/common/v1/chalk_error_pb2.py +0 -0
  38. {chalk_remote_call_python-1.8.0 → chalk_remote_call_python-1.8.2}/chalk_remote_call/_gen/chalk/common/v1/chalk_error_pb2_grpc.py +0 -0
  39. {chalk_remote_call_python-1.8.0 → chalk_remote_call_python-1.8.2}/chalk_remote_call/_gen/chalk/runtime/__init__.py +0 -0
  40. {chalk_remote_call_python-1.8.0 → chalk_remote_call_python-1.8.2}/chalk_remote_call/_gen/chalk/runtime/v1/__init__.py +0 -0
  41. {chalk_remote_call_python-1.8.0 → chalk_remote_call_python-1.8.2}/chalk_remote_call/_gen/chalk/runtime/v1/remote_python_call_pb2.py +0 -0
  42. {chalk_remote_call_python-1.8.0 → chalk_remote_call_python-1.8.2}/chalk_remote_call/_gen/chalk/runtime/v1/remote_python_call_pb2_grpc.py +0 -0
  43. {chalk_remote_call_python-1.8.0 → chalk_remote_call_python-1.8.2}/chalk_remote_call/_gen/chalk/utils/__init__.py +0 -0
  44. {chalk_remote_call_python-1.8.0 → chalk_remote_call_python-1.8.2}/chalk_remote_call/_gen/chalk/utils/v1/__init__.py +0 -0
  45. {chalk_remote_call_python-1.8.0 → chalk_remote_call_python-1.8.2}/chalk_remote_call/_gen/chalk/utils/v1/encoding_pb2.py +0 -0
  46. {chalk_remote_call_python-1.8.0 → chalk_remote_call_python-1.8.2}/chalk_remote_call/_gen/chalk/utils/v1/encoding_pb2_grpc.py +0 -0
  47. {chalk_remote_call_python-1.8.0 → chalk_remote_call_python-1.8.2}/chalk_remote_call/_gen/chalk/utils/v1/field_change_pb2.py +0 -0
  48. {chalk_remote_call_python-1.8.0 → chalk_remote_call_python-1.8.2}/chalk_remote_call/_gen/chalk/utils/v1/field_change_pb2_grpc.py +0 -0
  49. {chalk_remote_call_python-1.8.0 → chalk_remote_call_python-1.8.2}/chalk_remote_call/_gen/chalk/utils/v1/sensitive_pb2.py +0 -0
  50. {chalk_remote_call_python-1.8.0 → chalk_remote_call_python-1.8.2}/chalk_remote_call/_gen/chalk/utils/v1/sensitive_pb2_grpc.py +0 -0
  51. {chalk_remote_call_python-1.8.0 → chalk_remote_call_python-1.8.2}/chalk_remote_call/_native.pyi +0 -0
  52. {chalk_remote_call_python-1.8.0 → chalk_remote_call_python-1.8.2}/chalk_remote_call/arrow_utils.py +0 -0
  53. {chalk_remote_call_python-1.8.0 → chalk_remote_call_python-1.8.2}/chalk_remote_call/cli.py +0 -0
  54. {chalk_remote_call_python-1.8.0 → chalk_remote_call_python-1.8.2}/chalk_remote_call/handler_loader.py +0 -0
  55. {chalk_remote_call_python-1.8.0 → chalk_remote_call_python-1.8.2}/chalk_remote_call/input_transform.py +0 -0
  56. {chalk_remote_call_python-1.8.0 → chalk_remote_call_python-1.8.2}/chalk_remote_call/server.py +0 -0
  57. {chalk_remote_call_python-1.8.0 → chalk_remote_call_python-1.8.2}/chalk_remote_call/servicer.py +0 -0
  58. {chalk_remote_call_python-1.8.0 → chalk_remote_call_python-1.8.2}/chalk_remote_call/tracing.py +0 -0
  59. {chalk_remote_call_python-1.8.0 → chalk_remote_call_python-1.8.2}/chalk_remote_call_python.egg-info/SOURCES.txt +0 -0
  60. {chalk_remote_call_python-1.8.0 → chalk_remote_call_python-1.8.2}/chalk_remote_call_python.egg-info/dependency_links.txt +0 -0
  61. {chalk_remote_call_python-1.8.0 → chalk_remote_call_python-1.8.2}/chalk_remote_call_python.egg-info/entry_points.txt +0 -0
  62. {chalk_remote_call_python-1.8.0 → chalk_remote_call_python-1.8.2}/chalk_remote_call_python.egg-info/requires.txt +0 -0
  63. {chalk_remote_call_python-1.8.0 → chalk_remote_call_python-1.8.2}/chalk_remote_call_python.egg-info/top_level.txt +0 -0
  64. {chalk_remote_call_python-1.8.0 → chalk_remote_call_python-1.8.2}/pyproject.toml +0 -0
  65. {chalk_remote_call_python-1.8.0 → chalk_remote_call_python-1.8.2}/setup.cfg +0 -0
  66. {chalk_remote_call_python-1.8.0 → chalk_remote_call_python-1.8.2}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: chalk-remote-call-python
3
- Version: 1.8.0
3
+ Version: 1.8.2
4
4
  Summary: Chalk remote call Python runtime interface client
5
5
  Author: Chalk AI, Inc.
6
6
  Project-URL: Homepage, https://chalk.ai
@@ -483,8 +483,8 @@ dependencies = [
483
483
 
484
484
  [[package]]
485
485
  name = "chalk-core"
486
- version = "0.1.0"
487
- source = "git+https://github.com/chalk-ai/chalk-private.git?rev=44c26fd738fabd7e2223854618d1f9ccb1b08a71#44c26fd738fabd7e2223854618d1f9ccb1b08a71"
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.1.0"
502
- source = "git+https://github.com/chalk-ai/chalk-private.git?rev=44c26fd738fabd7e2223854618d1f9ccb1b08a71#44c26fd738fabd7e2223854618d1f9ccb1b08a71"
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.1.0"
514
- source = "git+https://github.com/chalk-ai/chalk-private.git?rev=44c26fd738fabd7e2223854618d1f9ccb1b08a71#44c26fd738fabd7e2223854618d1f9ccb1b08a71"
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.1.0"
563
- source = "git+https://github.com/chalk-ai/chalk-private.git?rev=44c26fd738fabd7e2223854618d1f9ccb1b08a71#44c26fd738fabd7e2223854618d1f9ccb1b08a71"
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.1.0"
573
- source = "git+https://github.com/chalk-ai/chalk-private.git?rev=44c26fd738fabd7e2223854618d1f9ccb1b08a71#44c26fd738fabd7e2223854618d1f9ccb1b08a71"
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.1.0"
607
- source = "git+https://github.com/chalk-ai/chalk-private.git?rev=44c26fd738fabd7e2223854618d1f9ccb1b08a71#44c26fd738fabd7e2223854618d1f9ccb1b08a71"
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.52.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.5.10",
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=44c26fd738fabd7e2223854618d1f9ccb1b08a71#44c26fd738fabd7e2223854618d1f9ccb1b08a71"
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.52.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.52.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.3.4",
3131
+ "getrandom 0.4.2",
3132
3132
  "once_cell",
3133
3133
  "rustix 1.1.4",
3134
- "windows-sys 0.52.0",
3134
+ "windows-sys 0.61.2",
3135
3135
  ]
3136
3136
 
3137
3137
  [[package]]
@@ -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 = "44c26fd738fabd7e2223854618d1f9ccb1b08a71" }
40
- chalk_metrics = { git = "https://github.com/chalk-ai/chalk-private.git", rev = "44c26fd738fabd7e2223854618d1f9ccb1b08a71" }
41
- chalk_statsd = { git = "https://github.com/chalk-ai/chalk-private.git", rev = "44c26fd738fabd7e2223854618d1f9ccb1b08a71" }
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(python_handler: Arc<PythonHandler>, batching_enabled: bool) -> Self {
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) {
@@ -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(handler: Arc<PythonHandler>, max_size: usize, max_duration_ms: u64) -> Arc<Self> {
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(handler, items).await;
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(handler: Arc<PythonHandler>, items: Vec<BufferedCall>) {
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(),
@@ -132,6 +179,16 @@ impl CoalescingQueue {
132
179
  error!("Coalesced handler error: {}", e);
133
180
  // IPC decode errors are already caught by the service's
134
181
  // pre-validation; anything reaching here is a handler error.
182
+ #[cfg(target_os = "linux")]
183
+ if let Some(p) = &pipeline {
184
+ crate::metrics::record_completed_call(
185
+ p,
186
+ &function_name,
187
+ /* success = */ false,
188
+ started.elapsed(),
189
+ chalk_metrics::metrics::tags::Mode::Sync,
190
+ );
191
+ }
135
192
  let status =
136
193
  Status::unknown(format!("Exception raised during handler execution: {e}"));
137
194
  for tx in response_txs {
@@ -53,16 +53,6 @@ fn start_server(
53
53
 
54
54
  let python_handler = Arc::new(PythonHandler::new(handler, process_fn, arg_names));
55
55
 
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
56
  // Release the GIL while the Rust server runs
67
57
  py.detach(|| {
68
58
  let rt = tokio::runtime::Builder::new_multi_thread()
@@ -75,12 +65,35 @@ fn start_server(
75
65
  ))
76
66
  })?;
77
67
 
78
- rt.block_on(server::run_server(
79
- host,
80
- port,
81
- python_handler,
82
- coalescing_queue,
83
- ))
68
+ rt.block_on(async {
69
+ // Linux-only: the self-consumer + its metrics pipeline depend on
70
+ // chalk-queue / chalk_metrics, since these don't compile on macos
71
+ // and windows. Built inside the runtime since it needs to `.await`.
72
+ #[cfg(target_os = "linux")]
73
+ let pipeline = crate::metrics::init_pipeline().await;
74
+
75
+ let coalescing_queue = if max_batching_size > 0 {
76
+ Some(CoalescingQueue::new(
77
+ Arc::clone(&python_handler),
78
+ max_batching_size,
79
+ max_buffer_duration_ms,
80
+ #[cfg(target_os = "linux")]
81
+ pipeline.clone(),
82
+ ))
83
+ } else {
84
+ None
85
+ };
86
+
87
+ server::run_server(
88
+ host,
89
+ port,
90
+ python_handler,
91
+ coalescing_queue,
92
+ #[cfg(target_os = "linux")]
93
+ pipeline,
94
+ )
95
+ .await
96
+ })
84
97
  .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(format!("Server error: {e}")))
85
98
  })
86
99
  }
@@ -1,6 +1,6 @@
1
1
  //! Metrics emission for the self-consuming shim, mirroring the catalog
2
2
  //! consumer / fnq-server: build a `chalk_metrics` publishing pipeline at startup
3
- //! and emit `FunctionCallDequeued` per executed call.
3
+ //! and emit per-call dequeue + processing-latency metrics.
4
4
  //!
5
5
  //! The External Function "Function Calls Dequeued" chart reads TimescaleDB
6
6
  //! `metrics1`, which is fed only by the chalk_metrics PubSub bus (statsd goes to
@@ -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<dyn MetricsPipeline>> {
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(
@@ -42,12 +42,10 @@ pub async fn init_pipeline() -> Option<Arc<dyn MetricsPipeline>> {
42
42
 
43
43
  let publishers: Vec<Arc<dyn BusPublisher>> = build_bus_publisher().await.into_iter().collect();
44
44
  if publishers.is_empty() {
45
- warn!(
46
- "no metrics bus publisher configured; FunctionCallDequeued will not reach the dashboard"
47
- );
45
+ warn!("no metrics bus publisher configured; function call metrics will not reach the dashboard");
48
46
  }
49
47
 
50
- let metrics_pipeline: Arc<dyn MetricsPipeline> =
48
+ let metrics_pipeline: Arc<PublishingMetricsPipeline> =
51
49
  Arc::new(PublishingMetricsPipeline::new_with_env_var_defaults(
52
50
  environment_id,
53
51
  String::new(),
@@ -106,24 +104,39 @@ async fn build_bus_publisher() -> Option<Arc<dyn BusPublisher>> {
106
104
  }
107
105
  }
108
106
 
109
- /// Emit one `FunctionCallDequeued` count for a completed call, tagged with the
110
- /// function name and success/failure — mirroring the catalog consumer's emission
111
- /// (minus the scaling-group revision tag, which the shim does not carry; the
112
- /// dashboard filters by function name only).
113
- pub fn record_dequeue(pipeline: &Arc<dyn MetricsPipeline>, function_name: &str, success: bool) {
107
+ /// Emit completed-call metrics tagged with function name and success/failure.
108
+ ///
109
+ /// `FunctionCallProcessingLatency` is defined as a sketch in metrics.cue, which
110
+ /// is emitted through the histogram API in chalk_metrics. The shim does not
111
+ /// carry the scaling-group revision tag used by the legacy catalog consumer;
112
+ /// the dashboard filters by function name.
113
+ pub fn record_completed_call(
114
+ pipeline: &Arc<PublishingMetricsPipeline>,
115
+ function_name: &str,
116
+ success: bool,
117
+ elapsed: Duration,
118
+ mode: Mode,
119
+ ) {
114
120
  let status = if success {
115
121
  MetricStatus::Success
116
122
  } else {
117
123
  MetricStatus::Failure
118
124
  };
119
- if let Err(e) = pipeline.count(
120
- WellKnownMetricName::FunctionCallDequeued,
121
- 1.0,
122
- vec![
123
- TagValue::FunctionName(FunctionName(function_name.to_string())),
124
- TagValue::Status(status),
125
- ],
126
- ) {
125
+ let tags = vec![
126
+ TagValue::FunctionName(FunctionName(function_name.to_string())),
127
+ TagValue::Status(status.clone()),
128
+ TagValue::Mode(mode.clone()),
129
+ ];
130
+
131
+ if let Err(e) = pipeline.count(WellKnownMetricName::FunctionCallDequeued, 1.0, tags.clone()) {
127
132
  warn!(error = %e, function = %function_name, "failed to emit FunctionCallDequeued metric");
128
133
  }
134
+
135
+ if let Err(e) = pipeline.histogram(
136
+ WellKnownMetricName::FunctionCallProcessingLatency,
137
+ elapsed.as_secs_f64() * 1000.0,
138
+ tags,
139
+ ) {
140
+ warn!(error = %e, function = %function_name, "failed to emit FunctionCallProcessingLatency metric");
141
+ }
129
142
  }
@@ -28,9 +28,10 @@
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
- use std::time::Duration;
34
+ use std::time::{Duration, Instant};
34
35
 
35
36
  use chalk_queue::semaphore::renew_many;
36
37
  use chalk_queue::stream_writer;
@@ -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::MetricsPipeline;
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<dyn MetricsPipeline>>,
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<dyn MetricsPipeline>>,
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<dyn MetricsPipeline>>,
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,15 @@ async fn coalesced_execute_and_write(
397
399
  queue: &FunctionQueue,
398
400
  conn: &mut ConnectionManager,
399
401
  work: QueuedCall,
400
- metrics_pipeline: Option<&Arc<dyn MetricsPipeline>>,
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);
410
+ let started = Instant::now();
407
411
 
408
412
  let _ = queue.update_status(call_uuid, "running").await;
409
413
  info!(call_id = %work.call_id, function = %function_name, "running coalesced queued call");
@@ -425,11 +429,17 @@ async fn coalesced_execute_and_write(
425
429
 
426
430
  let response = rx.recv().await;
427
431
 
428
- // Record the dequeue once the batch has produced this caller's result,
429
- // before the stream writes below — parity with the streaming path.
432
+ // Record metrics once the batch has produced this caller's result, before
433
+ // stream writes below — parity with the streaming path.
430
434
  let success = matches!(response, Some(Ok(_)));
431
435
  if let Some(p) = metrics_pipeline {
432
- crate::metrics::record_dequeue(p, &function_name, success);
436
+ crate::metrics::record_completed_call(
437
+ p,
438
+ &function_name,
439
+ success,
440
+ started.elapsed(),
441
+ Mode::Async,
442
+ );
433
443
  }
434
444
 
435
445
  match response {
@@ -437,7 +447,12 @@ async fn coalesced_execute_and_write(
437
447
  stream_writer::write_chunk(conn, &stream_key, 1, &resp.feather_stream).await?;
438
448
  stream_writer::write_end(conn, &stream_key, 2).await?;
439
449
  let _ = queue
440
- .complete_status(call_uuid, "completed", "1 chunk(s)")
450
+ .complete_status(
451
+ call_uuid,
452
+ "completed",
453
+ "1 chunk(s)",
454
+ Some(processing_trace_id),
455
+ )
441
456
  .await;
442
457
  info!(call_id = %work.call_id, "coalesced queued call completed");
443
458
  }
@@ -454,7 +469,9 @@ async fn coalesced_execute_and_write(
454
469
  )
455
470
  .await?;
456
471
  let summary: String = status.message().chars().take(200).collect();
457
- let _ = queue.complete_status(call_uuid, "failed", &summary).await;
472
+ let _ = queue
473
+ .complete_status(call_uuid, "failed", &summary, Some(processing_trace_id))
474
+ .await;
458
475
  error!(call_id = %work.call_id, error = %status, "coalesced queued call failed");
459
476
  }
460
477
  None => {
@@ -464,13 +481,31 @@ async fn coalesced_execute_and_write(
464
481
  let msg = "coalesced dispatch produced no response";
465
482
  stream_writer::write_error(conn, &stream_key, 1, "RemoteFunctionError", msg, None)
466
483
  .await?;
467
- let _ = queue.complete_status(call_uuid, "failed", msg).await;
484
+ let _ = queue
485
+ .complete_status(call_uuid, "failed", msg, Some(processing_trace_id))
486
+ .await;
468
487
  error!(call_id = %work.call_id, "{msg}");
469
488
  }
470
489
  }
471
490
  Ok(())
472
491
  }
473
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
+
474
509
  /// Decode the payload, invoke the handler, and write the outcome (chunks then
475
510
  /// `end`, or `error`) to the call's result stream, updating meta status along
476
511
  /// the way.
@@ -480,13 +515,15 @@ async fn execute_and_write(
480
515
  conn: &mut ConnectionManager,
481
516
  work: QueuedCall,
482
517
  flush_timeout: Duration,
483
- metrics_pipeline: Option<&Arc<dyn MetricsPipeline>>,
518
+ metrics_pipeline: Option<&Arc<PublishingMetricsPipeline>>,
484
519
  ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
485
520
  let call_uuid = Uuid::parse_str(&work.call_id)?;
486
521
  let stream_key = queue.stream_key_for(call_uuid);
487
522
  let ipc_bytes = work.call.feather_bytes()?;
488
523
  let function_name = work.call.name.clone();
489
- 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);
526
+ let started = Instant::now();
490
527
 
491
528
  let _ = queue.update_status(call_uuid, "running").await;
492
529
  info!(call_id = %work.call_id, function = %function_name, "running queued call");
@@ -501,7 +538,7 @@ async fn execute_and_write(
501
538
  let buffer = ChunkBuffer::new().into_shared();
502
539
  let handler_fut = handler.call_into_buffer(
503
540
  ipc_bytes,
504
- function_name,
541
+ function_name.clone(),
505
542
  metadata,
506
543
  PEER.to_string(),
507
544
  buffer.clone(),
@@ -537,12 +574,17 @@ async fn execute_and_write(
537
574
  }
538
575
  };
539
576
 
540
- // Emit FunctionCallDequeued as soon as execution finishes before the
541
- // result-stream writes below so the dequeue metric is recorded for every
542
- // executed call regardless of any downstream Redis write failure. Mirrors
543
- // the catalog consumer's per-call emission.
577
+ // Emit metrics as soon as execution finishes, before result-stream writes,
578
+ // so every executed call is recorded regardless of downstream Redis write
579
+ // failure. Mirrors the catalog consumer's per-call emission.
544
580
  if let Some(p) = metrics_pipeline {
545
- crate::metrics::record_dequeue(p, &work.call.name, result.is_ok());
581
+ crate::metrics::record_completed_call(
582
+ p,
583
+ &function_name,
584
+ result.is_ok(),
585
+ started.elapsed(),
586
+ Mode::Async,
587
+ );
546
588
  }
547
589
 
548
590
  // Flush whatever the handler produced after the loop exits (on the fast path
@@ -557,7 +599,12 @@ async fn execute_and_write(
557
599
  Ok(()) => {
558
600
  stream_writer::write_end(conn, &stream_key, seq).await?;
559
601
  let _ = queue
560
- .complete_status(call_uuid, "completed", &format!("{flushed} chunk(s)"))
602
+ .complete_status(
603
+ call_uuid,
604
+ "completed",
605
+ &format!("{flushed} chunk(s)"),
606
+ Some(processing_trace_id),
607
+ )
561
608
  .await;
562
609
  info!(call_id = %work.call_id, chunks = flushed, "queued call completed");
563
610
  }
@@ -568,7 +615,9 @@ async fn execute_and_write(
568
615
  stream_writer::write_error(conn, &stream_key, seq, "RemoteFunctionError", &e.0, None)
569
616
  .await?;
570
617
  let summary: String = e.0.chars().take(200).collect();
571
- let _ = queue.complete_status(call_uuid, "failed", &summary).await;
618
+ let _ = queue
619
+ .complete_status(call_uuid, "failed", &summary, Some(processing_trace_id))
620
+ .await;
572
621
  error!(call_id = %work.call_id, error = %e, "queued call failed");
573
622
  }
574
623
  }
@@ -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
- let async_svc = AsyncRemoteCallServiceImpl::new(python_handler.clone(), batching_enabled);
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
- // main init) so the self-consumer can emit FunctionCallDequeued.
75
- let pipeline = crate::metrics::init_pipeline().await;
84
+ // main init) so the self-consumer can emit function-call metrics.
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
- crate::self_consumer::run(cfg, consumer_handler, pipeline, consumer_coalescing)
81
- .await
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,14 +99,40 @@ 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
@@ -0,0 +1 @@
1
+ __version__ = "1.8.2"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: chalk-remote-call-python
3
- Version: 1.8.0
3
+ Version: 1.8.2
4
4
  Summary: Chalk remote call Python runtime interface client
5
5
  Author: Chalk AI, Inc.
6
6
  Project-URL: Homepage, https://chalk.ai
@@ -1 +0,0 @@
1
- __version__ = "1.8.0"