timeseries_table_format/table/operations/append/
error.rs1use arrow::error::ArrowError;
4use parquet::errors::ParquetError;
5use snafu::{Backtrace, Snafu};
6
7use crate::{
8 coverage::{
9 EntityIdentity, IndexIntervalId,
10 index_interval::{IndexInterval, IndexIntervalMappingError},
11 io::CoverageSidecarError,
12 },
13 formats::parquet::SegmentCoverageError,
14 metadata::{
15 logical_schema::ArrowToLogicalSchemaError, protocol::TableProtocolError,
16 schema_compat::SchemaCompatibilityError,
17 },
18 storage::StorageError,
19 transaction_log::{CommitError, segments::SegmentError},
20};
21
22#[derive(Debug, Snafu)]
24#[snafu(visibility(pub(crate)))]
25#[non_exhaustive]
26pub enum AppendError {
27 #[snafu(context(false), display("Table protocol error: {source}"))]
29 Protocol {
30 #[snafu(source)]
32 source: TableProtocolError,
33 backtrace: Backtrace,
35 },
36
37 #[snafu(display("Arrow input error: {source}"))]
39 ArrowInput {
40 #[snafu(source)]
42 source: ArrowError,
43 backtrace: Backtrace,
45 },
46
47 #[snafu(display("Invalid append input schema: {source}"))]
49 ArrowToLogicalSchema {
50 #[snafu(source)]
52 source: ArrowToLogicalSchemaError,
53 backtrace: Backtrace,
55 },
56
57 #[snafu(display("Cannot append an empty Arrow input"))]
59 EmptyInput,
60
61 #[snafu(display(
63 "Invalid maximum rows per Parquet row group: {max_rows_per_row_group}; expected a positive value"
64 ))]
65 InvalidMaxRowsPerRowGroup {
66 max_rows_per_row_group: usize,
68 },
69
70 #[snafu(context(false), display("Schema validation failed: {source}"))]
72 SchemaValidation {
73 #[snafu(source(from(SchemaCompatibilityError, Box::new)), backtrace)]
75 source: Box<SchemaCompatibilityError>,
76 },
77
78 #[snafu(display(
80 "Generated segment {segment_path} is incompatible with the table schema: {source}"
81 ))]
82 GeneratedSegmentSchemaCompatibility {
83 segment_path: String,
85 #[snafu(source(from(SchemaCompatibilityError, Box::new)), backtrace)]
87 source: Box<SchemaCompatibilityError>,
88 },
89
90 #[snafu(display(
92 "Table has no logical_schema at version {version}; cannot append without a canonical schema"
93 ))]
94 MissingCanonicalTableSchema {
95 version: u64,
97 },
98
99 #[snafu(context(false), display("Generated segment metadata error: {source}"))]
101 SegmentMetadata {
102 #[snafu(source(from(SegmentError, Box::new)), backtrace)]
104 source: Box<SegmentError>,
105 },
106
107 #[snafu(display("Parquet write error: {source}"))]
109 ParquetWrite {
110 #[snafu(source)]
112 source: ParquetError,
113 backtrace: Backtrace,
115 },
116
117 #[snafu(context(false), display("Segment coverage error: {source}"))]
119 GeneratedSegmentCoverage {
120 #[snafu(source(from(SegmentCoverageError, Box::new)), backtrace)]
122 source: Box<SegmentCoverageError>,
123 },
124
125 #[snafu(context(false), display("Index interval mapping failed: {source}"))]
127 IndexIntervalMapping {
128 #[snafu(source)]
130 source: IndexIntervalMappingError,
131 backtrace: Backtrace,
133 },
134
135 #[snafu(context(false), display("Coverage sidecar error: {source}"))]
137 CoverageSidecar {
138 #[snafu(source(from(CoverageSidecarError, Box::new)), backtrace)]
140 source: Box<CoverageSidecarError>,
141 },
142
143 #[snafu(display(
145 "Ordered-index interval overlap while appending {segment_path}: {overlap_count} overlapping identity/index interval pairs (example_identity={example_identity:?}, example_index_interval={example_index_interval})"
146 ))]
147 PersistedIndexIntervalOverlap {
148 segment_path: String,
150 overlap_count: u128,
152 example_identity: Option<EntityIdentity>,
154 example_index_interval_id: IndexIntervalId,
156 example_index_interval: Box<IndexInterval>,
158 },
159
160 #[snafu(display("No entity coverage derived while appending segment {segment_path}"))]
162 EmptySegmentEntityCoverage {
163 segment_path: String,
165 },
166
167 #[snafu(display(
169 "Entity {identity:?} in segment {segment_path} has no non-null ordered-index values"
170 ))]
171 EntityWithoutIndexCoverage {
172 segment_path: String,
174 identity: EntityIdentity,
176 },
177
178 #[snafu(display(
180 "Cannot append because existing segment {segment_path} is missing coverage_path"
181 ))]
182 ExistingSegmentMissingCoverageMetadata {
183 segment_path: String,
185 },
186
187 #[snafu(display(
189 "Cannot recover append coverage: failed to read segment {segment_path} coverage at {coverage_path}: {source}"
190 ))]
191 ExistingSegmentCoverageSidecarRead {
192 segment_path: String,
194 coverage_path: String,
196 #[snafu(source(from(CoverageSidecarError, Box::new)), backtrace)]
198 source: Box<CoverageSidecarError>,
199 },
200
201 #[snafu(context(false), display("Storage error: {source}"))]
203 Storage {
204 #[snafu(source, backtrace)]
206 source: StorageError,
207 },
208
209 #[snafu(context(false), display("Commit error: {source}"))]
211 Commit {
212 #[snafu(source, backtrace)]
214 source: CommitError,
215 },
216
217 #[snafu(display(
219 "Commit outcome is ambiguous; generated Parquet path {segment_path} was preserved: {source}"
220 ))]
221 CommitAmbiguous {
222 segment_path: String,
224 #[snafu(source(from(CommitError, Box::new)), backtrace)]
226 source: Box<CommitError>,
227 },
228
229 #[snafu(display(
231 "{source}; artifact rollback also failed: [{}]",
232 cleanup_errors
233 .iter()
234 .map(ToString::to_string)
235 .collect::<Vec<_>>()
236 .join("; ")
237 ))]
238 Rollback {
239 #[snafu(source, backtrace)]
241 source: Box<AppendError>,
242 cleanup_errors: Vec<StorageError>,
244 },
245}