Skip to main content

Conn

Struct Conn 

Source
pub struct Conn { /* private fields */ }
Expand description

A live, namespace-bound connection. Cheap to clone, clones share one HTTP/2 channel, which is what makes multi-namespace fan-out cheap.

Implementations§

Source§

impl Conn

Source

pub async fn connect(profile: &ProfileRef) -> Result<Self, ConnectError>

Examples found in repository?
examples/spike.rs (line 33)
27async fn main() -> anyhow::Result<()> {
28    let profile = ProfileRef {
29        name: std::env::args().nth(1),
30        config_file: None,
31    };
32
33    let conn = Conn::connect(&profile).await?;
34    println!(
35        "connected  profile={}  address={}  namespace={}",
36        conn.profile(),
37        conn.address(),
38        conn.namespace()
39    );
40
41    let mut wf = conn.wf();
42    let ns = wf
43        .list_namespaces(Request::new(ListNamespacesRequest {
44            page_size: 50,
45            ..Default::default()
46        }))
47        .await?
48        .into_inner();
49    println!("\nnamespaces ({}):", ns.namespaces.len());
50    for n in &ns.namespaces {
51        if let Some(info) = &n.namespace_info {
52            println!("  - {}", info.name);
53        }
54    }
55
56    let count = wf
57        .count_workflow_executions(Request::new(CountWorkflowExecutionsRequest {
58            namespace: conn.namespace().to_string(),
59            query: String::new(),
60        }))
61        .await?
62        .into_inner();
63    println!(
64        "\ntotal workflows in `{}`: {}",
65        conn.namespace(),
66        count.count
67    );
68
69    let list = wf
70        .list_workflow_executions(Request::new(ListWorkflowExecutionsRequest {
71            namespace: conn.namespace().to_string(),
72            page_size: 10,
73            query: String::new(),
74            ..Default::default()
75        }))
76        .await?
77        .into_inner();
78
79    println!("\nworkflows ({} shown):", list.executions.len());
80    let mut first: Option<WorkflowExecution> = None;
81    for e in &list.executions {
82        let exec = e.execution.clone().unwrap_or_default();
83        println!(
84            "  {:<10} {:<28} {}",
85            format!("{:?}", e.status()),
86            e.r#type.as_ref().map(|t| t.name.as_str()).unwrap_or("?"),
87            exec.workflow_id
88        );
89        first.get_or_insert(exec);
90    }
91    println!("  next_page_token: {} bytes", list.next_page_token.len());
92
93    if let Some(exec) = first {
94        let hist = wf
95            .get_workflow_execution_history(Request::new(GetWorkflowExecutionHistoryRequest {
96                namespace: conn.namespace().to_string(),
97                execution: Some(exec.clone()),
98                maximum_page_size: 100,
99                // `wait_new_event: true` is what turns this into `tail -f`. Left false
100                // here so the spike terminates.
101                wait_new_event: false,
102                history_event_filter_type: HistoryEventFilterType::AllEvent as i32,
103                ..Default::default()
104            }))
105            .await?
106            .into_inner();
107
108        let events = hist.history.map(|h| h.events).unwrap_or_default();
109        println!(
110            "\nhistory of {} ({} events):",
111            exec.workflow_id,
112            events.len()
113        );
114        for ev in events.iter().take(15) {
115            println!("  {:>4}  {:?}", ev.event_id, ev.event_type());
116        }
117    }
118
119    Ok(())
120}
Source

pub fn namespace(&self) -> &str

