mz_compute_client/
protocol.rs

1// Copyright Materialize, Inc. and contributors. All rights reserved.
2//
3// Use of this software is governed by the Business Source License
4// included in the LICENSE file.
5//
6// As of the Change Date specified in that file, in accordance with
7// the Business Source License, use of this software will be governed
8// by the Apache License, Version 2.0.
9
10//! The compute protocol defines the communication between the compute controller and individual
11//! compute replicas.
12//!
13//! # Overview
14//!
15//! The compute protocol consists of [`ComputeCommand`]s and [`ComputeResponse`]s.
16//! [`ComputeCommand`]s are sent from the compute controller to the replicas and instruct the
17//! receivers to perform some action. [`ComputeResponse`]s are sent in the opposite direction and
18//! inform the receiver about status changes of their senders.
19//!
20//! The compute protocol is an asynchronous protocol. Both participants must expect and be able to
21//! gracefully handle messages that don’t reflect the state of the world described by the messages
22//! they have previously sent. In other words, messages sent take effect only eventually. For
23//! example, after the compute controller has instructed the replica to cancel a peek (by sending a
24//! [`CancelPeek`] command), it must still be ready to accept non-[`Canceled`] responses to the
25//! peek. Similarly, if a replica receives a [`CancelPeek`] command for a peek it has already
26//! answered, it must handle that command gracefully (e.g., by ignoring it).
27//!
28//! While the protocol does not provide any guarantees about the delay between sending a message
29//! and it being received and processed, it does guarantee that messages are delivered in the same
30//! order they are sent in. For example, if the compute controller sends a [`Peek`] command
31//! followed by a [`CancelPeek`] command, it is guaranteed that [`CancelPeek`] is only received by
32//! the replica after the [`Peek`] command.
33//!
34//! # Message Transport and Encoding
35//!
36//! To initiate communication, the replica starts listening on a known host and port, to which the
37//! compute controller then connects. A Cluster Transport Protocol (CTP) connection is established
38//! that allows both peers to stream bincode-encoded messages.
39//!
40//! # Protocol Stages
41//!
42//! The compute protocol consists of three stages that must be transitioned in order:
43//!
44//!   1. Creation
45//!   2. Initialization
46//!   3. Computation
47//!
48//! ## Creation Stage
49//!
50//! The creation stage is the first stage of the compute protocol. It is initiated by the
51//! successful establishment of a CTP connection between compute controller and replica. In this
52//! stage, the compute controller must send two creation commands in order:
53//!
54//!   1. A [`Hello`] command, which provides the replica with connection metadata.
55//!   2. A [`CreateInstance`] command, which instructs the replica to initialize the rest of its
56//!      state.
57//!
58//! The replica must not send any responses.
59//!
60//! ## Initialization Stage
61//!
62//! The initialization stage begins as soon as the compute controller has sent the
63//! [`CreateInstance`] command. In this stage, the compute controller informs the replica about its
64//! expected dataflow state. It does so by sending any number of [computation
65//! commands](#computation-stage), followed by an [`InitializationComplete`] command, which marks
66//! the end of the initialization stage.
67//!
68//! Upon receiving computation commands during the initialization phase, the replica is obligated
69//! to ensure its state matches what is requested through the commands. It is up to the replica
70//! whether it ensures that by initializing new state or by reusing existing state (through a
71//! reconciliation process).
72//!
73//! The replica may send responses to computation commands it receives. It may also opt to defer
74//! sending responses to the computation stages instead.
75//!
76//! ## Computation Stage
77//!
78//! This computation stage begins as soon as the compute controller has sent the
79//! [`InitializationComplete`] command. In this stage, the compute controller instructs the replica
80//! to create and maintain dataflows, and to perform peeks on indexes exported by dataflows.
81//!
82//! The compute controller may send any number of computation commands:
83//!
84//!   - [`CreateDataflow`]
85//!   - [`Schedule`]
86//!   - [`AllowWrites`]
87//!   - [`AllowCompaction`]
88//!   - [`Peek`]
89//!   - [`CancelPeek`]
90//!   - [`UpdateConfiguration`]
91//!
92//! The compute controller must respect dependencies between commands. For example, it must send a
93//! [`CreateDataflow`] command before it sends [`AllowCompaction`] or [`Peek`] commands that target
94//! the created dataflow.
95//!
96//! The replica must send the required responses to computation commands. This includes commands it
97//! has received in the initialization phase that have not already been responded to.
98//!
99//! # Read-only and read-write dataflows
100//!
101//! All dataflows are initially read-only. While in read-only mode, the dataflow must not affect
102//! changes to external systems (writes). For the most part this means it cannot
103//! write to persist. The dataflow can transition out of read-only mode when
104//! receiving an [`AllowWrites`] command.
105//!
106//! The read-write mode is exactly like the read-only mode, with the
107//! addition that now the dataflow _can_ affect writes to external systems. This mode begins once
108//! the controller has sent the [`AllowWrites`] command for a specific collection.
109//!
110//! A dataflow cannot transition back to read-only mode from read-write mode.
111//!
112//! Note that the controller manages dataflows implicitly by their exports. If a dataflow
113//! had multiple exports, the controller would allow writes for each of them separately.
114//! This is something we may want to revisit in the future if we have dataflows with
115//! multiple exports.
116//!
117//! [`ComputeCommand`]: self::command::ComputeCommand
118//! [`Hello`]: self::command::ComputeCommand::Hello
119//! [`CreateInstance`]: self::command::ComputeCommand::CreateInstance
120//! [`InitializationComplete`]: self::command::ComputeCommand::InitializationComplete
121//! [`CreateDataflow`]: self::command::ComputeCommand::CreateDataflow
122//! [`Schedule`]: self::command::ComputeCommand::Schedule
123//! [`AllowCompaction`]: self::command::ComputeCommand::AllowCompaction
124//! [`AllowWrites`]: self::command::ComputeCommand::AllowWrites
125//! [`Peek`]: self::command::ComputeCommand::Peek
126//! [`CancelPeek`]: self::command::ComputeCommand::CancelPeek
127//! [`UpdateConfiguration`]: self::command::ComputeCommand::UpdateConfiguration
128//! [`ComputeResponse`]: self::response::ComputeResponse
129//! [`Canceled`]: self::response::PeekResponse::Canceled
130//! [`SubscribeResponse::DroppedAt`]: self::response::SubscribeResponse::DroppedAt
131
132pub mod command;
133pub mod history;
134pub mod response;