1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
// Copyright Materialize, Inc. and contributors. All rights reserved.
//
// Use of this software is governed by the Business Source License
// included in the LICENSE file.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0.

//! Persistence related errors.

use std::sync::Arc;
use std::{error, fmt, io, sync};

use crate::location::{ExternalError, SeqNo};

/// A persistence related error.
#[derive(Debug, Clone)]
pub enum Error {
    /// A persistence related error resulting from an io failure.
    IO(Arc<io::Error>),
    /// An operation failed because storage was out of space or quota.
    OutOfQuota(String),
    /// An unstructured persistence related error.
    String(String),
    /// There is no stream registered under the given name.
    UnknownRegistration(String),
    /// The associated write request was sequenced (given a SeqNo) and applied
    /// to the persist state machine, but that application was deterministically
    /// made into a no-op because it was contextually invalid (a write or seal
    /// at a sealed timestamp, an allow_compactions at an unsealed timestamp,
    /// etc).
    Noop(SeqNo, String),
    /// An error returned when a command is sent to a persistence runtime that
    /// was previously stopped.
    RuntimeShutdown,
}

impl error::Error for Error {}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Error::IO(e) => fmt::Display::fmt(e, f),
            Error::OutOfQuota(e) => f.write_str(e),
            Error::String(e) => f.write_str(e),
            Error::UnknownRegistration(id) => write!(f, "unknown registration: {}", id),
            Error::Noop(_, e) => f.write_str(e),
            Error::RuntimeShutdown => f.write_str("runtime shutdown"),
        }
    }
}

// Hack so we can debug_assert_eq against Result<(), Error>.
impl PartialEq for Error {
    fn eq(&self, other: &Self) -> bool {
        match (self, other) {
            (Error::String(s), Error::String(o)) => s == o,
            (Error::OutOfQuota(s), Error::OutOfQuota(o)) => s == o,
            (Error::UnknownRegistration(s), Error::UnknownRegistration(o)) => s == o,
            (Error::RuntimeShutdown, Error::RuntimeShutdown) => true,
            _ => false,
        }
    }
}

impl From<ExternalError> for Error {
    fn from(e: ExternalError) -> Self {
        // Print the chain of causes along with each error by default.
        Error::String(format!("{e:#}"))
    }
}

impl From<io::Error> for Error {
    fn from(e: io::Error) -> Self {
        // This only works on unix/macos, but I don't see a great alternative.
        if let Some(28) = e.raw_os_error() {
            return Error::OutOfQuota(e.to_string());
        }
        Error::IO(Arc::new(e))
    }
}

impl From<String> for Error {
    fn from(e: String) -> Self {
        Error::String(e)
    }
}

impl<'a> From<&'a str> for Error {
    fn from(e: &'a str) -> Self {
        Error::String(e.into())
    }
}

impl From<parquet::errors::ParquetError> for Error {
    fn from(e: parquet::errors::ParquetError) -> Self {
        Error::String(e.to_string())
    }
}

impl From<arrow::error::ArrowError> for Error {
    fn from(e: arrow::error::ArrowError) -> Self {
        Error::String(e.to_string())
    }
}

impl<T> From<sync::PoisonError<T>> for Error {
    fn from(e: sync::PoisonError<T>) -> Self {
        Error::String(format!("poison: {}", e))
    }
}