Examples found in repository?
examples/spike.rs (line 38)
27async fn main() -> anyhow::Result<()> {
28    let profile = ProfileRef {
29        name: std::env::args().nth(1),
30        config_file: None,
31    };
32
33    let conn = Conn::connect(&profile).await?;
34    println!(
35        "connected  profile={}  address={}  namespace={}",
36        conn.profile(),
37        conn.address(),
38        conn.namespace()
39    );
40
41    let mut wf = conn.wf();
42    let ns = wf
43        .list_namespaces(Request::new(ListNamespacesRequest {
44            page_size: 50,
45            ..Default::default()
46        }))
47        .await?
48        .into_inner();
49    println!("\nnamespaces ({}):", ns.namespaces.len());
50    for n in &ns.namespaces {
51        if let Some(info) = &n.namespace_info {
52            println!("  - {}", info.name);
53        }
54    }
55
56    let count = wf
57        .count_workflow_executions(Request::new(CountWorkflowExecutionsRequest {
58            namespace: conn.namespace().to_string(),
59            query: String::new(),
60        }))
61        .await?
62        .into_inner();
63    println!(
64        "\ntotal workflows in `{}`: {}",
65        conn.namespace(),
66        count.count
67    );
68
69    let list = wf
70        .list_workflow_executions(Request::new(ListWorkflowExecutionsRequest {
71            namespace: conn.namespace().to_string(),
72            page_size: 10,
73            query: String::new(),
74            ..Default::default()
75        }))
76        .await?
77        .into_inner();
78
79    println!("\nworkflows ({} shown):", list.executions.len());
80    let mut first: Option<WorkflowExecution> = None;
81    for e in &list.executions {
82        let exec = e.execution.clone().unwrap_or_default();
83        println!(
84            "  {:<10} {:<28} {}",
85            format!("{:?}", e.status()),
86            e.r#type.as_ref().map(|t| t.name.as_str()).unwrap_or("?"),
87            exec.workflow_id
88        );
89        first.get_or_insert(exec);
90    }
91    println!("  next_page_token: {} bytes", list.next_page_token.len());
92
93    if let Some(exec) = first {
94        let hist = wf
95            .get_workflow_execution_history(Request::new(GetWorkflowExecutionHistoryRequest {
96                namespace: conn.namespace().to_string(),
97                execution: Some(exec.clone()),
98                maximum_page_size: 100,
99                // `wait_new_event: true` is what turns this into `tail -f`. Left false
100                // here so the spike terminates.
101                wait_new_event: false,
102                history_event_filter_type: HistoryEventFilterType::AllEvent as i32,
103                ..Default::default()
104            }))
105            .await?
106            .into_inner();
107
108        let events = hist.history.map(|h| h.events).unwrap_or_default();
109        println!(
110            "\nhistory of {} ({} events):",
111            exec.workflow_id,
112            events.len()
113        );
114        for ev in events.iter().take(15) {
115            println!("  {:>4}  {:?}", ev.event_id, ev.event_type());
116        }
117    }
118
119    Ok(())
120}
Source

pub fn profile(&self) -> &str

Examples found in repository?
examples/spike.rs (line 36)
27async fn main() -> anyhow::Result<()> {
28    let profile = ProfileRef {
29        name: std::env::args().nth(1),
30        config_file: None,
31    };
32
33    let conn = Conn::connect(&profile).await?;
34    println!(
35        "connected  profile={}  address={}  namespace={}",
36        conn.profile(),
37        conn.address(),
38        conn.namespace()
39    );
40
41    let mut wf = conn.wf();
42    let ns = wf
43        .list_namespaces(Request::new(ListNamespacesRequest {
44            page_size: 50,
45            ..Default::default()
46        }))
47        .await?
48        .into_inner();
49    println!("\nnamespaces ({}):", ns.namespaces.len());
50    for n in &ns.namespaces {
51        if let Some(info) = &n.namespace_info {
52            println!("  - {}", info.name);
53        }
54    }
55
56    let count = wf
57        .count_workflow_executions(Request::new(CountWorkflowExecutionsRequest {
58            namespace: conn.namespace().to_string(),
59            query: String::new(),
60        }))
61        .await?
62        .into_inner();
63    println!(
64        "\ntotal workflows in `{}`: {}",
65        conn.namespace(),
66        count.count
67    );
68
69    let list = wf
70        .list_workflow_executions(Request::new(ListWorkflowExecutionsRequest {
71            namespace: conn.namespace().to_string(),
72            page_size: 10,
73            query: String::new(),
74            ..Default::default()
75        }))
76        .await?
77        .into_inner();
78
79    println!("\nworkflows ({} shown):", list.executions.len());
80    let mut first: Option<WorkflowExecution> = None;
81    for e in &list.executions {
82        let exec = e.execution.clone().unwrap_or_default();
83        println!(
84            "  {:<10} {:<28} {}",
85            format!("{:?}", e.status()),
86            e.r#type.as_ref().map(|t| t.name.as_str()).unwrap_or("?"),
87            exec.workflow_id
88        );
89        first.get_or_insert(exec);
90    }
91    println!("  next_page_token: {} bytes", list.next_page_token.len());
92
93    if let Some(exec) = first {
94        let hist = wf
95            .get_workflow_execution_history(Request::new(GetWorkflowExecutionHistoryRequest {
96                namespace: conn.namespace().to_string(),
97                execution: Some(exec.clone()),
98                maximum_page_size: 100,
99                // `wait_new_event: true` is what turns this into `tail -f`. Left false
100                // here so the spike terminates.
101                wait_new_event: false,
102                history_event_filter_type: HistoryEventFilterType::AllEvent as i32,
103                ..Default::default()
104            }))
105            .await?
106            .into_inner();
107
108        let events = hist.history.map(|h| h.events).unwrap_or_default();
109        println!(
110            "\nhistory of {} ({} events):",
111            exec.workflow_id,
112            events.len()
113        );
114        for ev in events.iter().take(15) {
115            println!("  {:>4}  {:?}", ev.event_id, ev.event_type());
116        }
117    }
118
119    Ok(())
120}
Source

