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
impl Conn
Sourcepub async fn connect(profile: &ProfileRef) -> Result<Self, ConnectError>
pub async fn connect(profile: &ProfileRef) -> Result<Self, ConnectError>
Examples found in repository?
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}Sourcepub fn namespace(&self) -> &str
pub fn namespace(&self) -> &str
Examples found in repository?
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}Sourcepub fn profile(&self) -> &str
pub fn profile(&self) -> &str
Examples found in repository?
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}Sourcepub fn address(&self) -> &str
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?
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}Sourcepub fn wf(&self) -> Box<dyn WorkflowService>
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?
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}pub fn operator(&self) -> Box<dyn OperatorService>
pub fn cloud(&self) -> Box<dyn CloudService>
Source§impl Conn
impl Conn
Sourcepub async fn follow_history(
&self,
namespace: &str,
workflow_id: &str,
run_id: &str,
next_page_token: Vec<u8>,
) -> Result<HistoryPage, OpError>
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: false | wait_new_event: true | |
|---|---|---|
| running workflow, caught up | returns 0 events, empty token | blocks, then returns new events, token stays non-empty |
| closed workflow | empty token | empty 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.
Sourcepub async fn get_history(
&self,
namespace: &str,
workflow_id: &str,
run_id: &str,
page_size: i32,
next_page_token: Vec<u8>,
) -> Result<HistoryPage, OpError>
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
impl Conn
Sourcepub async fn list_namespaces(&self) -> Result<Vec<NamespaceInfo>, OpError>
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
impl Conn
pub async fn list_schedules( &self, namespace: &str, page_size: i32, next_page_token: Vec<u8>, ) -> Result<SchedulePage, OpError>
Source§impl Conn
impl Conn
Sourcepub async fn list_workflows(
&self,
namespace: &str,
query: &str,
page_size: i32,
next_page_token: Vec<u8>,
) -> Result<WorkflowPage, OpError>
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.
Sourcepub async fn list_workflows_across(
&self,
namespaces: &[String],
query: &str,
page_size: i32,
) -> Result<(Vec<WorkflowRow>, Continuation), OpError>
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.
Sourcepub async fn continue_workflows_across(
&self,
tokens: &Continuation,
query: &str,
page_size: i32,
) -> Result<(Vec<WorkflowRow>, Continuation), OpError>
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.
Sourcepub async fn count_workflows_by_status(
&self,
namespace: &str,
query: &str,
) -> Result<StatusCounts, OpError>
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
impl Conn
Sourcepub async fn count_workflows_across(
&self,
namespaces: &[String],
query: &str,
) -> Result<StatusCounts, OpError>
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§
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> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoRequest<T> for T
impl<T> IntoRequest<T> for T
Source§fn into_request(self) -> Request<T>
fn into_request(self) -> Request<T>
T in a tonic::Request