chalk-remote-call-python 1.10.0__tar.gz → 1.10.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.
- {chalk_remote_call_python-1.10.0/chalk_remote_call_python.egg-info → chalk_remote_call_python-1.10.2}/PKG-INFO +1 -1
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk-remote-call-rs/Cargo.lock +1 -1
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk-remote-call-rs/chalk-remote-call-server/Cargo.toml +1 -1
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk-remote-call-rs/chalk-remote-call-server/src/self_consumer.rs +233 -30
- chalk_remote_call_python-1.10.2/chalk_remote_call/_version.py +1 -0
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2/chalk_remote_call_python.egg-info}/PKG-INFO +1 -1
- chalk_remote_call_python-1.10.0/chalk_remote_call/_version.py +0 -1
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/MANIFEST.in +0 -0
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/README.md +0 -0
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk-remote-call-rs/Cargo.toml +0 -0
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk-remote-call-rs/chalk-remote-call-proto/Cargo.toml +0 -0
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/chalk.auth.v1.rs +0 -0
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/chalk.common.v1.rs +0 -0
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/chalk.runtime.v1.rs +0 -0
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/chalk.runtime.v1.tonic.rs +0 -0
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/chalk.utils.v1.rs +0 -0
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/descriptor.bin +0 -0
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk-remote-call-rs/chalk-remote-call-proto/src/lib.rs +0 -0
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk-remote-call-rs/chalk-remote-call-server/src/async_service.rs +0 -0
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk-remote-call-rs/chalk-remote-call-server/src/coalesce.rs +0 -0
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk-remote-call-rs/chalk-remote-call-server/src/lib.rs +0 -0
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk-remote-call-rs/chalk-remote-call-server/src/metrics.rs +0 -0
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk-remote-call-rs/chalk-remote-call-server/src/python_bridge.rs +0 -0
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk-remote-call-rs/chalk-remote-call-server/src/server.rs +0 -0
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk-remote-call-rs/chalk-remote-call-server/src/service.rs +0 -0
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk-remote-call-rs/rust-toolchain.toml +0 -0
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk_remote_call/__init__.py +0 -0
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk_remote_call/__main__.py +0 -0
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk_remote_call/_gen/__init__.py +0 -0
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk_remote_call/_gen/chalk/__init__.py +0 -0
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk_remote_call/_gen/chalk/auth/__init__.py +0 -0
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk_remote_call/_gen/chalk/auth/v1/__init__.py +0 -0
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk_remote_call/_gen/chalk/auth/v1/permissions_pb2.py +0 -0
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk_remote_call/_gen/chalk/auth/v1/permissions_pb2_grpc.py +0 -0
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk_remote_call/_gen/chalk/common/__init__.py +0 -0
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk_remote_call/_gen/chalk/common/v1/__init__.py +0 -0
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk_remote_call/_gen/chalk/common/v1/chalk_error_pb2.py +0 -0
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk_remote_call/_gen/chalk/common/v1/chalk_error_pb2_grpc.py +0 -0
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk_remote_call/_gen/chalk/runtime/__init__.py +0 -0
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk_remote_call/_gen/chalk/runtime/v1/__init__.py +0 -0
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk_remote_call/_gen/chalk/runtime/v1/remote_python_call_pb2.py +0 -0
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk_remote_call/_gen/chalk/runtime/v1/remote_python_call_pb2_grpc.py +0 -0
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk_remote_call/_gen/chalk/utils/__init__.py +0 -0
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk_remote_call/_gen/chalk/utils/v1/__init__.py +0 -0
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk_remote_call/_gen/chalk/utils/v1/encoding_pb2.py +0 -0
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk_remote_call/_gen/chalk/utils/v1/encoding_pb2_grpc.py +0 -0
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk_remote_call/_gen/chalk/utils/v1/field_change_pb2.py +0 -0
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk_remote_call/_gen/chalk/utils/v1/field_change_pb2_grpc.py +0 -0
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk_remote_call/_gen/chalk/utils/v1/sensitive_pb2.py +0 -0
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk_remote_call/_gen/chalk/utils/v1/sensitive_pb2_grpc.py +0 -0
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk_remote_call/_native.pyi +0 -0
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk_remote_call/arrow_utils.py +0 -0
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk_remote_call/cli.py +0 -0
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk_remote_call/errors.py +0 -0
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk_remote_call/handler_loader.py +0 -0
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk_remote_call/input_transform.py +0 -0
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk_remote_call/logging.py +0 -0
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk_remote_call/server.py +0 -0
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk_remote_call/servicer.py +0 -0
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk_remote_call/tracing.py +0 -0
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk_remote_call_python.egg-info/SOURCES.txt +0 -0
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk_remote_call_python.egg-info/dependency_links.txt +0 -0
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk_remote_call_python.egg-info/entry_points.txt +0 -0
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk_remote_call_python.egg-info/requires.txt +0 -0
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk_remote_call_python.egg-info/top_level.txt +0 -0
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/pyproject.toml +0 -0
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/setup.cfg +0 -0
- {chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/setup.py +0 -0
{chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk-remote-call-rs/Cargo.lock
RENAMED
|
@@ -511,7 +511,7 @@ dependencies = [
|
|
|
511
511
|
[[package]]
|
|
512
512
|
name = "chalk-queue"
|
|
513
513
|
version = "0.0.0"
|
|
514
|
-
source = "git+https://github.com/chalk-ai/chalk-private.git?rev=
|
|
514
|
+
source = "git+https://github.com/chalk-ai/chalk-private.git?rev=1460144ea659debbaee9dccadcd4d7dbf383ccf8#1460144ea659debbaee9dccadcd4d7dbf383ccf8"
|
|
515
515
|
dependencies = [
|
|
516
516
|
"base64 0.22.1",
|
|
517
517
|
"redis",
|
|
@@ -37,6 +37,6 @@ futures-util = "0.3"
|
|
|
37
37
|
# `[patch.crates-io]` here it builds against vanilla crates.io redis (it only
|
|
38
38
|
# uses standard APIs). Metrics and StatsD share a revision so their client types agree.
|
|
39
39
|
[target.'cfg(target_os = "linux")'.dependencies]
|
|
40
|
-
chalk-queue = { git = "https://github.com/chalk-ai/chalk-private.git", rev = "
|
|
40
|
+
chalk-queue = { git = "https://github.com/chalk-ai/chalk-private.git", rev = "1460144ea659debbaee9dccadcd4d7dbf383ccf8" }
|
|
41
41
|
chalk_metrics = { git = "https://github.com/chalk-ai/chalk-private.git", rev = "a0f8932ec10d3b8340af3e98cf63b800d60b4926" }
|
|
42
42
|
chalk_statsd = { git = "https://github.com/chalk-ai/chalk-private.git", rev = "a0f8932ec10d3b8340af3e98cf63b800d60b4926" }
|
|
@@ -39,7 +39,7 @@ use std::time::{Duration, Instant};
|
|
|
39
39
|
|
|
40
40
|
use chalk_queue::semaphore::renew_many;
|
|
41
41
|
use chalk_queue::stream_writer;
|
|
42
|
-
use chalk_queue::types::QueuedCall;
|
|
42
|
+
use chalk_queue::types::{DeliveryAttempt, QueuedCall};
|
|
43
43
|
use chalk_queue::{
|
|
44
44
|
BoundFunctionQueue, DequeuedCall, FunctionQueue, QueueProtocol, QueueReceipt, SemaphoreLease,
|
|
45
45
|
};
|
|
@@ -108,6 +108,7 @@ const PEER: &str = "fnq-self-consumer";
|
|
|
108
108
|
enum ResultDestination {
|
|
109
109
|
PerCall {
|
|
110
110
|
stream_key: String,
|
|
111
|
+
result_ttl: Duration,
|
|
111
112
|
},
|
|
112
113
|
Batch {
|
|
113
114
|
stream_key: String,
|
|
@@ -125,10 +126,18 @@ impl ResultDestination {
|
|
|
125
126
|
batch_result.row_index,
|
|
126
127
|
)
|
|
127
128
|
});
|
|
128
|
-
Self::from_routing(
|
|
129
|
+
Self::from_routing(
|
|
130
|
+
queue.stream_key_for(call_uuid),
|
|
131
|
+
Duration::from_secs(work.result_ttl_seconds),
|
|
132
|
+
batch,
|
|
133
|
+
)
|
|
129
134
|
}
|
|
130
135
|
|
|
131
|
-
fn from_routing(
|
|
136
|
+
fn from_routing(
|
|
137
|
+
per_call_stream_key: String,
|
|
138
|
+
result_ttl: Duration,
|
|
139
|
+
batch: Option<(String, String, u64)>,
|
|
140
|
+
) -> Self {
|
|
132
141
|
match batch {
|
|
133
142
|
Some((stream_key, call_id, row_index)) => Self::Batch {
|
|
134
143
|
stream_key,
|
|
@@ -137,6 +146,7 @@ impl ResultDestination {
|
|
|
137
146
|
},
|
|
138
147
|
None => Self::PerCall {
|
|
139
148
|
stream_key: per_call_stream_key,
|
|
149
|
+
result_ttl,
|
|
140
150
|
},
|
|
141
151
|
}
|
|
142
152
|
}
|
|
@@ -148,7 +158,7 @@ impl ResultDestination {
|
|
|
148
158
|
data: &[u8],
|
|
149
159
|
) -> Result<(), redis::RedisError> {
|
|
150
160
|
match self {
|
|
151
|
-
Self::PerCall { stream_key } => {
|
|
161
|
+
Self::PerCall { stream_key, .. } => {
|
|
152
162
|
stream_writer::write_chunk(conn, stream_key, seq, data).await
|
|
153
163
|
}
|
|
154
164
|
Self::Batch {
|
|
@@ -169,7 +179,10 @@ impl ResultDestination {
|
|
|
169
179
|
seq: u64,
|
|
170
180
|
) -> Result<(), redis::RedisError> {
|
|
171
181
|
match self {
|
|
172
|
-
Self::PerCall {
|
|
182
|
+
Self::PerCall {
|
|
183
|
+
stream_key,
|
|
184
|
+
result_ttl,
|
|
185
|
+
} => stream_writer::write_end_with_ttl(conn, stream_key, seq, *result_ttl).await,
|
|
173
186
|
Self::Batch {
|
|
174
187
|
stream_key,
|
|
175
188
|
call_id,
|
|
@@ -191,9 +204,20 @@ impl ResultDestination {
|
|
|
191
204
|
traceback: Option<&str>,
|
|
192
205
|
) -> Result<(), redis::RedisError> {
|
|
193
206
|
match self {
|
|
194
|
-
Self::PerCall {
|
|
195
|
-
|
|
196
|
-
|
|
207
|
+
Self::PerCall {
|
|
208
|
+
stream_key,
|
|
209
|
+
result_ttl,
|
|
210
|
+
} => {
|
|
211
|
+
stream_writer::write_error_with_ttl(
|
|
212
|
+
conn,
|
|
213
|
+
stream_key,
|
|
214
|
+
seq,
|
|
215
|
+
exc_type,
|
|
216
|
+
message,
|
|
217
|
+
traceback,
|
|
218
|
+
*result_ttl,
|
|
219
|
+
)
|
|
220
|
+
.await
|
|
197
221
|
}
|
|
198
222
|
Self::Batch {
|
|
199
223
|
stream_key,
|
|
@@ -665,25 +689,83 @@ async fn run_one_call(
|
|
|
665
689
|
flush_timeout: Duration,
|
|
666
690
|
metrics_pipeline: Option<Arc<PublishingMetricsPipeline>>,
|
|
667
691
|
) {
|
|
692
|
+
let attempt = match input_queue.delivery_attempt(&delivery).await {
|
|
693
|
+
Ok(attempt) => attempt,
|
|
694
|
+
Err(error) => {
|
|
695
|
+
error!(call_id = %delivery.call.call_id, %error, "failed to resolve delivery attempt");
|
|
696
|
+
finish_delivery(
|
|
697
|
+
&input_queue,
|
|
698
|
+
&delivery.receipt,
|
|
699
|
+
false,
|
|
700
|
+
&delivery.call.call_id,
|
|
701
|
+
lease,
|
|
702
|
+
&live_calls,
|
|
703
|
+
)
|
|
704
|
+
.await;
|
|
705
|
+
return;
|
|
706
|
+
}
|
|
707
|
+
};
|
|
668
708
|
let DequeuedCall {
|
|
669
709
|
call: work,
|
|
670
710
|
receipt,
|
|
671
711
|
} = delivery;
|
|
672
712
|
let call_id = work.call_id.clone();
|
|
713
|
+
let final_attempt = match attempt {
|
|
714
|
+
DeliveryAttempt::Ready {
|
|
715
|
+
attempt,
|
|
716
|
+
max_attempts,
|
|
717
|
+
} => {
|
|
718
|
+
info!(%call_id, attempt, max_attempts, "executing queued call attempt");
|
|
719
|
+
attempt == max_attempts
|
|
720
|
+
}
|
|
721
|
+
DeliveryAttempt::Exhausted {
|
|
722
|
+
attempts,
|
|
723
|
+
max_attempts,
|
|
724
|
+
} => {
|
|
725
|
+
let message = format!(
|
|
726
|
+
"retry attempts exhausted after {attempts} deliveries ({max_attempts} executions allowed)"
|
|
727
|
+
);
|
|
728
|
+
let completed = write_terminal_error(
|
|
729
|
+
&queue,
|
|
730
|
+
&mut conn,
|
|
731
|
+
&work,
|
|
732
|
+
"RetryAttemptsExhausted",
|
|
733
|
+
&message,
|
|
734
|
+
None,
|
|
735
|
+
)
|
|
736
|
+
.await
|
|
737
|
+
.is_ok();
|
|
738
|
+
finish_delivery(
|
|
739
|
+
&input_queue,
|
|
740
|
+
&receipt,
|
|
741
|
+
completed,
|
|
742
|
+
&call_id,
|
|
743
|
+
lease,
|
|
744
|
+
&live_calls,
|
|
745
|
+
)
|
|
746
|
+
.await;
|
|
747
|
+
return;
|
|
748
|
+
}
|
|
749
|
+
DeliveryAttempt::OwnershipLost => {
|
|
750
|
+
finish_delivery(&input_queue, &receipt, false, &call_id, lease, &live_calls).await;
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
753
|
+
};
|
|
673
754
|
let completed = match AssertUnwindSafe(execute_and_write(
|
|
674
755
|
&handler,
|
|
675
756
|
&queue,
|
|
676
757
|
&mut conn,
|
|
677
|
-
work,
|
|
758
|
+
work.clone(),
|
|
678
759
|
flush_timeout,
|
|
760
|
+
final_attempt,
|
|
679
761
|
metrics_pipeline.as_ref(),
|
|
680
762
|
))
|
|
681
763
|
.catch_unwind()
|
|
682
764
|
.await
|
|
683
765
|
{
|
|
684
|
-
Ok(Ok(
|
|
766
|
+
Ok(Ok(completed)) => completed,
|
|
685
767
|
Ok(Err(e)) => {
|
|
686
|
-
// The call's result stream may be incomplete; the
|
|
768
|
+
// The call's result stream may be incomplete; the enqueue safety TTL
|
|
687
769
|
// and the meta status are the backstop for a stuck poller.
|
|
688
770
|
error!(call_id = %call_id, error = %e, "failed to execute/write queued call");
|
|
689
771
|
false
|
|
@@ -699,7 +781,20 @@ async fn run_one_call(
|
|
|
699
781
|
panic = %panic_message,
|
|
700
782
|
"panic while executing/writing queued call"
|
|
701
783
|
);
|
|
702
|
-
|
|
784
|
+
if final_attempt {
|
|
785
|
+
write_terminal_error(
|
|
786
|
+
&queue,
|
|
787
|
+
&mut conn,
|
|
788
|
+
&work,
|
|
789
|
+
"RemoteFunctionPanic",
|
|
790
|
+
panic_message,
|
|
791
|
+
None,
|
|
792
|
+
)
|
|
793
|
+
.await
|
|
794
|
+
.is_ok()
|
|
795
|
+
} else {
|
|
796
|
+
false
|
|
797
|
+
}
|
|
703
798
|
}
|
|
704
799
|
};
|
|
705
800
|
|
|
@@ -753,24 +848,82 @@ async fn run_one_coalesced_call(
|
|
|
753
848
|
live_calls: LiveCalls,
|
|
754
849
|
metrics_pipeline: Option<Arc<PublishingMetricsPipeline>>,
|
|
755
850
|
) {
|
|
851
|
+
let attempt = match input_queue.delivery_attempt(&delivery).await {
|
|
852
|
+
Ok(attempt) => attempt,
|
|
853
|
+
Err(error) => {
|
|
854
|
+
error!(call_id = %delivery.call.call_id, %error, "failed to resolve delivery attempt");
|
|
855
|
+
finish_delivery(
|
|
856
|
+
&input_queue,
|
|
857
|
+
&delivery.receipt,
|
|
858
|
+
false,
|
|
859
|
+
&delivery.call.call_id,
|
|
860
|
+
lease,
|
|
861
|
+
&live_calls,
|
|
862
|
+
)
|
|
863
|
+
.await;
|
|
864
|
+
return;
|
|
865
|
+
}
|
|
866
|
+
};
|
|
756
867
|
let DequeuedCall {
|
|
757
868
|
call: work,
|
|
758
869
|
receipt,
|
|
759
870
|
} = delivery;
|
|
760
871
|
let call_id = work.call_id.clone();
|
|
872
|
+
let final_attempt = match attempt {
|
|
873
|
+
DeliveryAttempt::Ready {
|
|
874
|
+
attempt,
|
|
875
|
+
max_attempts,
|
|
876
|
+
} => {
|
|
877
|
+
info!(%call_id, attempt, max_attempts, "executing coalesced queued call attempt");
|
|
878
|
+
attempt == max_attempts
|
|
879
|
+
}
|
|
880
|
+
DeliveryAttempt::Exhausted {
|
|
881
|
+
attempts,
|
|
882
|
+
max_attempts,
|
|
883
|
+
} => {
|
|
884
|
+
let message = format!(
|
|
885
|
+
"retry attempts exhausted after {attempts} deliveries ({max_attempts} executions allowed)"
|
|
886
|
+
);
|
|
887
|
+
let completed = write_terminal_error(
|
|
888
|
+
&queue,
|
|
889
|
+
&mut conn,
|
|
890
|
+
&work,
|
|
891
|
+
"RetryAttemptsExhausted",
|
|
892
|
+
&message,
|
|
893
|
+
None,
|
|
894
|
+
)
|
|
895
|
+
.await
|
|
896
|
+
.is_ok();
|
|
897
|
+
finish_delivery(
|
|
898
|
+
&input_queue,
|
|
899
|
+
&receipt,
|
|
900
|
+
completed,
|
|
901
|
+
&call_id,
|
|
902
|
+
lease,
|
|
903
|
+
&live_calls,
|
|
904
|
+
)
|
|
905
|
+
.await;
|
|
906
|
+
return;
|
|
907
|
+
}
|
|
908
|
+
DeliveryAttempt::OwnershipLost => {
|
|
909
|
+
finish_delivery(&input_queue, &receipt, false, &call_id, lease, &live_calls).await;
|
|
910
|
+
return;
|
|
911
|
+
}
|
|
912
|
+
};
|
|
761
913
|
let completed = match coalesced_execute_and_write(
|
|
762
914
|
&coalescing_queue,
|
|
763
915
|
&queue,
|
|
764
916
|
&mut conn,
|
|
765
917
|
work,
|
|
918
|
+
final_attempt,
|
|
766
919
|
metrics_pipeline.as_ref(),
|
|
767
920
|
)
|
|
768
921
|
.await
|
|
769
922
|
{
|
|
770
|
-
Ok(
|
|
923
|
+
Ok(completed) => completed,
|
|
771
924
|
Err(e) => {
|
|
772
|
-
// The call's result stream may be incomplete; the
|
|
773
|
-
// the meta status are the backstop for a stuck poller.
|
|
925
|
+
// The call's result stream may be incomplete; the enqueue safety TTL
|
|
926
|
+
// and the meta status are the backstop for a stuck poller.
|
|
774
927
|
error!(call_id = %call_id, error = %e, "failed to execute/write coalesced queued call");
|
|
775
928
|
false
|
|
776
929
|
}
|
|
@@ -797,8 +950,9 @@ async fn coalesced_execute_and_write(
|
|
|
797
950
|
queue: &FunctionQueue,
|
|
798
951
|
conn: &mut ConnectionManager,
|
|
799
952
|
work: QueuedCall,
|
|
953
|
+
final_attempt: bool,
|
|
800
954
|
metrics_pipeline: Option<&Arc<PublishingMetricsPipeline>>,
|
|
801
|
-
) -> Result<
|
|
955
|
+
) -> Result<bool, Box<dyn std::error::Error + Send + Sync>> {
|
|
802
956
|
let call_uuid = Uuid::parse_str(&work.call_id)?;
|
|
803
957
|
let result_destination = ResultDestination::for_call(queue, &work, call_uuid);
|
|
804
958
|
let ipc_bytes = work.call.feather_bytes()?;
|
|
@@ -857,6 +1011,10 @@ async fn coalesced_execute_and_write(
|
|
|
857
1011
|
info!(call_id = %work.call_id, "coalesced queued call completed");
|
|
858
1012
|
}
|
|
859
1013
|
Some(Err(status)) => {
|
|
1014
|
+
if !final_attempt {
|
|
1015
|
+
warn!(call_id = %work.call_id, error = %status, "coalesced queued call failed; leaving delivery pending for retry");
|
|
1016
|
+
return Ok(false);
|
|
1017
|
+
}
|
|
860
1018
|
// Parity with the enqueue/poll path: only a message string is
|
|
861
1019
|
// available, so exc_type is a fixed placeholder.
|
|
862
1020
|
result_destination
|
|
@@ -873,6 +1031,10 @@ async fn coalesced_execute_and_write(
|
|
|
873
1031
|
// happen (it always sends one Ok/Err per caller), but treat a
|
|
874
1032
|
// missing response as a failure rather than leaving the poller hung.
|
|
875
1033
|
let msg = "coalesced dispatch produced no response";
|
|
1034
|
+
if !final_attempt {
|
|
1035
|
+
warn!(call_id = %work.call_id, "{msg}; leaving delivery pending for retry");
|
|
1036
|
+
return Ok(false);
|
|
1037
|
+
}
|
|
876
1038
|
result_destination
|
|
877
1039
|
.write_error(conn, 1, "RemoteFunctionError", msg, None)
|
|
878
1040
|
.await?;
|
|
@@ -882,7 +1044,7 @@ async fn coalesced_execute_and_write(
|
|
|
882
1044
|
error!(call_id = %work.call_id, "{msg}");
|
|
883
1045
|
}
|
|
884
1046
|
}
|
|
885
|
-
Ok(
|
|
1047
|
+
Ok(true)
|
|
886
1048
|
}
|
|
887
1049
|
|
|
888
1050
|
// Insert trace ID into headers if none already present
|
|
@@ -910,8 +1072,9 @@ async fn execute_and_write(
|
|
|
910
1072
|
conn: &mut ConnectionManager,
|
|
911
1073
|
work: QueuedCall,
|
|
912
1074
|
flush_timeout: Duration,
|
|
1075
|
+
final_attempt: bool,
|
|
913
1076
|
metrics_pipeline: Option<&Arc<PublishingMetricsPipeline>>,
|
|
914
|
-
) -> Result<
|
|
1077
|
+
) -> Result<bool, Box<dyn std::error::Error + Send + Sync>> {
|
|
915
1078
|
let call_uuid = Uuid::parse_str(&work.call_id)?;
|
|
916
1079
|
let result_destination = ResultDestination::for_call(queue, &work, call_uuid);
|
|
917
1080
|
let ipc_bytes = work.call.feather_bytes()?;
|
|
@@ -923,13 +1086,9 @@ async fn execute_and_write(
|
|
|
923
1086
|
let _ = queue.update_status(call_uuid, "running").await;
|
|
924
1087
|
info!(call_id = %work.call_id, function = %function_name, "running queued call");
|
|
925
1088
|
|
|
926
|
-
// The
|
|
927
|
-
//
|
|
928
|
-
//
|
|
929
|
-
// handler returns: we wait on the notify (signaled on each append) and flush
|
|
930
|
-
// immediately when chunks arrive, so a slow generator's early yields reach
|
|
931
|
-
// the client without waiting for the whole call to finish. A timeout fallback
|
|
932
|
-
// ensures we make progress if the notification pipeline breaks.
|
|
1089
|
+
// The Python bridge appends each generator yield into `buffer`. Calls
|
|
1090
|
+
// without retries flush incrementally; retry-enabled calls retain chunks
|
|
1091
|
+
// until the attempt succeeds so a later attempt cannot duplicate output.
|
|
933
1092
|
let buffer = ChunkBuffer::new().into_shared();
|
|
934
1093
|
let handler_fut = handler.call_into_buffer(
|
|
935
1094
|
ipc_bytes,
|
|
@@ -944,6 +1103,9 @@ async fn execute_and_write(
|
|
|
944
1103
|
// seq 0. `flushed` is the count of chunks already written to the stream.
|
|
945
1104
|
let mut seq: u64 = 1;
|
|
946
1105
|
let mut flushed: usize = 0;
|
|
1106
|
+
// A retry must not duplicate generator chunks. Retry-enabled calls buffer
|
|
1107
|
+
// output until the handler succeeds or reaches its final attempt.
|
|
1108
|
+
let buffer_until_terminal = work.max_retries.is_some();
|
|
947
1109
|
|
|
948
1110
|
// Completion is driven by the handler future resolving, NOT by the buffer
|
|
949
1111
|
// emitter being torn down — so a handler that pins its emitter (e.g. via a
|
|
@@ -960,10 +1122,12 @@ async fn execute_and_write(
|
|
|
960
1122
|
biased;
|
|
961
1123
|
res = &mut handler_fut => break res,
|
|
962
1124
|
_ = tokio::time::timeout(flush_timeout, notify_handle.notified()) => {
|
|
963
|
-
if
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
1125
|
+
if !buffer_until_terminal {
|
|
1126
|
+
if let Err(e) = flush_new(conn, &result_destination, &buffer, &mut flushed, &mut seq).await {
|
|
1127
|
+
// Non-fatal: keep draining; the cursor didn't advance past the
|
|
1128
|
+
// failed chunk, so the next wait retries it without duplicating.
|
|
1129
|
+
warn!(call_id = %work.call_id, error = %e, "failed to flush partial results; retrying");
|
|
1130
|
+
}
|
|
967
1131
|
}
|
|
968
1132
|
}
|
|
969
1133
|
}
|
|
@@ -982,6 +1146,11 @@ async fn execute_and_write(
|
|
|
982
1146
|
);
|
|
983
1147
|
}
|
|
984
1148
|
|
|
1149
|
+
if result.is_err() && !final_attempt {
|
|
1150
|
+
warn!(call_id = %work.call_id, "queued call failed; leaving delivery pending for retry");
|
|
1151
|
+
return Ok(false);
|
|
1152
|
+
}
|
|
1153
|
+
|
|
985
1154
|
// Flush whatever the handler produced after the loop exits (on the fast path
|
|
986
1155
|
// this is everything). This happens even if the handler panicked mid-execution,
|
|
987
1156
|
// so partial output is always preserved. Parity with the enqueue/poll path: a
|
|
@@ -1025,6 +1194,34 @@ async fn execute_and_write(
|
|
|
1025
1194
|
);
|
|
1026
1195
|
}
|
|
1027
1196
|
}
|
|
1197
|
+
Ok(true)
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
async fn write_terminal_error(
|
|
1201
|
+
queue: &FunctionQueue,
|
|
1202
|
+
conn: &mut ConnectionManager,
|
|
1203
|
+
work: &QueuedCall,
|
|
1204
|
+
exc_type: &str,
|
|
1205
|
+
message: &str,
|
|
1206
|
+
traceback: Option<&str>,
|
|
1207
|
+
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
|
1208
|
+
let call_uuid = Uuid::parse_str(&work.call_id)?;
|
|
1209
|
+
let result_destination = ResultDestination::for_call(queue, work, call_uuid);
|
|
1210
|
+
result_destination
|
|
1211
|
+
.write_error(conn, 1, exc_type, message, traceback)
|
|
1212
|
+
.await?;
|
|
1213
|
+
let summary: String = message.chars().take(200).collect();
|
|
1214
|
+
let processing_trace_id = work
|
|
1215
|
+
.metadata
|
|
1216
|
+
.otel_headers
|
|
1217
|
+
.get("traceparent")
|
|
1218
|
+
.and_then(|value| value.split('-').nth(1))
|
|
1219
|
+
.filter(|value| value.len() == 32)
|
|
1220
|
+
.map(str::to_string);
|
|
1221
|
+
let _ = queue
|
|
1222
|
+
.complete_status(call_uuid, "failed", &summary, processing_trace_id)
|
|
1223
|
+
.await;
|
|
1224
|
+
error!(call_id = %work.call_id, %message, "queued call reached terminal failure");
|
|
1028
1225
|
Ok(())
|
|
1029
1226
|
}
|
|
1030
1227
|
|
|
@@ -1128,9 +1325,14 @@ mod tests {
|
|
|
1128
1325
|
#[test]
|
|
1129
1326
|
fn result_destination_preserves_per_call_stream() {
|
|
1130
1327
|
assert_eq!(
|
|
1131
|
-
ResultDestination::from_routing(
|
|
1328
|
+
ResultDestination::from_routing(
|
|
1329
|
+
"tenant:gen:call-1:stream".to_string(),
|
|
1330
|
+
Duration::from_secs(60),
|
|
1331
|
+
None,
|
|
1332
|
+
),
|
|
1132
1333
|
ResultDestination::PerCall {
|
|
1133
1334
|
stream_key: "tenant:gen:call-1:stream".to_string(),
|
|
1335
|
+
result_ttl: Duration::from_secs(60),
|
|
1134
1336
|
}
|
|
1135
1337
|
);
|
|
1136
1338
|
}
|
|
@@ -1140,6 +1342,7 @@ mod tests {
|
|
|
1140
1342
|
assert_eq!(
|
|
1141
1343
|
ResultDestination::from_routing(
|
|
1142
1344
|
"unused-per-call-stream".to_string(),
|
|
1345
|
+
Duration::from_secs(60),
|
|
1143
1346
|
Some((
|
|
1144
1347
|
"tenant:batch:batch-1:results".to_string(),
|
|
1145
1348
|
"call-1".to_string(),
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "1.10.2"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "1.10.0"
|
|
File without changes
|
|
File without changes
|
{chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/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
|
|
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.10.0 → chalk_remote_call_python-1.10.2}/chalk_remote_call/__init__.py
RENAMED
|
File without changes
|
{chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk_remote_call/__main__.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
|
|
File without changes
|
{chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk_remote_call/_native.pyi
RENAMED
|
File without changes
|
{chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk_remote_call/arrow_utils.py
RENAMED
|
File without changes
|
{chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk_remote_call/cli.py
RENAMED
|
File without changes
|
{chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk_remote_call/errors.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk_remote_call/logging.py
RENAMED
|
File without changes
|
{chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk_remote_call/server.py
RENAMED
|
File without changes
|
{chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/chalk_remote_call/servicer.py
RENAMED
|
File without changes
|
{chalk_remote_call_python-1.10.0 → chalk_remote_call_python-1.10.2}/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
|