pub fn address(&self) -> &str

The frontend this is connected to, as a URL. Never carries credentials: an API key lives in the connection options, not the target.

Examples found in repository?
examples/spike.rs (line 37)
27async fn main() -> anyhow::Result<()> {
28    let profile = ProfileRef {
29        name: std::env::args().nth(1),
30        config_file: None,
31    };
32
33    let conn = Conn::connect(&profile).await?;
34    println!(
35        "connected  profile={}  address={}  namespace={}",
36        conn.profile(),
37        conn.address(),
38        conn.namespace()
39    );
40
41    let mut wf = conn.wf();
42    let ns = wf
43        .list_namespaces(Request::new(ListNamespacesRequest {
44            page_size: 50,
45            ..Default::default()
46        }))
47        .await?
48        .into_inner();
49    println!("\nnamespaces ({}):", ns.namespaces.len());
50    for n in &ns.namespaces {
51        if let Some(info) = &n.namespace_info {
52            println!("  - {}", info.name);
53        }
54    }
55
56    let count = wf
57        .count_workflow_executions(Request::new(CountWorkflowExecutionsRequest {
58            namespace: conn.namespace().to_string(),
59            query: String::new(),
60        }))
61        .await?
62        .into_inner();
63    println!(
64        "\ntotal workflows in `{}`: {}",
65        conn.namespace(),
66        count.count
67    );
68
69    let list = wf
70        .list_workflow_executions(Request::new(ListWorkflowExecutionsRequest {
71            namespace: conn.namespace().to_string(),
72            page_size: 10,
73            query: String::new(),
74            ..Default::default()
75        }))
76        .await?
77        .into_inner();
78
79    println!("\nworkflows ({} shown):", list.executions.len());
80    let mut first: Option<WorkflowExecution> = None;
81    for e in &list.executions {
82        let exec = e.execution.clone().unwrap_or_default();
83        println!(
84            "  {:<10} {:<28} {}",
85            format!("{:?}", e.status()),
86            e.r#type.as_ref().map(|t| t.name.as_str()).unwrap_or("?"),
87            exec.workflow_id
88        );
89        first.get_or_insert(exec);
90    }
91    println!("  next_page_token: {} bytes", list.next_page_token.len());
92
93    if let Some(exec) = first {
94        let hist = wf
95            .get_workflow_execution_history(Request::new(GetWorkflowExecutionHistoryRequest {
96                namespace: conn.namespace().to_string(),
97                execution: Some(exec.clone()),
98                maximum_page_size: 100,
99                // `wait_new_event: true` is what turns this into `tail -f`. Left false
100                // here so the spike terminates.
101                wait_new_event: false,
102                history_event_filter_type: HistoryEventFilterType::AllEvent as i32,
103                ..Default::default()
104            }))
105            .await?
106            .into_inner();
107
108        let events = hist.history.map(|h| h.events).unwrap_or_default();
109        println!(
110            "\nhistory of {} ({} events):",
111            exec.workflow_id,
112            events.len()
113        );
114        for ev in events.iter().take(15) {
115            println!("  {:>4}  {:?}", ev.event_id, ev.event_type());
116        }
117    }
118
119    Ok(())
120}
Source

