Skip to content

Commit d76f19e

Browse files
committed
Client keep alive support
Fixes #395
1 parent ded3747 commit d76f19e

File tree

4 files changed

+70
-6
lines changed

4 files changed

+70
-6
lines changed

temporalio/bridge/client.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,14 @@ class ClientRetryConfig:
3838
max_retries: int
3939

4040

41+
@dataclass
42+
class ClientKeepAliveConfig:
43+
"""Python representation of the Rust struct for configuring keep alive."""
44+
45+
interval_millis: int
46+
timeout_millis: int
47+
48+
4149
@dataclass
4250
class ClientConfig:
4351
"""Python representation of the Rust struct for configuring the client."""
@@ -47,6 +55,7 @@ class ClientConfig:
4755
identity: str
4856
tls_config: Optional[ClientTlsConfig]
4957
retry_config: Optional[ClientRetryConfig]
58+
keep_alive_config: Optional[ClientKeepAliveConfig]
5059
client_name: str
5160
client_version: str
5261

temporalio/bridge/src/client.rs

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ use std::str::FromStr;
66
use std::sync::Arc;
77
use std::time::Duration;
88
use temporal_client::{
9-
ClientOptions, ClientOptionsBuilder, ConfiguredClient, HealthService, OperatorService,
10-
RetryClient, RetryConfig, TemporalServiceClientWithMetrics, TestService, TlsConfig,
11-
WorkflowService,
9+
ClientKeepAliveConfig as CoreClientKeepAliveConfig, ClientOptions, ClientOptionsBuilder,
10+
ConfiguredClient, HealthService, OperatorService, RetryClient, RetryConfig,
11+
TemporalServiceClientWithMetrics, TestService, TlsConfig, WorkflowService,
1212
};
1313
use tonic::metadata::MetadataKey;
1414
use url::Url;
@@ -34,6 +34,7 @@ pub struct ClientConfig {
3434
identity: String,
3535
tls_config: Option<ClientTlsConfig>,
3636
retry_config: Option<ClientRetryConfig>,
37+
keep_alive_config: Option<ClientKeepAliveConfig>,
3738
}
3839

3940
#[derive(FromPyObject)]
@@ -54,6 +55,12 @@ struct ClientRetryConfig {
5455
pub max_retries: usize,
5556
}
5657

58+
#[derive(FromPyObject)]
59+
struct ClientKeepAliveConfig {
60+
pub interval_millis: u64,
61+
pub timeout_millis: u64,
62+
}
63+
5764
#[derive(FromPyObject)]
5865
struct RpcCall {
5966
rpc: String,
@@ -388,7 +395,8 @@ impl TryFrom<ClientConfig> for ClientOptions {
388395
.retry_config(
389396
opts.retry_config
390397
.map_or(RetryConfig::default(), |c| c.into()),
391-
);
398+
)
399+
.keep_alive(opts.keep_alive_config.map(Into::into));
392400
// Builder does not allow us to set option here, so we have to make
393401
// a conditional to even call it
394402
if let Some(tls_config) = opts.tls_config {
@@ -437,3 +445,12 @@ impl From<ClientRetryConfig> for RetryConfig {
437445
}
438446
}
439447
}
448+
449+
impl From<ClientKeepAliveConfig> for CoreClientKeepAliveConfig {
450+
fn from(conf: ClientKeepAliveConfig) -> Self {
451+
CoreClientKeepAliveConfig {
452+
interval: Duration::from_millis(conf.interval_millis),
453+
timeout: Duration::from_millis(conf.timeout_millis),
454+
}
455+
}
456+
}

temporalio/client.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,13 @@
5151
import temporalio.runtime
5252
import temporalio.service
5353
import temporalio.workflow
54-
from temporalio.service import RetryConfig, RPCError, RPCStatusCode, TLSConfig
54+
from temporalio.service import (
55+
KeepAliveConfig,
56+
RetryConfig,
57+
RPCError,
58+
RPCStatusCode,
59+
TLSConfig,
60+
)
5561

