Port #3748 to main: executor client connect timeout - #3852
Conversation
✅ Deploy Preview for golemcloud canceled.
|
|
Found one more thing in the new review:
Reproducer: diff --git a/golem-service-base/tests/grpc_client.rs b/golem-service-base/tests/grpc_client.rs
index a3e2da8b3..0922f0f7c 100644
--- a/golem-service-base/tests/grpc_client.rs
+++ b/golem-service-base/tests/grpc_client.rs
@@ -1484,3 +1484,40 @@ async fn a_reset_stream_does_not_tear_down_the_connection_carrying_it() {
new one instead of reusing the channel every other request rides"
);
}
+
+#[test]
+async fn review_cancel_reset_must_not_cancel_sibling_call() {
+ let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
+ let uri: Uri = format!("http://{}", listener.local_addr().unwrap()).parse().unwrap();
+ let (started_tx, started_rx) = tokio::sync::oneshot::channel();
+ let server = tokio::spawn(async move {
+ let (socket, _) = listener.accept().await.unwrap();
+ let mut connection = h2::server::handshake(socket).await.unwrap();
+ let first_stream = connection.accept().await.unwrap().unwrap();
+ let _ = started_tx.send(());
+ while let Some(Ok((_request, mut respond))) = connection.accept().await {
+ respond.send_reset(h2::Reason::CANCEL);
+ }
+ drop(first_stream);
+ });
+ let client = executor_client(no_keepalive(Duration::from_secs(5)));
+ let sibling = tokio::spawn({
+ let client = client.clone();
+ let uri = uri.clone();
+ async move {
+ client.call_without_retry("sibling", uri, move |executor| {
+ Box::pin(executor.assign_shards(AssignShardsRequest { shard_ids: vec![] }))
+ }).await.map(|_| ())
+ }
+ });
+ started_rx.await.unwrap();
+ let reset = ping(&client, uri).await.expect_err("peer resets the stream");
+ assert_eq!(reset.code(), tonic::Code::Cancelled);
+ assert!(std::error::Error::source(&reset)
+ .map(|source| source.is::<tonic::transport::Error>()).unwrap_or(false));
+ let mut sibling = sibling;
+ let outcome = tokio::time::timeout(Duration::from_millis(200), &mut sibling).await;
+ sibling.abort();
+ server.abort();
+ assert!(outcome.is_err(), "stream-only CANCEL terminated sibling: {outcome:?}");
+} |
|
Fixed in ee2cd88. The two predicates read only the code and whether a What does separate them is further down the chain. Tests, in One note for later: the predicate is identical on |
Ports #3748 to
main.golem-service-base/src/grpc/client.rsonmainwas the 1.5.x base plus one addition of its own,call_without_retryon bothGrpcClientandMultiTargetGrpcClient, for operations whose request cannot be replayed. That is reinstated on top of the rewritten client, threaded through the newCallAttemptsasnot_replayable. Its three callers (clients/registry.rs, worker-service's invocation session,worker_proxy.rs) are unchanged.One behavioural note that falls out of the rewrite: a failed connect is still retried under
call_without_retry. Connecting is now eager and happens before the closure runs, so nothing of the request has been consumed and the next attempt is the first one the peer could ever see. Only the call itself is gated.main's own test for this,multi_target_call_without_retry_attempts_unavailable_request_once, aimed an unreachable target at the client and counted closure invocations. With eager connect that target never reaches the closure at all, so it is replaced by two tests in the newmod testbuilt on itssilent_peerharness: one asserting a non-replayable call is attempted exactly once, and its converse asserting the same failure is retried when the request can be replayed, so the first is measuring the gate rather than a client that would not have retried anyway.The shard-manager config regenerates with three more
http2_keep_alive_*keys than the 1.5.x hunks carry, underregistry_serviceand the shard-manager client, both of which differ onmain. All twelve config files here arecargo make generate-configsoutput.Whole workspace type-checks with
--all-targets.golem-service-base: 67 unit tests and the 18 newgrpc_clientintegration tests pass.