pub fn wf(&self) -> Box<dyn WorkflowService>

Raw WorkflowService. Requests take tonic::Request<T> and the connection’s retry policy is already applied underneath.

Examples found in repository?
examples/spike.rs (line 41)
27async fn main() -> anyhow::Result<()> {
28    let profile = ProfileRef {
29        name: std::env::args().nth(1),
30        config_file: None,
31    };
32
33    let conn = Conn::connect(&profile).await?;
34    println!(
35        "connected  profile={}  address={}  namespace={}",
36        conn.profile(),
37        conn.address(),
38        conn.namespace()
39    );
40
41    let mut wf = conn.wf();
42    let ns = wf
43        .list_namespaces(Request::new(ListNamespacesRequest {
44            page_size: 50,
45            ..Default::default()
46        }))
47        .await?
48        .into_inner();
49    println!("\nnamespaces ({}):", ns.namespaces.len());
50    for n in &ns.namespaces {
51        if let Some(info) = &n.namespace_info {
52            println!("  - {}", info.name);
53        }
54    }
55
56    let count = wf
57        .count_workflow_executions(Request::new(CountWorkflowExecutionsRequest {
58            namespace: conn.namespace().to_string(),
59            query: String::new(),
60        }))
61        .await?
62        .into_inner();
63    println!(
64        "\ntotal workflows in `{}`: {}",
65        conn.namespace(),
66        count.count
67    );
68
69    let list = wf
70        .list_workflow_executions(Request::new(ListWorkflowExecutionsRequest {
71            namespace: conn.namespace().to_string(),
72            page_size: 10,
73            query: String::new(),
74            ..Default::default()
75        }))
76        .await?
77        .into_inner();
78
79    println!("\nworkflows ({} shown):", list.executions.len());
80    let mut first: Option<WorkflowExecution> = None;
81    for e in &list.executions {
82        let exec = e.execution.clone().unwrap_or_default();
83        println!(
84            "  {:<10} {:<28} {}",
85            format!("{:?}", e.status()),
86            e.r#type.as_ref().map(|t| t.name.as_str()).unwrap_or("?"),
87            exec.workflow_id
88        );
89        first.get_or_insert(exec);
90    }
91    println!("  next_page_token: {} bytes", list.next_page_token.len());
92
93    if let Some(exec) = first {
94        let hist = wf
95            .get_workflow_execution_history(Request::new(GetWorkflowExecutionHistoryRequest {
96                namespace: conn.namespace().to_string(),
97                execution: Some(exec.clone()),
98                maximum_page_size: 100,
99                // `wait_new_event: true` is what turns this into `tail -f`. Left false
100                // here so the spike terminates.
101                wait_new_event: false,
102                history_event_filter_type: HistoryEventFilterType::AllEvent as i32,
103                ..Default::default()
104            }))
105            .await?
106            .into_inner();
107
108        let events = hist.history.map(|h| h.events).unwrap_or_default();
109        println!(
110            "\nhistory of {} ({} events):",
111            exec.workflow_id,
112            events.len()
113        );
114        for ev in events.iter().take(15) {
115            println!("  {:>4}  {:?}", ev.event_id, ev.event_type());
116        }
117    }
118
119    Ok(())
120}
Source

pub fn operator(&self) -> Box<dyn OperatorService>

Source

pub fn cloud(&self) -> Box<dyn CloudService>

Source

pub fn raw(&self) -> &Client

The high-level client, for the handful of operations where temporalio-client already does the assembly work for us (schedules, workflow handles).

Source§

impl Conn

Source

pub async fn follow_history( &self, namespace: &str, workflow_id: &str, run_id: &str, next_page_token: Vec<u8>, ) -> Result<HistoryPage, OpError>

One long-poll step of follow mode.

This is Conn::get_history with wait_new_event: true, which makes the call block until the workflow does something, up to about a minute, then it returns empty-handed and you call again. Never reach for this outside a task dedicated to following; anything else it is on will simply stop.

The behaviour of the continuation token differs from paging, and follow mode is built on the difference. Measured against a dev server:

wait_new_event: falsewait_new_event: true
running workflow, caught upreturns 0 events, empty tokenblocks, then returns new events, token stays non-empty
closed workflowempty tokenempty token, terminal event last

