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(display(
72 "Invalid maximum bytes per Parquet row group: {max_bytes_per_row_group}; expected a positive value"
73 ))]
74 InvalidMaxBytesPerRowGroup {
75 max_bytes_per_row_group: usize,
77 },
78
79 #[snafu(context(false), display("Schema validation failed: {source}"))]
81 SchemaValidation {
82 #[snafu(source(from(SchemaCompatibilityError, Box::new)), backtrace)]
84 source: Box<SchemaCompatibilityError>,
85 },
86
87 #[snafu(display(
89 "Generated segment {segment_path} is incompatible with the table schema: {source}"
90 ))]
91 GeneratedSegmentSchemaCompatibility {
92 segment_path: String,
94 #[snafu(source(from(SchemaCompatibilityError, Box::new)), backtrace)]
96 source: Box<SchemaCompatibilityError>,
97 },
98
99 #[snafu(display(
101 "Table has no logical_schema at version {version}; cannot append without a canonical schema"
102 ))]
103 MissingCanonicalTableSchema {
104 version: u64,
106 },
107
108 #[snafu(context(false), display("Generated segment metadata error: {source}"))]
110 SegmentMetadata {
111 #[snafu(source(from(SegmentError, Box::new)), backtrace)]
113 source: Box<SegmentError>,
114 },
115
116 #[snafu(display("Parquet write error: {source}"))]
118 ParquetWrite {
119 #[snafu(source)]
121 source: ParquetError,
122 backtrace: Backtrace,
124 },
125
126 #[snafu(context(false), display("Segment coverage error: {source}"))]
128 GeneratedSegmentCoverage {
129 #[snafu(source(from(SegmentCoverageError, Box::new)), backtrace)]
131 source: Box<SegmentCoverageError>,
132 },
133
134 #[snafu(context(false), display("Index interval mapping failed: {source}"))]
136 IndexIntervalMapping {
137 #[snafu(source)]
139 source: IndexIntervalMappingError,
140 backtrace: Backtrace,
142 },
143
144 #[snafu(context(false), display("Coverage sidecar error: {source}"))]
146 CoverageSidecar {
147 #[snafu(source(from(CoverageSidecarError, Box::new)), backtrace)]
149 source: Box<CoverageSidecarError>,
150 },
151
152 #[snafu(display(
154 "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})"
155 ))]
156 PersistedIndexIntervalOverlap {
157 segment_path: String,
159 overlap_count: u128,
161 example_identity: Option<EntityIdentity>,
163 example_index_interval_id: IndexIntervalId,
165 example_index_interval: Box<IndexInterval>,
167 },
168
169 #[snafu(display("No entity coverage derived while appending segment {segment_path}"))]
171 EmptySegmentEntityCoverage {
172 segment_path: String,
174 },
175
176 #[snafu(display(
178 "Entity {identity:?} in segment {segment_path} has no non-null ordered-index values"
179 ))]
180 EntityWithoutIndexCoverage {
181 segment_path: String,
183 identity: EntityIdentity,
185 },
186
187 #[snafu(display(
189 "Cannot append because existing segment {segment_path} is missing coverage_path"
190 ))]
191 ExistingSegmentMissingCoverageMetadata {
192 segment_path: String,
194 },
195
196 #[snafu(display(
198 "Cannot recover append coverage: failed to read segment {segment_path} coverage at {coverage_path}: {source}"
199 ))]
200 ExistingSegmentCoverageSidecarRead {
201 segment_path: String,
203 coverage_path: String,
205 #[snafu(source(from(CoverageSidecarError, Box::new)), backtrace)]
207 source: Box<CoverageSidecarError>,
208 },
209
210 #[snafu(context(false), display("Storage error: {source}"))]
212 Storage {
213 #[snafu(source, backtrace)]
215 source: StorageError,
216 },
217
218 #[snafu(context(false), display("Commit error: {source}"))]
220 Commit {
221 #[snafu(source, backtrace)]
223 source: CommitError,
224 },
225
226 #[snafu(display(
228 "Commit outcome is ambiguous; generated Parquet path {segment_path} was preserved: {source}"
229 ))]
230 CommitAmbiguous {
231 segment_path: String,
233 #[snafu(source(from(CommitError, Box::new)), backtrace)]
235 source: Box<CommitError>,
236 },
237
238 #[snafu(display(
240 "{source}; artifact rollback also failed: [{}]",
241 cleanup_errors
242 .iter()
243 .map(ToString::to_string)
244 .collect::<Vec<_>>()
245 .join("; ")
246 ))]
247 Rollback {
248 #[snafu(source, backtrace)]
250 source: Box<AppendError>,
251 cleanup_errors: Vec<StorageError>,
253 },
254}