Skip to main content

datum_agent/dcp/
proto.rs

1use std::collections::HashMap;
2
3use prost::Message as ProstMessage;
4
5/// DCP protocol version implemented by this crate.
6///
7/// Minor-version bumps are additive within one DCP major: new request variants
8/// and payload fields may be added without changing the major. Servers keep
9/// rejecting unknown major versions during `Hello` negotiation.
10pub const DCP_PROTOCOL_VERSION: &str = "0.10.0";
11
12/// DCP major version accepted by this crate.
13pub const DCP_PROTOCOL_MAJOR: u32 = 0;
14
15/// Client role advertised in the initial DCP hello.
16#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, prost::Enumeration)]
17#[repr(i32)]
18pub enum ClientKind {
19    Unspecified = 0,
20    Cli = 1,
21    Tui = 2,
22    Daemon = 3,
23    ClusterNode = 4,
24}
25
26/// Response status for unary DCP requests and hello negotiation.
27#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, prost::Enumeration)]
28#[repr(i32)]
29pub enum ResponseStatus {
30    Ok = 0,
31    BadRequest = 1,
32    Unauthorized = 2,
33    NotFound = 3,
34    Conflict = 4,
35    Failed = 5,
36    ProtocolMismatch = 6,
37    DeadlineExceeded = 7,
38}
39
40/// Authentication data carried in the hello. Remote deployments are expected to
41/// rely on QUIC mTLS first; this is an optional operator token hook.
42#[derive(Clone, PartialEq, ProstMessage)]
43pub struct Auth {
44    #[prost(string, tag = "1")]
45    pub bearer_token: String,
46}
47
48/// First client-to-server frame on every DCP stream.
49#[derive(Clone, PartialEq, ProstMessage)]
50pub struct Hello {
51    #[prost(string, tag = "1")]
52    pub protocol_version: String,
53    #[prost(string, tag = "2")]
54    pub node_id: String,
55    #[prost(enumeration = "ClientKind", tag = "3")]
56    pub client_kind: i32,
57    #[prost(message, optional, tag = "4")]
58    pub auth: Option<Auth>,
59    #[prost(string, repeated, tag = "5")]
60    pub capabilities: Vec<String>,
61}
62
63impl Hello {
64    #[must_use]
65    pub fn new(node_id: impl Into<String>, client_kind: ClientKind) -> Self {
66        Self {
67            protocol_version: DCP_PROTOCOL_VERSION.to_owned(),
68            node_id: node_id.into(),
69            client_kind: client_kind as i32,
70            auth: None,
71            capabilities: Vec::new(),
72        }
73    }
74}
75
76/// Unary or subscription request.
77#[derive(Clone, PartialEq, ProstMessage)]
78pub struct Request {
79    #[prost(uint64, tag = "1")]
80    pub request_id: u64,
81    #[prost(uint64, tag = "2")]
82    pub deadline_ms: u64,
83    #[prost(
84        oneof = "request::Command",
85        tags = "10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30"
86    )]
87    pub command: Option<request::Command>,
88}
89
90pub mod request {
91    #[derive(Clone, PartialEq, prost::Oneof)]
92    pub enum Command {
93        #[prost(message, tag = "10")]
94        ListJobs(super::ListJobs),
95        #[prost(message, tag = "11")]
96        StartJob(super::StartJob),
97        #[prost(message, tag = "12")]
98        DrainJob(super::DrainJob),
99        #[prost(message, tag = "13")]
100        StopJob(super::StopJob),
101        #[prost(message, tag = "14")]
102        RestartJob(super::RestartJob),
103        #[prost(message, tag = "15")]
104        JobStatus(super::JobStatusRequest),
105        #[prost(message, tag = "16")]
106        SubscribeEvents(super::SubscribeEvents),
107        #[prost(message, tag = "17")]
108        SubscribeMetrics(super::SubscribeMetrics),
109        #[prost(message, tag = "18")]
110        GetConfig(super::GetConfig),
111        #[prost(message, tag = "19")]
112        PutConfig(super::PutConfig),
113        #[prost(message, tag = "20")]
114        ListClusterJobs(super::ListClusterJobs),
115        #[prost(message, tag = "21")]
116        ClusterNodeInfo(super::ClusterNodeInfo),
117        #[prost(message, tag = "22")]
118        SubmitClusterJob(super::SubmitClusterJob),
119        #[prost(message, tag = "23")]
120        RememberClusterAssignment(super::RememberClusterAssignment),
121        #[prost(message, tag = "24")]
122        AllocateShard(super::ShardAllocationRequest),
123        #[prost(message, tag = "25")]
124        RememberShardAllocations(super::RememberShardAllocations),
125        #[prost(message, tag = "26")]
126        GetShardAllocations(super::GetShardAllocations),
127        #[prost(message, tag = "27")]
128        ForwardShardEnvelopes(super::ForwardShardEnvelopes),
129        #[prost(message, tag = "28")]
130        CompleteShardingAsk(super::CompleteShardingAsk),
131        #[prost(message, tag = "29")]
132        OpenShardPipe(super::OpenShardPipe),
133        #[prost(message, tag = "30")]
134        UnsubscribeMetrics(super::UnsubscribeMetrics),
135    }
136}
137
138#[derive(Clone, PartialEq, ProstMessage)]
139pub struct ListJobs {}
140
141/// StartJob uses the v0.9.1 registered-factory model.
142///
143/// Dynamic blueprint upload is intentionally out of scope until cluster
144/// placement work in v0.10.
145#[derive(Clone, PartialEq, ProstMessage)]
146pub struct StartJob {
147    #[prost(string, tag = "1")]
148    pub factory_name: String,
149    #[prost(string, tag = "2")]
150    pub instance_name: String,
151    #[prost(map = "string, string", tag = "3")]
152    pub params: HashMap<String, String>,
153    #[prost(message, optional, tag = "4")]
154    pub cluster: Option<ClusterJobStart>,
155}
156
157#[derive(Clone, PartialEq, ProstMessage)]
158pub struct DrainJob {
159    #[prost(string, tag = "1")]
160    pub name: String,
161    #[prost(bool, tag = "2")]
162    pub cluster: bool,
163}
164
165#[derive(Clone, PartialEq, ProstMessage)]
166pub struct StopJob {
167    #[prost(string, tag = "1")]
168    pub name: String,
169    #[prost(bool, tag = "2")]
170    pub cluster: bool,
171}
172
173#[derive(Clone, PartialEq, ProstMessage)]
174pub struct RestartJob {
175    #[prost(string, tag = "1")]
176    pub name: String,
177    #[prost(bool, tag = "2")]
178    pub cluster: bool,
179}
180
181#[derive(Clone, PartialEq, ProstMessage)]
182pub struct JobStatusRequest {
183    #[prost(string, tag = "1")]
184    pub name: String,
185    #[prost(bool, tag = "2")]
186    pub cluster: bool,
187}
188
189#[derive(Clone, PartialEq, ProstMessage)]
190pub struct SubscribeEvents {
191    #[prost(uint64, tag = "1")]
192    pub buffer: u64,
193}
194
195#[derive(Clone, PartialEq, ProstMessage)]
196pub struct SubscribeMetrics {
197    #[prost(uint64, tag = "1")]
198    pub interval_ms: u64,
199    /// Restrict samples to instrumentation names owned by these jobs. Empty
200    /// means every local stream.
201    #[prost(string, repeated, tag = "2")]
202    pub job_names: Vec<String>,
203    /// Sample only this server's instrumentation registry. Cluster node
204    /// sessions set this to prevent recursive proxying.
205    #[prost(bool, tag = "3")]
206    pub local_only: bool,
207}
208
209#[derive(Clone, PartialEq, ProstMessage)]
210pub struct UnsubscribeMetrics {
211    #[prost(uint64, tag = "1")]
212    pub subscription_id: u64,
213}
214
215#[derive(Clone, PartialEq, ProstMessage)]
216pub struct GetConfig {
217    #[prost(string, tag = "1")]
218    pub key: String,
219}
220
221#[derive(Clone, PartialEq, ProstMessage)]
222pub struct PutConfig {
223    #[prost(string, tag = "1")]
224    pub key: String,
225    #[prost(string, tag = "2")]
226    pub value: String,
227}
228
229/// Aggregate job snapshots across the cluster.
230///
231/// The server fans out to currently known peer node sessions and returns
232/// partial results with per-node errors when a peer is unreachable or the
233/// bounded wait expires.
234#[derive(Clone, PartialEq, ProstMessage)]
235pub struct ListClusterJobs {
236    /// Per-peer fan-out timeout in milliseconds. A zero value uses the server
237    /// default.
238    #[prost(uint64, tag = "1")]
239    pub timeout_ms: u64,
240}
241
242/// Return the local node's cluster membership and DCP session view.
243#[derive(Clone, PartialEq, ProstMessage)]
244pub struct ClusterNodeInfo {
245    /// Snapshot collection timeout in milliseconds. A zero value uses the
246    /// server default.
247    #[prost(uint64, tag = "1")]
248    pub timeout_ms: u64,
249}
250
251/// Submit a registered-factory job to the placement coordinator.
252#[derive(Clone, PartialEq, ProstMessage)]
253pub struct SubmitClusterJob {
254    #[prost(string, tag = "1")]
255    pub factory_name: String,
256    #[prost(string, tag = "2")]
257    pub instance_name: String,
258    #[prost(map = "string, string", tag = "3")]
259    pub params: HashMap<String, String>,
260    #[prost(message, optional, tag = "4")]
261    pub placement: Option<PlacementSpec>,
262    /// Coordinator/placement timeout in milliseconds. A zero value uses the
263    /// server default.
264    #[prost(uint64, tag = "5")]
265    pub timeout_ms: u64,
266}
267
268/// Internal best-effort replication of coordinator assignment state to peers.
269/// A present assignment upserts intent; an absent assignment removes it.
270#[derive(Clone, PartialEq, ProstMessage)]
271pub struct RememberClusterAssignment {
272    #[prost(string, tag = "1")]
273    pub instance_name: String,
274    #[prost(message, optional, tag = "2")]
275    pub assignment: Option<ClusterJobStart>,
276}
277
278/// Request the coordinator allocation for one shard.
279#[derive(Clone, PartialEq, ProstMessage)]
280pub struct ShardAllocationRequest {
281    #[prost(string, tag = "1")]
282    pub type_name: String,
283    #[prost(string, tag = "2")]
284    pub shard_id: String,
285    /// Coordinator timeout in milliseconds. A zero value uses the server
286    /// default.
287    #[prost(uint64, tag = "3")]
288    pub timeout_ms: u64,
289}
290
291/// Coordinator answer for one shard home.
292#[derive(Clone, PartialEq, ProstMessage)]
293pub struct ShardAllocation {
294    #[prost(string, tag = "1")]
295    pub type_name: String,
296    #[prost(string, tag = "2")]
297    pub shard_id: String,
298    #[prost(string, tag = "3")]
299    pub node_id: String,
300    #[prost(uint64, tag = "4")]
301    pub generation: u64,
302}
303
304/// Allocation-table row replicated to peer regions.
305#[derive(Clone, PartialEq, ProstMessage)]
306pub struct ShardAllocationEntry {
307    #[prost(string, tag = "1")]
308    pub type_name: String,
309    #[prost(string, tag = "2")]
310    pub shard_id: String,
311    #[prost(string, tag = "3")]
312    pub node_id: String,
313    #[prost(uint64, tag = "4")]
314    pub generation: u64,
315}
316
317/// Allocation-table snapshot. v0.10 keeps this in memory; persistence and
318/// remember-entities belong to later sharding work packages.
319#[derive(Clone, PartialEq, ProstMessage)]
320pub struct ShardAllocationTable {
321    #[prost(message, repeated, tag = "1")]
322    pub entries: Vec<ShardAllocationEntry>,
323}
324
325/// Best-effort allocation-table replication from the active coordinator.
326#[derive(Clone, PartialEq, ProstMessage)]
327pub struct RememberShardAllocations {
328    #[prost(message, optional, tag = "1")]
329    pub table: Option<ShardAllocationTable>,
330}
331
332/// Ask a peer region for its cached allocation table.
333#[derive(Clone, PartialEq, ProstMessage)]
334pub struct GetShardAllocations {
335    #[prost(string, tag = "1")]
336    pub type_name: String,
337}
338
339/// One serialized entity envelope carried between peer regions.
340#[derive(Clone, PartialEq, ProstMessage)]
341pub struct ShardEnvelopeWire {
342    #[prost(string, tag = "1")]
343    pub entity_id: String,
344    #[prost(string, tag = "2")]
345    pub shard_id: String,
346    #[prost(bytes = "vec", tag = "3")]
347    pub payload: Vec<u8>,
348}
349
350/// Batched peer-region forwarding on the existing C2 session.
351#[derive(Clone, PartialEq, ProstMessage)]
352pub struct ForwardShardEnvelopes {
353    #[prost(string, tag = "1")]
354    pub type_name: String,
355    #[prost(message, repeated, tag = "2")]
356    pub envelopes: Vec<ShardEnvelopeWire>,
357}
358
359/// Per-envelope enqueue result for a forwarded batch.
360#[derive(Clone, PartialEq, ProstMessage)]
361pub struct ShardEnvelopeAck {
362    #[prost(string, tag = "1")]
363    pub entity_id: String,
364    #[prost(string, tag = "2")]
365    pub shard_id: String,
366    #[prost(bool, tag = "3")]
367    pub ok: bool,
368    #[prost(string, tag = "4")]
369    pub message: String,
370}
371
372/// Result for a peer-region forwarding batch.
373#[derive(Clone, PartialEq, ProstMessage)]
374pub struct ShardEnvelopeBatchResult {
375    #[prost(message, repeated, tag = "1")]
376    pub acks: Vec<ShardEnvelopeAck>,
377}
378
379/// Complete an ask whose reply port originated on another region.
380#[derive(Clone, PartialEq, ProstMessage)]
381pub struct CompleteShardingAsk {
382    #[prost(uint64, tag = "1")]
383    pub request_id: u64,
384    #[prost(bool, tag = "2")]
385    pub ok: bool,
386    #[prost(bytes = "vec", tag = "3")]
387    pub payload: Vec<u8>,
388    #[prost(string, tag = "4")]
389    pub message: String,
390}
391
392/// Switch this DCP connection to the sharding hot-path pipe after hello.
393#[derive(Clone, PartialEq, ProstMessage)]
394pub struct OpenShardPipe {}
395
396/// One sharding hot-path pipe frame.
397///
398/// Frames are one-way and ordered per peer connection. The sender batches only
399/// when its local pipe queue already has backlog; idle sends are flushed
400/// immediately so p50 does not pay a linger tax.
401#[derive(Clone, PartialEq, ProstMessage)]
402pub struct ShardPipeFrame {
403    #[prost(message, repeated, tag = "1")]
404    pub forwards: Vec<ForwardShardEnvelopes>,
405    #[prost(message, repeated, tag = "2")]
406    pub replies: Vec<CompleteShardingAsk>,
407}
408
409#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, prost::Enumeration)]
410#[repr(i32)]
411pub enum PlacementStrategy {
412    LeastJobs = 0,
413    Pinned = 1,
414}
415
416#[derive(Clone, PartialEq, ProstMessage)]
417pub struct PlacementSpec {
418    #[prost(string, tag = "1")]
419    pub role_constraint: String,
420    #[prost(enumeration = "PlacementStrategy", tag = "2")]
421    pub strategy: i32,
422    #[prost(string, tag = "3")]
423    pub pinned_node_id: String,
424}
425
426#[derive(Clone, PartialEq, ProstMessage)]
427pub struct ClusterPlacementHistory {
428    #[prost(uint64, tag = "1")]
429    pub generation: u64,
430    #[prost(string, tag = "2")]
431    pub from_node_id: String,
432    #[prost(string, tag = "3")]
433    pub to_node_id: String,
434    #[prost(string, tag = "4")]
435    pub reason: String,
436    #[prost(uint64, tag = "5")]
437    pub timestamp_ms: u64,
438}
439
440/// Cluster metadata carried on coordinator-issued StartJob.
441#[derive(Clone, PartialEq, ProstMessage)]
442pub struct ClusterJobStart {
443    #[prost(string, tag = "1")]
444    pub factory_name: String,
445    #[prost(map = "string, string", tag = "2")]
446    pub params: HashMap<String, String>,
447    #[prost(message, optional, tag = "3")]
448    pub placement: Option<PlacementSpec>,
449    #[prost(string, tag = "4")]
450    pub coordinator_node_id: String,
451    #[prost(string, tag = "5")]
452    pub assigned_node_id: String,
453    #[prost(uint64, tag = "6")]
454    pub placement_generation: u64,
455    #[prost(message, repeated, tag = "7")]
456    pub history: Vec<ClusterPlacementHistory>,
457}
458
459#[derive(Clone, PartialEq, ProstMessage)]
460pub struct Response {
461    #[prost(uint64, tag = "1")]
462    pub request_id: u64,
463    #[prost(enumeration = "ResponseStatus", tag = "2")]
464    pub status: i32,
465    #[prost(string, tag = "3")]
466    pub message: String,
467    #[prost(bytes = "vec", tag = "4")]
468    pub payload: Vec<u8>,
469}
470
471impl Response {
472    #[must_use]
473    pub fn ok(request_id: u64, payload: Vec<u8>) -> Self {
474        Self {
475            request_id,
476            status: ResponseStatus::Ok as i32,
477            message: String::new(),
478            payload,
479        }
480    }
481
482    #[must_use]
483    pub fn error(request_id: u64, status: ResponseStatus, message: impl Into<String>) -> Self {
484        Self {
485            request_id,
486            status: status as i32,
487            message: message.into(),
488            payload: Vec::new(),
489        }
490    }
491
492    #[must_use]
493    pub fn response_status(&self) -> ResponseStatus {
494        ResponseStatus::try_from(self.status).unwrap_or(ResponseStatus::Failed)
495    }
496}
497
498#[derive(Clone, PartialEq, ProstMessage)]
499pub struct JobList {
500    #[prost(message, repeated, tag = "1")]
501    pub jobs: Vec<JobStatus>,
502}
503
504/// Cluster-wide job list response.
505#[derive(Clone, PartialEq, ProstMessage)]
506pub struct ClusterJobList {
507    #[prost(message, repeated, tag = "1")]
508    pub nodes: Vec<ClusterJobNode>,
509    #[prost(message, repeated, tag = "2")]
510    pub errors: Vec<ClusterNodeError>,
511    #[prost(bool, tag = "3")]
512    pub partial: bool,
513}
514
515/// Jobs reported by one cluster node.
516#[derive(Clone, PartialEq, ProstMessage)]
517pub struct ClusterJobNode {
518    #[prost(string, tag = "1")]
519    pub node_id: String,
520    #[prost(string, tag = "2")]
521    pub address: String,
522    #[prost(bool, tag = "3")]
523    pub local: bool,
524    #[prost(message, repeated, tag = "4")]
525    pub jobs: Vec<JobStatus>,
526}
527
528/// Cluster node information response.
529#[derive(Clone, PartialEq, ProstMessage)]
530pub struct ClusterNodeList {
531    #[prost(message, repeated, tag = "1")]
532    pub nodes: Vec<ClusterNodeStatus>,
533    #[prost(message, repeated, tag = "2")]
534    pub errors: Vec<ClusterNodeError>,
535    #[prost(bool, tag = "3")]
536    pub partial: bool,
537    #[prost(string, tag = "4")]
538    pub coordinator_node_id: String,
539}
540
541/// Membership and DCP session state for one node.
542#[derive(Clone, PartialEq, ProstMessage)]
543pub struct ClusterNodeStatus {
544    #[prost(string, tag = "1")]
545    pub node_id: String,
546    #[prost(string, tag = "2")]
547    pub member_state: String,
548    #[prost(string, tag = "3")]
549    pub address: String,
550    #[prost(string, tag = "4")]
551    pub agent_addr: String,
552    #[prost(string, repeated, tag = "5")]
553    pub roles: Vec<String>,
554    #[prost(bool, tag = "6")]
555    pub unreachable: bool,
556    #[prost(bool, tag = "7")]
557    pub local: bool,
558    #[prost(string, tag = "8")]
559    pub session_state: String,
560}
561
562/// Per-node partial-result error for cluster fan-out requests.
563#[derive(Clone, PartialEq, ProstMessage)]
564pub struct ClusterNodeError {
565    #[prost(string, tag = "1")]
566    pub node_id: String,
567    #[prost(string, tag = "2")]
568    pub message: String,
569}
570
571#[derive(Clone, PartialEq, ProstMessage)]
572pub struct ConfigValue {
573    #[prost(string, tag = "1")]
574    pub key: String,
575    #[prost(string, tag = "2")]
576    pub value: String,
577    #[prost(bool, tag = "3")]
578    pub existed: bool,
579}
580
581#[derive(Clone, PartialEq, ProstMessage)]
582pub struct JobStatus {
583    #[prost(string, tag = "1")]
584    pub name: String,
585    #[prost(uint64, tag = "2")]
586    pub job_id: u64,
587    #[prost(string, tag = "3")]
588    pub state: String,
589    #[prost(string, tag = "4")]
590    pub desired_state: String,
591    #[prost(uint64, tag = "5")]
592    pub generation: u64,
593    #[prost(uint64, tag = "6")]
594    pub starts_total: u64,
595    #[prost(uint64, tag = "7")]
596    pub restarts_total: u64,
597    #[prost(uint64, optional, tag = "8")]
598    pub last_start_at_ms: Option<u64>,
599    #[prost(uint64, optional, tag = "9")]
600    pub last_exit_at_ms: Option<u64>,
601    #[prost(string, tag = "10")]
602    pub last_exit_reason: String,
603    #[prost(uint64, optional, tag = "11")]
604    pub backoff_remaining_ms: Option<u64>,
605    #[prost(uint64, optional, tag = "12")]
606    pub drain_remaining_ms: Option<u64>,
607    #[prost(bool, tag = "13")]
608    pub drain_supported: bool,
609    #[prost(uint64, optional, tag = "14")]
610    pub active_streams: Option<u64>,
611    #[prost(bool, tag = "15")]
612    pub cluster_job: bool,
613    #[prost(string, tag = "16")]
614    pub factory_name: String,
615    #[prost(message, optional, tag = "17")]
616    pub placement: Option<PlacementSpec>,
617    #[prost(string, tag = "18")]
618    pub coordinator_node_id: String,
619    #[prost(string, tag = "19")]
620    pub placement_node_id: String,
621    #[prost(uint64, tag = "20")]
622    pub placement_generation: u64,
623    #[prost(message, repeated, tag = "21")]
624    pub placement_history: Vec<ClusterPlacementHistory>,
625    #[prost(map = "string, string", tag = "22")]
626    pub params: HashMap<String, String>,
627}
628
629#[derive(Clone, PartialEq, ProstMessage)]
630pub struct EventFrame {
631    #[prost(uint64, tag = "1")]
632    pub subscription_id: u64,
633    #[prost(message, optional, tag = "2")]
634    pub event: Option<Event>,
635}
636
637#[derive(Clone, PartialEq, ProstMessage)]
638pub struct Event {
639    #[prost(uint64, tag = "1")]
640    pub sequence: u64,
641    #[prost(uint64, tag = "2")]
642    pub timestamp_ms: u64,
643    #[prost(string, tag = "3")]
644    pub name: String,
645    #[prost(uint64, tag = "4")]
646    pub job_id: u64,
647    #[prost(uint64, tag = "5")]
648    pub generation: u64,
649    #[prost(string, tag = "6")]
650    pub kind: String,
651    #[prost(string, tag = "7")]
652    pub detail: String,
653}
654
655#[derive(Clone, PartialEq, ProstMessage)]
656pub struct MetricFrame {
657    #[prost(uint64, tag = "1")]
658    pub subscription_id: u64,
659    #[prost(message, optional, tag = "2")]
660    pub sample: Option<MetricSample>,
661}
662
663#[derive(Clone, PartialEq, ProstMessage)]
664pub struct MetricSample {
665    #[prost(uint64, tag = "1")]
666    pub timestamp_ms: u64,
667    #[prost(message, repeated, tag = "2")]
668    pub streams: Vec<StreamMetric>,
669}
670
671#[derive(Clone, PartialEq, ProstMessage)]
672pub struct StreamMetric {
673    #[prost(uint64, tag = "1")]
674    pub id: u64,
675    #[prost(string, tag = "2")]
676    pub name: String,
677    #[prost(uint64, tag = "3")]
678    pub elements_through: u64,
679    #[prost(uint64, tag = "4")]
680    pub restarts: u64,
681    #[prost(string, tag = "5")]
682    pub state: String,
683    #[prost(uint64, tag = "6")]
684    pub started_at_ms: u64,
685    #[prost(uint64, tag = "7")]
686    pub state_changed_at_ms: u64,
687    #[prost(uint64, optional, tag = "8")]
688    pub finished_at_ms: Option<u64>,
689    #[prost(uint64, tag = "9")]
690    pub uptime_ms: u64,
691}
692
693#[derive(Clone, PartialEq, ProstMessage)]
694pub struct DcpFrame {
695    #[prost(oneof = "dcp_frame::Frame", tags = "1, 2, 3, 4, 5, 6")]
696    pub frame: Option<dcp_frame::Frame>,
697}
698
699impl DcpFrame {
700    #[must_use]
701    pub fn hello(hello: Hello) -> Self {
702        Self {
703            frame: Some(dcp_frame::Frame::Hello(hello)),
704        }
705    }
706
707    #[must_use]
708    pub fn request(request: Request) -> Self {
709        Self {
710            frame: Some(dcp_frame::Frame::Request(request)),
711        }
712    }
713
714    #[must_use]
715    pub fn response(response: Response) -> Self {
716        Self {
717            frame: Some(dcp_frame::Frame::Response(response)),
718        }
719    }
720
721    #[must_use]
722    pub fn event(subscription_id: u64, event: Event) -> Self {
723        Self {
724            frame: Some(dcp_frame::Frame::Event(EventFrame {
725                subscription_id,
726                event: Some(event),
727            })),
728        }
729    }
730
731    #[must_use]
732    pub fn metric(subscription_id: u64, sample: MetricSample) -> Self {
733        Self {
734            frame: Some(dcp_frame::Frame::Metric(MetricFrame {
735                subscription_id,
736                sample: Some(sample),
737            })),
738        }
739    }
740
741    #[must_use]
742    pub fn shard_pipe(pipe: ShardPipeFrame) -> Self {
743        Self {
744            frame: Some(dcp_frame::Frame::ShardPipe(pipe)),
745        }
746    }
747}
748
749pub mod dcp_frame {
750    #[allow(clippy::large_enum_variant)]
751    #[derive(Clone, PartialEq, prost::Oneof)]
752    pub enum Frame {
753        #[prost(message, tag = "1")]
754        Hello(super::Hello),
755        #[prost(message, tag = "2")]
756        Request(super::Request),
757        #[prost(message, tag = "3")]
758        Response(super::Response),
759        #[prost(message, tag = "4")]
760        Event(super::EventFrame),
761        #[prost(message, tag = "5")]
762        Metric(super::MetricFrame),
763        #[prost(message, tag = "6")]
764        ShardPipe(super::ShardPipeFrame),
765    }
766}