So an empty token here means the workflow has closed and there is nothing further to follow: the loop’s termination condition, and it is authoritative in a way that inspecting the last event’s type is not.

Passing an empty token restarts from event 1, so a caller resuming a follow should hand back the last non-empty token it saw. The page that token sits in is replayed, which is why events are merged rather than appended, see tmprl_core::history::merge_events.

Source

pub async fn get_history( &self, namespace: &str, workflow_id: &str, run_id: &str, page_size: i32, next_page_token: Vec<u8>, ) -> Result<HistoryPage, OpError>

One page of history, normalised.

wait_new_event is false here. Setting it true turns this into a long poll that does not return until something happens, which is correct for follow mode and a hang everywhere else, so follow mode gets Conn::follow_history rather than a flag on this one that is easy to pass by accident.

Source§

impl Conn

Source

pub async fn mutate(&self, m: &Mutation) -> Result<(), OpError>

Carry out a confirmed mutation.

One entry point rather than four, so the reducer has exactly one place that writes and the audit log has exactly one thing to wrap.

Source§

impl Conn

Source

pub async fn list_namespaces(&self) -> Result<Vec<NamespaceInfo>, OpError>

Every namespace on the cluster, paged to exhaustion.

Namespace counts are small (tens, not thousands), so this collects rather than streaming. Workflow listing will not be able to do that.

Source§

impl Conn

Source

pub async fn list_schedules( &self, namespace: &str, page_size: i32, next_page_token: Vec<u8>, ) -> Result<SchedulePage, OpError>

Source§

impl Conn

Source

pub async fn list_workflows( &self, namespace: &str, query: &str, page_size: i32, next_page_token: Vec<u8>, ) -> Result<WorkflowPage, OpError>

One page of executions matching query, newest first.

This deliberately does not page to exhaustion the way list_namespaces does. A namespace list is tens of rows; a workflow list is unbounded, and draining it would hang the interface on any real cluster.

Source

pub async fn list_workflows_across( &self, namespaces: &[String], query: &str, page_size: i32, ) -> Result<(Vec<WorkflowRow>, Continuation), OpError>

The first page from several namespaces at once, merged newest-first.

Returned alongside the rows is a continuation token per namespace, because the namespaces exhaust at different points and one merged token cannot express that. A namespace that is already finished simply does not appear in the returned list. Feed that list to Conn::continue_workflows_across for the next page.

Source

pub async fn continue_workflows_across( &self, tokens: &Continuation, query: &str, page_size: i32, ) -> Result<(Vec<WorkflowRow>, Continuation), OpError>

The next page, asking only the namespaces that still have one.

Taking the token list rather than the namespace list is the point. Re-deriving the namespaces from the original scope would hand an exhausted namespace an empty token, which the server reads as “start from the beginning”, so it would return page one again, along with a fresh token, and that namespace would never finish.

Source

pub async fn count_workflows_by_status( &self, namespace: &str, query: &str, ) -> Result<StatusCounts, OpError>

Per-status counts for the list header.

One GROUP BY call rather than one call per status. The grouped counts are approximate by design on large clusters (Temporal says so) which is why the total is taken from the response rather than summed from the groups.

Source§

impl Conn

Source

pub async fn count_workflows_across( &self, namespaces: &[String], query: &str, ) -> Result<StatusCounts, OpError>

Header counts summed over a fan-out.

A header that counted only the first of several namespaces would be quietly wrong, which is worse than having no header at all.

Trait Implementations§

Source§

impl Clone for Conn

Source§

fn clone(&self) -> Conn

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more

Auto Trait Implementations§

§

impl !RefUnwindSafe for Conn

§

impl !UnwindSafe for Conn

§

impl Freeze for Conn

§

impl Send for Conn

§

impl Sync for Conn

§

impl Unpin for Conn

§

impl UnsafeUnpin for Conn

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DynClone for T
where T: Clone,

Source§

fn __clone_box(&self, _: Private) -> *mut ()

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoRequest<T> for T

Source§

fn into_request(self) -> Request<T>

Wrap the input message T in a tonic::Request
Source§

impl<L> LayerExt<L> for L

Source§

fn named_layer<S>(&self, service: S) -> Layered<<L as Layer<S>>::Service, S>
where L: Layer<S>,

Applies the layer to a service and wraps it in Layered.
Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more