5662
from .types import (
5763
AnyType,
@@ -96,6 +102,7 @@ async def connect(
96102
] = None,
97103
tls: Union[bool, TLSConfig] = False,
98104
retry_config: Optional[RetryConfig] = None,
105+
keep_alive_config: Optional[KeepAliveConfig] = KeepAliveConfig.default,
99106
rpc_metadata: Mapping[str, str] = {},
100107
identity: Optional[str] = None,
101108
lazy: bool = False,
@@ -128,6 +135,10 @@ async def connect(
128135
opted in) or all high-level calls made by this client (which all
129136
opt-in to retries by default). If unset, a default retry
130137
configuration is used.
138+
keep_alive_config: Keep-alive configuration for the client
139+
connection. Default is to check every 30s and kill the
140+
connection if a response doesn't come back in 15s. Can be set to
141+
``None`` to disable.
131142
rpc_metadata: Headers to use for all calls to the server. Keys here
132143
can be overriden by per-call RPC metadata keys.
133144
identity: Identity for this client. If unset, a default is created
@@ -141,6 +152,7 @@ async def connect(
141152
target_host=target_host,
142153
tls=tls,
143154
retry_config=retry_config,
155+
keep_alive_config=keep_alive_config,
144156
rpc_metadata=rpc_metadata,
145157
identity=identity or "",
146158
lazy=lazy,

temporalio/service.py

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from dataclasses import dataclass, field
1212
from datetime import timedelta
1313
from enum import IntEnum
14-
from typing import Generic, Mapping, Optional, Type, TypeVar, Union
14+
from typing import ClassVar, Generic, Mapping, Optional, Type, TypeVar, Union
1515

1616
import google.protobuf.empty_pb2
1717
import google.protobuf.message
@@ -93,13 +93,36 @@ def _to_bridge_config(self) -> temporalio.bridge.client.ClientRetryConfig:
9393
)
9494

9595

96+
@dataclass(frozen=True)
97+
class KeepAliveConfig:
98+
"""Keep-alive configuration for client connections."""
99+
100+
interval_millis: int = 30000
101+
"""Interval to send HTTP2 keep alive pings."""
102+
timeout_millis: int = 15000
103+
"""Timeout that the keep alive must be responded to within or the connection
104+
will be closed."""
105+
default: ClassVar[KeepAliveConfig]
106+
"""Default keep alive config."""
107+
108+
def _to_bridge_config(self) -> temporalio.bridge.client.ClientKeepAliveConfig:
109+
return temporalio.bridge.client.ClientKeepAliveConfig(
110+
interval_millis=self.interval_millis,
111+
timeout_millis=self.timeout_millis,
112+
)
113+
114+
115+
KeepAliveConfig.default = KeepAliveConfig()
116+
117+
96118
@dataclass
97119
class ConnectConfig:
98120
"""Config for connecting to the server."""
99121

100122
target_host: str
101123
tls: Union[bool, TLSConfig] = False
102124
retry_config: Optional[RetryConfig] = None
125+
keep_alive_config: Optional[KeepAliveConfig] = KeepAliveConfig.default
103126
rpc_metadata: Mapping[str, str] = field(default_factory=dict)
104127
identity: str = ""
105128
lazy: bool = False
@@ -142,6 +165,9 @@ def _to_bridge_config(self) -> temporalio.bridge.client.ClientConfig:
142165
retry_config=self.retry_config._to_bridge_config()
143166
if self.retry_config
144167
else None,
168+
keep_alive_config=self.keep_alive_config._to_bridge_config()
169+
if self.keep_alive_config
170+
else None,
145171
metadata=self.rpc_metadata,
146172
identity=self.identity,
147173
client_name="temporal-python",

0 commit comments

Comments
 (0)