mz_compute_client/controller/
error.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//! Errors returned by the compute controller.
11//!
12//! The guiding principle for error handling in the compute controller is that each public method
13//! should return an error type that defines exactly the error variants the method can return, and
14//! no additional ones. This precludes the usage of a single `ComputeControllerError` enum that
15//! simply includes the union of all error variants the compute controller can ever return (and
16//! possibly some internal ones that are never returned to external callers). Instead, compute
17//! controller methods return bespoke error types that serve as documentation for the failure modes
18//! of each method and make it easy for callers to ensure that all possible errors are handled.
19
20use mz_repr::GlobalId;
21use thiserror::Error;
22
23pub use mz_storage_types::errors::CollectionMissing;
24
25use crate::controller::instance::InstanceShutDown;
26use crate::controller::{ComputeInstanceId, ReplicaId};
27
28/// The error returned by replica-targeted peeks and subscribes when the target replica
29/// disconnects.
30pub const ERROR_TARGET_REPLICA_FAILED: &str = "target replica failed or was dropped";
31
32/// Error returned in response to a reference to an unknown compute instance.
33#[derive(Error, Debug)]
34#[error("instance does not exist: {0}")]
35pub struct InstanceMissing(pub ComputeInstanceId);
36
37/// Error returned in response to a request to create a compute instance with an ID of an existing
38/// compute instance.
39#[derive(Error, Debug)]
40#[error("instance exists already: {0}")]
41pub struct InstanceExists(pub ComputeInstanceId);
42
43/// Error returned in response to a reference to an unknown compute collection.
44#[derive(Error, Debug)]
45#[error("No replicas found in cluster for target list.")]
46pub struct HydrationCheckBadTarget(pub Vec<ReplicaId>);
47
48/// Errors arising during compute collection lookup.
49#[derive(Error, Debug)]
50pub enum CollectionLookupError {
51    /// The specified compute instance does not exist.
52    #[error("instance does not exist: {0}")]
53    InstanceMissing(ComputeInstanceId),
54    /// The specified compute instance has shut down.
55    #[error("the instance has shut down")]
56    InstanceShutDown,
57    /// The compute collection does not exist.
58    #[error("collection does not exist: {0}")]
59    CollectionMissing(GlobalId),
60}
61
62impl From<InstanceMissing> for CollectionLookupError {
63    fn from(error: InstanceMissing) -> Self {
64        Self::InstanceMissing(error.0)
65    }
66}
67
68impl From<InstanceShutDown> for CollectionLookupError {
69    fn from(_error: InstanceShutDown) -> Self {
70        Self::InstanceShutDown
71    }
72}
73
74impl From<CollectionMissing> for CollectionLookupError {
75    fn from(error: CollectionMissing) -> Self {
76        Self::CollectionMissing(error.0)
77    }
78}
79
80/// Errors arising during compute replica creation.
81#[derive(Error, Debug)]
82pub enum ReplicaCreationError {
83    /// TODO(database-issues#7533): Add documentation.
84    #[error("instance does not exist: {0}")]
85    InstanceMissing(ComputeInstanceId),
86    /// TODO(database-issues#7533): Add documentation.
87    #[error("replica exists already: {0}")]
88    ReplicaExists(ReplicaId),
89}
90
91impl From<InstanceMissing> for ReplicaCreationError {
92    fn from(error: InstanceMissing) -> Self {
93        Self::InstanceMissing(error.0)
94    }
95}
96
97/// Errors arising during compute replica removal.
98#[derive(Error, Debug)]
99pub enum ReplicaDropError {
100    /// TODO(database-issues#7533): Add documentation.
101    #[error("instance does not exist: {0}")]
102    InstanceMissing(ComputeInstanceId),
103    /// TODO(database-issues#7533): Add documentation.
104    #[error("replica does not exist: {0}")]
105    ReplicaMissing(ReplicaId),
106}
107
108impl From<InstanceMissing> for ReplicaDropError {
109    fn from(error: InstanceMissing) -> Self {
110        Self::InstanceMissing(error.0)
111    }
112}
113
114/// Errors arising during dataflow creation.
115#[derive(Error, Debug)]
116pub enum DataflowCreationError {
117    /// The given instance does not exist.
118    #[error("instance does not exist: {0}")]
119    InstanceMissing(ComputeInstanceId),
120    /// One of the imported collections does not exist.
121    #[error("collection does not exist: {0}")]
122    CollectionMissing(GlobalId),
123    /// The targeted replica does not exist.
124    #[error("replica does not exist: {0}")]
125    ReplicaMissing(ReplicaId),
126    /// The dataflow definition has doesn't have an `as_of` set.
127    #[error("dataflow definition lacks an as_of value")]
128    MissingAsOf,
129    /// One of the imported collections has a read frontier greater than the dataflow `as_of`.
130    #[error("dataflow has an as_of not beyond the since of collection: {0}")]
131    SinceViolation(GlobalId),
132    /// We skip dataflow creation for empty `as_of`s, which would be a problem for a SUBSCRIBE,
133    /// because an initial response is expected.
134    #[error("subscribe dataflow has an empty as_of")]
135    EmptyAsOfForSubscribe,
136    /// We skip dataflow creation for empty `as_of`s, which would be a problem for a COPY TO,
137    /// because it should always have an external side effect.
138    #[error("copy to dataflow has an empty as_of")]
139    EmptyAsOfForCopyTo,
140}
141
142impl From<InstanceMissing> for DataflowCreationError {
143    fn from(error: InstanceMissing) -> Self {
144        Self::InstanceMissing(error.0)
145    }
146}
147
148impl From<CollectionMissing> for DataflowCreationError {
149    fn from(error: CollectionMissing) -> Self {
150        Self::CollectionMissing(error.0)
151    }
152}
153
154/// Errors arising during peek processing.
155#[derive(Error, Debug)]
156pub enum PeekError {
157    /// The instance that the peek was issued against does not exist.
158    #[error("instance does not exist: {0}")]
159    InstanceMissing(ComputeInstanceId),
160    /// The peek's target collection was not found.
161    #[error("collection does not exist: {0}")]
162    CollectionMissing(GlobalId),
163    /// The replica that the peek was issued against does not exist.
164    #[error("replica does not exist: {0}")]
165    ReplicaMissing(ReplicaId),
166    /// The read hold that was passed in is for a later time than the peek's timestamp.
167    #[error("peek timestamp is not beyond the since of collection: {0}")]
168    SinceViolation(GlobalId),
169}
170
171impl From<InstanceMissing> for PeekError {
172    fn from(error: InstanceMissing) -> Self {
173        Self::InstanceMissing(error.0)
174    }
175}
176
177impl From<CollectionMissing> for PeekError {
178    fn from(error: CollectionMissing) -> Self {
179        Self::CollectionMissing(error.0)
180    }
181}
182
183/// Errors arising during collection updates.
184#[derive(Error, Debug)]
185pub enum CollectionUpdateError {
186    /// TODO(database-issues#7533): Add documentation.
187    #[error("instance does not exist: {0}")]
188    InstanceMissing(ComputeInstanceId),
189    /// TODO(database-issues#7533): Add documentation.
190    #[error("collection does not exist: {0}")]
191    CollectionMissing(GlobalId),
192}
193
194impl From<InstanceMissing> for CollectionUpdateError {
195    fn from(error: InstanceMissing) -> Self {
196        Self::InstanceMissing(error.0)
197    }
198}
199
200impl From<CollectionMissing> for CollectionUpdateError {
201    fn from(error: CollectionMissing) -> Self {
202        Self::CollectionMissing(error.0)
203    }
204}
205
206/// Errors arising during collection read policy assignment.
207#[derive(Error, Debug)]
208pub enum ReadPolicyError {
209    /// TODO(database-issues#7533): Add documentation.
210    #[error("instance does not exist: {0}")]
211    InstanceMissing(ComputeInstanceId),
212    /// TODO(database-issues#7533): Add documentation.
213    #[error("collection does not exist: {0}")]
214    CollectionMissing(GlobalId),
215    /// TODO(database-issues#7533): Add documentation.
216    #[error("collection is write-only: {0}")]
217    WriteOnlyCollection(GlobalId),
218}
219
220impl From<InstanceMissing> for ReadPolicyError {
221    fn from(error: InstanceMissing) -> Self {
222        Self::InstanceMissing(error.0)
223    }
224}
225
226impl From<CollectionMissing> for ReadPolicyError {
227    fn from(error: CollectionMissing) -> Self {
228        Self::CollectionMissing(error.0)
229    }
230}
231
232/// Errors arising during orphan removal.
233#[derive(Error, Debug)]
234pub enum RemoveOrphansError {
235    /// TODO(database-issues#7533): Add documentation.
236    #[error("orchestrator error: {0}")]
237    OrchestratorError(anyhow::Error),
238}
239
240impl From<anyhow::Error> for RemoveOrphansError {
241    fn from(error: anyhow::Error) -> Self {
242        Self::OrchestratorError(error)
243    }
244}