Files
greptimedb/src/mito2/src/error.rs
T
2026-10-12 13:11:23 +00:00

1831 lines
53 KiB
Rust

// Copyright 2023 Greptime Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use std::any::Any;
use std::sync::Arc;
use common_datasource::compression::CompressionType;
use common_error::ext::{BoxedError, ErrorExt, RetryHint};
use common_error::status_code::StatusCode;
use common_macro::stack_trace_debug;
use common_memory_manager;
use common_runtime::JoinError;
use common_time::Timestamp;
use common_time::timestamp::TimeUnit;
use datatypes::arrow::error::ArrowError;
use datatypes::prelude::ConcreteDataType;
use object_store::ErrorKind;
use object_store::error::retry_hint_from_opendal_error;
use partition::error::Error as PartitionError;
use prost::DecodeError;
use snafu::{Location, Snafu};
use store_api::ManifestVersion;
use store_api::logstore::provider::Provider;
use store_api::storage::{FileId, RegionId};
use tokio::time::error::Elapsed;
use crate::cache::file_cache::FileType;
use crate::region::RegionRoleState;
use crate::schedule::remote_job_scheduler::JobId;
use crate::worker::WorkerId;
#[derive(Snafu)]
#[snafu(visibility(pub))]
#[stack_trace_debug]
pub enum Error {
#[snafu(display("Unexpected data type"))]
DataTypeMismatch {
source: datatypes::error::Error,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("External error, context: {}", context))]
External {
source: BoxedError,
context: String,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("OpenDAL operator failed"))]
OpenDal {
#[snafu(implicit)]
location: Location,
#[snafu(source)]
error: object_store::Error,
},
#[snafu(display(
"Manifest delta {} disappeared after it was listed, path: {}",
version,
path
))]
ManifestDeltaNotFound {
version: ManifestVersion,
path: String,
#[snafu(source)]
error: object_store::Error,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Fail to compress object by {}, path: {}", compress_type, path))]
CompressObject {
compress_type: CompressionType,
path: String,
#[snafu(source)]
error: std::io::Error,
},
#[snafu(display("Fail to decompress object by {}, path: {}", compress_type, path))]
DecompressObject {
compress_type: CompressionType,
path: String,
#[snafu(source)]
error: std::io::Error,
},
#[snafu(display("Failed to ser/de json object"))]
SerdeJson {
#[snafu(implicit)]
location: Location,
#[snafu(source)]
error: serde_json::Error,
},
#[snafu(display("Failed to serialize column metadata"))]
SerializeColumnMetadata {
#[snafu(source)]
error: serde_json::Error,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Failed to serialize manifest, region_id: {}", region_id))]
SerializeManifest {
region_id: RegionId,
#[snafu(source)]
error: serde_json::Error,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Invalid scan index, start: {}, end: {}", start, end))]
InvalidScanIndex {
start: ManifestVersion,
end: ManifestVersion,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Invalid UTF-8 content"))]
Utf8 {
#[snafu(implicit)]
location: Location,
#[snafu(source)]
error: std::str::Utf8Error,
},
#[snafu(display("Cannot find RegionMetadata"))]
RegionMetadataNotFound {
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Failed to join handle"))]
Join {
#[snafu(source)]
error: common_runtime::JoinError,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Worker {} is stopped", id))]
WorkerStopped {
id: WorkerId,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Failed to recv result"))]
Recv {
#[snafu(source)]
error: tokio::sync::oneshot::error::RecvError,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Invalid metadata, {}", reason))]
InvalidMeta {
reason: String,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Invalid region metadata"))]
InvalidMetadata {
source: store_api::metadata::MetadataError,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Failed to create RecordBatch from vectors"))]
NewRecordBatch {
#[snafu(implicit)]
location: Location,
#[snafu(source)]
error: ArrowError,
},
#[snafu(display("Failed to read parquet file, path: {}", path))]
ReadParquet {
path: String,
#[snafu(source)]
error: parquet::errors::ParquetError,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Failed to write parquet file"))]
WriteParquet {
#[snafu(source)]
error: parquet::errors::ParquetError,
#[snafu(implicit)]
location: Location,
},
#[snafu(display(
"Cannot assign a stable field id to native histogram sub-field '{}' of column id {} (unknown sub-field name or derived id overflows i32)",
field_name,
column_id
))]
InvalidNativeHistogramSubfield {
column_id: i32,
field_name: String,
#[snafu(implicit)]
location: Location,
},
#[snafu(display(
"Native histogram column '{}' has no usable PARQUET:field_id to namespace its sub-field ids (missing, malformed, or exceeds i32::MAX)",
field_name
))]
InvalidNativeHistogramFieldId {
field_name: String,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Region {} not found", region_id))]
RegionNotFound {
region_id: RegionId,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Object store not found: {}", object_store))]
ObjectStoreNotFound {
object_store: String,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Region {} is corrupted, reason: {}", region_id, reason))]
RegionCorrupted {
region_id: RegionId,
reason: String,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Invalid request to region {}, reason: {}", region_id, reason))]
InvalidRequest {
region_id: RegionId,
reason: String,
#[snafu(implicit)]
location: Location,
},
#[snafu(display(
"STALE_CURSOR: incremental query stale, region: {}, given_seq: {}, min_readable_seq: {}, retry_hint: FALLBACK_FULL_RECOMPUTE",
region_id,
given_seq,
min_readable_seq
))]
IncrementalQueryStale {
region_id: RegionId,
given_seq: u64,
min_readable_seq: u64,
#[snafu(implicit)]
location: Location,
},
#[snafu(display(
"STALE_SNAPSHOT_FENCE: snapshot upper bound stale, region: {}, given_seq: {}, min_enforceable_seq: {}, retry_hint: REBIND_SNAPSHOT_FENCE",
region_id,
given_seq,
min_enforceable_seq
))]
SnapshotFenceStale {
region_id: RegionId,
given_seq: u64,
min_enforceable_seq: u64,
#[snafu(implicit)]
location: Location,
},
#[snafu(display(
"SEQUENCE_RANGE_UNSUPPORTED: exact sequence-range read unsupported, region: {}, min_seq: {}, max_seq: {}, reason: {}, retry_hint: FALLBACK_MEMTABLE_ONLY_OR_FULL_RECOMPUTE",
region_id,
min_seq,
max_seq,
reason
))]
SequenceRangeUnsupported {
region_id: RegionId,
min_seq: u64,
max_seq: u64,
reason: String,
#[snafu(implicit)]
location: Location,
},
#[snafu(display(
"region {} is unusable for sequence reads: file {} declares region {}",
region_id,
file_id,
file_region_id
))]
RegionSequenceDomainBroken {
region_id: RegionId,
file_region_id: RegionId,
file_id: FileId,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Old manifest missing for region {}", region_id))]
MissingOldManifest {
region_id: RegionId,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("New manifest missing for region {}", region_id))]
MissingNewManifest {
region_id: RegionId,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Manifest missing for region {}", region_id))]
MissingManifest {
region_id: RegionId,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("File consistency check failed for file {}: {}", file_id, reason))]
InconsistentFile {
file_id: FileId,
reason: String,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Files lost during remapping: old={}, new={}", old_count, new_count))]
FilesLost {
old_count: usize,
new_count: usize,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("No old manifests provided (need at least one for template)"))]
NoOldManifests {
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Failed to fetch manifests"))]
FetchManifests {
#[snafu(implicit)]
location: Location,
source: BoxedError,
},
#[snafu(display("Partition expression missing for region {}", region_id))]
MissingPartitionExpr {
region_id: RegionId,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Failed to serialize partition expression: {}", source))]
SerializePartitionExpr {
#[snafu(source)]
source: PartitionError,
#[snafu(implicit)]
location: Location,
},
#[snafu(display(
"Failed to convert ConcreteDataType to ColumnDataType, reason: {}",
reason
))]
ConvertColumnDataType {
reason: String,
source: api::error::Error,
#[snafu(implicit)]
location: Location,
},
/// An error type to indicate that schema is changed and we need
/// to fill default values again.
#[snafu(display("Need to fill default value for region {}", region_id))]
FillDefault {
region_id: RegionId,
// The error is for internal use so we don't need a location.
},
#[snafu(display(
"Failed to create default value for column {} of region {}",
column,
region_id
))]
CreateDefault {
region_id: RegionId,
column: String,
source: datatypes::Error,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Failed to build entry, region_id: {}", region_id))]
BuildEntry {
region_id: RegionId,
#[snafu(implicit)]
location: Location,
source: BoxedError,
},
#[snafu(display("Failed to write WAL"))]
WriteWal {
#[snafu(implicit)]
location: Location,
source: BoxedError,
},
#[snafu(display("Failed to read WAL, provider: {}", provider))]
ReadWal {
provider: Provider,
#[snafu(implicit)]
location: Location,
source: BoxedError,
},
#[snafu(display("Failed to decode WAL entry, region_id: {}", region_id))]
DecodeWal {
region_id: RegionId,
#[snafu(implicit)]
location: Location,
#[snafu(source)]
error: DecodeError,
},
#[snafu(display("Failed to delete WAL, region_id: {}", region_id))]
DeleteWal {
region_id: RegionId,
#[snafu(implicit)]
location: Location,
source: BoxedError,
},
#[snafu(display("Failed to wait for the WAL to be durable, region_id: {}", region_id))]
WaitWalDurable {
region_id: RegionId,
#[snafu(implicit)]
location: Location,
source: BoxedError,
},
// Shared error for each writer in the write group.
#[snafu(display("Failed to write region"))]
WriteGroup { source: Arc<Error> },
#[snafu(display("Invalid parquet SST file {}, reason: {}", file, reason))]
InvalidParquet {
file: String,
reason: String,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Invalid batch, {}", reason))]
InvalidBatch {
reason: String,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Invalid arrow record batch, {}", reason))]
InvalidRecordBatch {
reason: String,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Invalid wal read request, {}", reason))]
InvalidWalReadRequest {
reason: String,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Failed to convert array to vector"))]
ConvertVector {
#[snafu(implicit)]
location: Location,
source: datatypes::error::Error,
},
#[snafu(display("Failed to compute arrow arrays"))]
ComputeArrow {
#[snafu(implicit)]
location: Location,
#[snafu(source)]
error: datatypes::arrow::error::ArrowError,
},
#[snafu(display("Failed to evaluate partition filter"))]
EvalPartitionFilter {
#[snafu(implicit)]
location: Location,
#[snafu(source)]
error: datafusion::error::DataFusionError,
},
#[snafu(display("Failed to merge candidate series"))]
MergeCandidateSeries {
#[snafu(implicit)]
location: Location,
#[snafu(source)]
error: datafusion::error::DataFusionError,
},
#[snafu(display("Failed to compute vector"))]
ComputeVector {
#[snafu(implicit)]
location: Location,
source: datatypes::error::Error,
},
#[snafu(display("Primary key length mismatch, expect: {}, actual: {}", expect, actual))]
PrimaryKeyLengthMismatch {
expect: usize,
actual: usize,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Invalid sender",))]
InvalidSender {
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Invalid scheduler state"))]
InvalidSchedulerState {
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Failed to stop scheduler"))]
StopScheduler {
#[snafu(source)]
error: JoinError,
#[snafu(implicit)]
location: Location,
},
#[snafu(display(
"Failed to batch delete SST files, region id: {}, file ids: {:?}",
region_id,
file_ids
))]
DeleteSsts {
region_id: RegionId,
file_ids: Vec<FileId>,
#[snafu(source)]
error: object_store::Error,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Failed to delete index file, file id: {}", file_id))]
DeleteIndex {
file_id: FileId,
#[snafu(source)]
error: object_store::Error,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Failed to batch delete index files, file ids: {:?}", file_ids))]
DeleteIndexes {
file_ids: Vec<FileId>,
#[snafu(source)]
error: object_store::Error,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Failed to flush region {}", region_id))]
FlushRegion {
region_id: RegionId,
source: Arc<Error>,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Region {} is dropped", region_id))]
RegionDropped {
region_id: RegionId,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Region {} is closed", region_id))]
RegionClosed {
region_id: RegionId,
#[snafu(implicit)]
location: Location,
},
#[snafu(display(
"Stale compaction execution for region {}, the region may have been reopened, truncated or the compaction was superseded",
region_id
))]
StaleCompactionExecution {
region_id: RegionId,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Region {} is truncated", region_id))]
RegionTruncated {
region_id: RegionId,
#[snafu(implicit)]
location: Location,
},
#[snafu(display(
"Engine write buffer is full, rejecting write requests of region {}",
region_id,
))]
RejectWrite {
region_id: RegionId,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Region {} rejects writes while in staging", region_id))]
RejectWriteInStaging {
region_id: RegionId,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Failed to compact region {}", region_id))]
CompactRegion {
region_id: RegionId,
source: Arc<Error>,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Failed to edit region {}", region_id))]
EditRegion {
region_id: RegionId,
source: Arc<Error>,
#[snafu(implicit)]
location: Location,
},
#[snafu(display(
"Failed to compat readers for region {}, reason: {}",
region_id,
reason,
))]
CompatReader {
region_id: RegionId,
reason: String,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Invalue region req"))]
InvalidRegionRequest {
source: store_api::metadata::MetadataError,
#[snafu(implicit)]
location: Location,
},
#[snafu(display(
"Region {} is in {:?} state, which does not permit manifest updates.",
region_id,
state
))]
UpdateManifest {
region_id: RegionId,
state: RegionRoleState,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Region {} is in {:?} state, expect: {:?}", region_id, state, expect))]
RegionState {
region_id: RegionId,
state: RegionRoleState,
expect: RegionRoleState,
#[snafu(implicit)]
location: Location,
},
#[snafu(display(
"Partition expr version mismatch for region {}: request {}, expected {}",
region_id,
request_version,
expected_version
))]
PartitionExprVersionMismatch {
region_id: RegionId,
request_version: u64,
expected_version: u64,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Invalid options"))]
JsonOptions {
#[snafu(source)]
error: serde_json::Error,
#[snafu(implicit)]
location: Location,
},
#[snafu(display(
"Empty region directory, region_id: {}, region_dir: {}",
region_id,
region_dir,
))]
EmptyRegionDir {
region_id: RegionId,
region_dir: String,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Empty manifest directory, manifest_dir: {}", manifest_dir,))]
EmptyManifestDir {
manifest_dir: String,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Column not found, column: {column}"))]
ColumnNotFound {
column: String,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Failed to build index applier"))]
BuildIndexApplier {
source: index::inverted_index::error::Error,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Failed to build index asynchronously in region {}", region_id))]
BuildIndexAsync {
region_id: RegionId,
source: Arc<Error>,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Failed to convert value"))]
ConvertValue {
source: datatypes::error::Error,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Failed to apply inverted index"))]
ApplyInvertedIndex {
source: index::inverted_index::error::Error,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Failed to apply bloom filter index"))]
ApplyBloomFilterIndex {
source: index::bloom_filter::error::Error,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Failed to push index value"))]
PushIndexValue {
source: index::inverted_index::error::Error,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Failed to write index completely"))]
IndexFinish {
source: index::inverted_index::error::Error,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Operate on aborted index"))]
OperateAbortedIndex {
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Failed to read puffin blob"))]
PuffinReadBlob {
source: puffin::error::Error,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Failed to add blob to puffin file"))]
PuffinAddBlob {
source: puffin::error::Error,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Failed to clean dir {dir}"))]
CleanDir {
dir: String,
#[snafu(source)]
error: std::io::Error,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Invalid config, {reason}"))]
InvalidConfig {
reason: String,
#[snafu(implicit)]
location: Location,
},
#[snafu(display(
"Stale log entry found during replay, region: {}, flushed: {}, replayed: {}",
region_id,
flushed_entry_id,
unexpected_entry_id
))]
StaleLogEntry {
region_id: RegionId,
flushed_entry_id: u64,
unexpected_entry_id: u64,
},
#[snafu(display(
"Failed to download file, region_id: {}, file_id: {}, file_type: {:?}",
region_id,
file_id,
file_type,
))]
Download {
region_id: RegionId,
file_id: FileId,
file_type: FileType,
#[snafu(source)]
error: std::io::Error,
#[snafu(implicit)]
location: Location,
},
#[snafu(display(
"Failed to upload file, region_id: {}, file_id: {}, file_type: {:?}",
region_id,
file_id,
file_type,
))]
Upload {
region_id: RegionId,
file_id: FileId,
file_type: FileType,
#[snafu(source)]
error: std::io::Error,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Failed to create directory {}", dir))]
CreateDir {
dir: String,
#[snafu(source)]
error: std::io::Error,
},
#[snafu(display("Record batch error"))]
RecordBatch {
source: common_recordbatch::error::Error,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("BiErrors, first: {first}, second: {second}"))]
BiErrors {
first: Box<Error>,
second: Box<Error>,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Encode null value"))]
IndexEncodeNull {
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Failed to encode memtable to Parquet bytes"))]
EncodeMemtable {
#[snafu(source)]
error: parquet::errors::ParquetError,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Partition {} out of range, {} in total", given, all))]
PartitionOutOfRange {
given: usize,
all: usize,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Failed to iter data part"))]
ReadDataPart {
#[snafu(implicit)]
location: Location,
#[snafu(source)]
error: parquet::errors::ParquetError,
},
#[snafu(display("Failed to read row group in memtable"))]
DecodeArrowRowGroup {
#[snafu(source)]
error: ArrowError,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Invalid region options, {}", reason))]
InvalidRegionOptions {
reason: String,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("checksum mismatch (actual: {}, expected: {})", actual, expected))]
ChecksumMismatch { actual: u32, expected: u32 },
#[snafu(display(
"No checkpoint found, region: {}, last_version: {}",
region_id,
last_version
))]
NoCheckpoint {
region_id: RegionId,
last_version: ManifestVersion,
#[snafu(implicit)]
location: Location,
},
#[snafu(display(
"No manifests found in range: [{}..{}), region: {}, last_version: {}",
start_version,
end_version,
region_id,
last_version
))]
NoManifests {
region_id: RegionId,
start_version: ManifestVersion,
end_version: ManifestVersion,
last_version: ManifestVersion,
#[snafu(implicit)]
location: Location,
},
#[snafu(display(
"Failed to install manifest to {}, region: {}, available manifest version: {}, last version: {}",
target_version,
region_id,
available_version,
last_version
))]
InstallManifestTo {
region_id: RegionId,
target_version: ManifestVersion,
available_version: ManifestVersion,
#[snafu(implicit)]
location: Location,
last_version: ManifestVersion,
},
#[snafu(display("Region {} is stopped", region_id))]
RegionStopped {
region_id: RegionId,
#[snafu(implicit)]
location: Location,
},
#[snafu(display(
"Time range predicate overflows, timestamp: {:?}, target unit: {}",
timestamp,
unit
))]
TimeRangePredicateOverflow {
timestamp: Timestamp,
unit: TimeUnit,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Failed to open region"))]
OpenRegion {
#[snafu(implicit)]
location: Location,
source: Arc<Error>,
},
#[snafu(display(
"Region {} does not satisfy requirement '{}': {}",
region_id,
requirement,
reason
))]
RegionRequirement {
region_id: RegionId,
requirement: &'static str,
reason: &'static str,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Failed to parse job id"))]
ParseJobId {
#[snafu(implicit)]
location: Location,
#[snafu(source)]
error: uuid::Error,
},
#[snafu(display("Operation is not supported: {}", err_msg))]
UnsupportedOperation {
err_msg: String,
#[snafu(implicit)]
location: Location,
},
#[snafu(display(
"Failed to remotely compact region {} by job {:?} due to {}",
region_id,
job_id,
reason
))]
RemoteCompaction {
region_id: RegionId,
job_id: Option<JobId>,
reason: String,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Failed to initialize puffin stager"))]
PuffinInitStager {
source: puffin::error::Error,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Failed to purge puffin stager"))]
PuffinPurgeStager {
source: puffin::error::Error,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Failed to build puffin reader"))]
PuffinBuildReader {
source: puffin::error::Error,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Failed to retrieve index options from column metadata"))]
IndexOptions {
#[snafu(implicit)]
location: Location,
source: datatypes::error::Error,
column_name: String,
},
#[snafu(display("Failed to create fulltext index creator"))]
CreateFulltextCreator {
source: index::fulltext_index::error::Error,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Failed to cast vector of {from} to {to}"))]
CastVector {
#[snafu(implicit)]
location: Location,
from: ConcreteDataType,
to: ConcreteDataType,
source: datatypes::error::Error,
},
#[snafu(display("Failed to push text to fulltext index"))]
FulltextPushText {
source: index::fulltext_index::error::Error,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Failed to finalize fulltext index creator"))]
FulltextFinish {
source: index::fulltext_index::error::Error,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Failed to apply fulltext index"))]
ApplyFulltextIndex {
source: index::fulltext_index::error::Error,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("SST file {} does not contain valid stats info", file_path))]
StatsNotPresent {
file_path: String,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Failed to decode stats of file {}", file_path))]
DecodeStats {
file_path: String,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Invalid SST primary key range: {reason}"))]
InvalidPrimaryKeyRange {
reason: String,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Failed to decode SST primary key range {endpoint} endpoint"))]
DecodePrimaryKeyRange {
endpoint: &'static str,
source: mito_codec::error::Error,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Region {} is busy", region_id))]
RegionBusy {
region_id: RegionId,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Failed to get schema metadata"))]
GetSchemaMetadata {
source: common_meta::error::Error,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Timeout"))]
Timeout {
#[snafu(source)]
error: Elapsed,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Failed to read file metadata"))]
Metadata {
#[snafu(source)]
error: std::io::Error,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Failed to push value to bloom filter"))]
PushBloomFilterValue {
source: index::bloom_filter::error::Error,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Failed to finish bloom filter"))]
BloomFilterFinish {
source: index::bloom_filter::error::Error,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Manual compaction is override by following operations."))]
ManualCompactionOverride {},
#[snafu(display("Manual compaction is already running for region {region_id}."))]
ManualCompactionAlreadyRunning { region_id: RegionId },
#[snafu(display("Compaction is cancelled."))]
CompactionCancelled {},
#[snafu(display("Flush is cancelled."))]
FlushCancelled {},
#[snafu(display("Compaction memory exhausted for region {region_id} (policy: {policy})",))]
CompactionMemoryExhausted {
region_id: RegionId,
policy: String,
#[snafu(source)]
source: common_memory_manager::Error,
#[snafu(implicit)]
location: Location,
},
#[snafu(display(
"Incompatible WAL provider change. This is typically caused by changing WAL provider in database config file without completely cleaning existing files. Global provider: {}, region provider: {}",
global,
region
))]
IncompatibleWalProviderChange { global: String, region: String },
#[snafu(display("Expected mito manifest info"))]
MitoManifestInfo {
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Failed to scan series"))]
ScanSeries {
#[snafu(implicit)]
location: Location,
source: Arc<Error>,
},
#[snafu(display("Partition {} scan multiple times", partition))]
ScanMultiTimes {
partition: usize,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Invalid partition expression: {}", expr))]
InvalidPartitionExpr {
expr: String,
#[snafu(implicit)]
location: Location,
source: partition::error::Error,
},
#[snafu(display("Failed to decode bulk wal entry"))]
ConvertBulkWalEntry {
#[snafu(implicit)]
location: Location,
source: common_grpc::Error,
},
#[snafu(display("Failed to encode"))]
Encode {
#[snafu(implicit)]
location: Location,
source: mito_codec::error::Error,
},
#[snafu(display("Failed to decode"))]
Decode {
#[snafu(implicit)]
location: Location,
source: mito_codec::error::Error,
},
#[snafu(display("Unexpected: {reason}"))]
Unexpected {
reason: String,
#[snafu(implicit)]
location: Location,
},
#[cfg(feature = "enterprise")]
#[snafu(display("Failed to scan external range"))]
ScanExternalRange {
source: BoxedError,
#[snafu(implicit)]
location: Location,
},
#[snafu(display(
"Inconsistent timestamp column length, expect: {}, actual: {}",
expected,
actual
))]
InconsistentTimestampLength {
expected: usize,
actual: usize,
#[snafu(implicit)]
location: Location,
},
#[snafu(display(
"Too many files to read concurrently: {}, max allowed: {}",
actual,
max
))]
TooManyFilesToRead {
actual: usize,
max: usize,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Duration out of range: {input:?}"))]
DurationOutOfRange {
input: std::time::Duration,
#[snafu(source)]
error: chrono::OutOfRangeError,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("GC job permit exhausted"))]
TooManyGcJobs {
#[snafu(implicit)]
location: Location,
},
#[snafu(display(
"Staging partition expr mismatch, manifest: {:?}, request: {}",
manifest_expr,
request_expr
))]
StagingPartitionExprMismatch {
manifest_expr: Option<String>,
request_expr: String,
#[snafu(implicit)]
location: Location,
},
#[snafu(display(
"Invalid source and target region, source: {}, target: {}",
source_region_id,
target_region_id
))]
InvalidSourceAndTargetRegion {
source_region_id: RegionId,
target_region_id: RegionId,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Failed to prune file"))]
PruneFile {
source: Arc<Error>,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Failed to cast column"))]
CastColumn {
#[snafu(source)]
error: datafusion::error::DataFusionError,
#[snafu(implicit)]
location: Location,
},
#[snafu(display("Failed to generate Arrow schema from Parquet file: {}", file))]
ParquetToArrowSchema {
file: String,
#[snafu(source)]
error: parquet::errors::ParquetError,
#[snafu(implicit)]
location: Location,
},
#[snafu(display(
"Region {} is in {:?} state, expect: Writable, Staging or Downgrading",
region_id,
state
))]
FlushableRegionState {
region_id: RegionId,
state: RegionRoleState,
#[snafu(implicit)]
location: Location,
},
}
pub type Result<T, E = Error> = std::result::Result<T, E>;
impl Error {
/// Returns true if we need to fill default value for a region.
pub(crate) fn is_fill_default(&self) -> bool {
matches!(self, Error::FillDefault { .. })
}
/// Returns true if the file is not found on the object store.
pub(crate) fn is_object_not_found(&self) -> bool {
match self {
Error::OpenDal { error, .. } => error.kind() == ErrorKind::NotFound,
Error::ManifestDeltaNotFound { .. } => true,
_ => false,
}
}
/// Returns whether a failed manifest update may have been persisted.
///
/// Unknown errors are treated conservatively because deleting output SSTs after an ambiguous
/// manifest write could leave the manifest referencing missing files.
pub(crate) fn may_have_persisted_manifest_update(&self) -> bool {
match self {
Error::UpdateManifest { .. }
| Error::RegionState { .. }
| Error::RegionTruncated { .. }
| Error::RegionStopped { .. }
| Error::SerdeJson { .. }
| Error::CompressObject { .. } => false,
Error::OpenDal { error, .. } => !matches!(
error.kind(),
ErrorKind::Unsupported
| ErrorKind::ConfigInvalid
| ErrorKind::NotFound
| ErrorKind::PermissionDenied
| ErrorKind::IsADirectory
| ErrorKind::NotADirectory
| ErrorKind::AlreadyExists
| ErrorKind::RateLimited
| ErrorKind::IsSameFile
| ErrorKind::ConditionNotMatch
| ErrorKind::RangeNotSatisfied
),
_ => true,
}
}
}
impl ErrorExt for Error {
fn status_code(&self) -> StatusCode {
use Error::*;
match self {
DataTypeMismatch { source, .. } => source.status_code(),
OpenDal { .. } | ManifestDeltaNotFound { .. } | ReadParquet { .. } => {
StatusCode::StorageUnavailable
}
WriteWal { source, .. }
| ReadWal { source, .. }
| DeleteWal { source, .. }
| WaitWalDurable { source, .. } => source.status_code(),
CompressObject { .. }
| DecompressObject { .. }
| SerdeJson { .. }
| Utf8 { .. }
| NewRecordBatch { .. }
| RegionCorrupted { .. }
| InconsistentFile { .. }
| CreateDefault { .. }
| InvalidParquet { .. }
| OperateAbortedIndex { .. }
| IndexEncodeNull { .. }
| NoCheckpoint { .. }
| NoManifests { .. }
| FilesLost { .. }
| InstallManifestTo { .. }
| Unexpected { .. }
| SerializeColumnMetadata { .. }
| SerializeManifest { .. }
| StagingPartitionExprMismatch { .. } => StatusCode::Unexpected,
RegionNotFound { .. } => StatusCode::RegionNotFound,
ObjectStoreNotFound { .. }
| InvalidScanIndex { .. }
| InvalidMeta { .. }
| InvalidRequest { .. }
| PartitionExprVersionMismatch { .. }
| FillDefault { .. }
| ConvertColumnDataType { .. }
| ColumnNotFound { .. }
| InvalidMetadata { .. }
| InvalidRegionOptions { .. }
| InvalidWalReadRequest { .. }
| PartitionOutOfRange { .. }
| ParseJobId { .. }
| DurationOutOfRange { .. }
| MissingOldManifest { .. }
| MissingNewManifest { .. }
| MissingManifest { .. }
| NoOldManifests { .. }
| MissingPartitionExpr { .. }
| SerializePartitionExpr { .. }
| InvalidSourceAndTargetRegion { .. } => StatusCode::InvalidArguments,
IncrementalQueryStale { .. } | SnapshotFenceStale { .. } => StatusCode::RequestOutdated,
SequenceRangeUnsupported { .. } => StatusCode::Unsupported,
RegionSequenceDomainBroken { .. }
| RegionMetadataNotFound { .. }
| Join { .. }
| WorkerStopped { .. }
| Recv { .. }
| DecodeWal { .. }
| ComputeArrow { .. }
| EvalPartitionFilter { .. }
| MergeCandidateSeries { .. }
| BiErrors { .. }
| StopScheduler { .. }
| ComputeVector { .. }
| EncodeMemtable { .. }
| CreateDir { .. }
| ReadDataPart { .. }
| BuildEntry { .. }
| Metadata { .. }
| CastColumn { .. }
| MitoManifestInfo { .. }
| ParquetToArrowSchema { .. } => StatusCode::Internal,
FetchManifests { source, .. } => source.status_code(),
OpenRegion { source, .. } => source.status_code(),
WriteParquet { .. } => StatusCode::StorageUnavailable,
WriteGroup { source, .. } => source.status_code(),
InvalidBatch { .. } => StatusCode::InvalidArguments,
InvalidRecordBatch { .. } => StatusCode::InvalidArguments,
ConvertVector { source, .. } => source.status_code(),
PrimaryKeyLengthMismatch { .. } => StatusCode::InvalidArguments,
InvalidSender { .. } => StatusCode::InvalidArguments,
InvalidSchedulerState { .. } => StatusCode::InvalidArguments,
RegionRequirement { .. } => StatusCode::InvalidArguments,
DeleteSsts { .. } | DeleteIndex { .. } | DeleteIndexes { .. } => {
StatusCode::StorageUnavailable
}
FlushRegion { source, .. } | BuildIndexAsync { source, .. } => source.status_code(),
RegionDropped { .. } => StatusCode::Cancelled,
RegionClosed { .. } => StatusCode::Cancelled,
StaleCompactionExecution { .. } => StatusCode::Cancelled,
RegionTruncated { .. } => StatusCode::Cancelled,
RejectWrite { .. } | RejectWriteInStaging { .. } => StatusCode::StorageUnavailable,
CompactRegion { source, .. } => source.status_code(),
EditRegion { source, .. } => source.status_code(),
CompatReader { .. } => StatusCode::Unexpected,
InvalidRegionRequest { source, .. } => source.status_code(),
RegionState { .. } | UpdateManifest { .. } => StatusCode::RegionNotReady,
JsonOptions { .. } => StatusCode::InvalidArguments,
EmptyRegionDir { .. } | EmptyManifestDir { .. } => StatusCode::RegionNotFound,
ConvertValue { source, .. } => source.status_code(),
ApplyBloomFilterIndex { source, .. } => source.status_code(),
InvalidPartitionExpr { source, .. } => source.status_code(),
BuildIndexApplier { source, .. }
| PushIndexValue { source, .. }
| ApplyInvertedIndex { source, .. }
| IndexFinish { source, .. } => source.status_code(),
PuffinReadBlob { source, .. }
| PuffinAddBlob { source, .. }
| PuffinInitStager { source, .. }
| PuffinBuildReader { source, .. }
| PuffinPurgeStager { source, .. } => source.status_code(),
CleanDir { .. } => StatusCode::Unexpected,
InvalidConfig { .. } => StatusCode::InvalidArguments,
StaleLogEntry { .. }
| InvalidNativeHistogramSubfield { .. }
| InvalidNativeHistogramFieldId { .. } => StatusCode::Unexpected,
External { source, .. } => source.status_code(),
RecordBatch { source, .. } => source.status_code(),
Download { .. } | Upload { .. } => StatusCode::StorageUnavailable,
ChecksumMismatch { .. } => StatusCode::Unexpected,
RegionStopped { .. } => StatusCode::RegionNotReady,
TimeRangePredicateOverflow { .. } => StatusCode::InvalidArguments,
UnsupportedOperation { .. } => StatusCode::Unsupported,
RemoteCompaction { .. } => StatusCode::Unexpected,
IndexOptions { source, .. } => source.status_code(),
CreateFulltextCreator { source, .. } => source.status_code(),
CastVector { source, .. } => source.status_code(),
FulltextPushText { source, .. }
| FulltextFinish { source, .. }
| ApplyFulltextIndex { source, .. } => source.status_code(),
DecodeStats { .. }
| StatsNotPresent { .. }
| InvalidPrimaryKeyRange { .. }
| DecodePrimaryKeyRange { .. } => StatusCode::Internal,
RegionBusy { .. } => StatusCode::RegionBusy,
GetSchemaMetadata { source, .. } => source.status_code(),
Timeout { .. } => StatusCode::Cancelled,
DecodeArrowRowGroup { .. } => StatusCode::Internal,
PushBloomFilterValue { source, .. } | BloomFilterFinish { source, .. } => {
source.status_code()
}
ManualCompactionOverride {} | CompactionCancelled {} | FlushCancelled {} => {
StatusCode::Cancelled
}
// A concurrent manual compaction fails fast instead of being queued;
// the conflict is reported to the caller, which decides whether to
// issue a new request after the running one finishes.
ManualCompactionAlreadyRunning { .. } => StatusCode::RegionBusy,
CompactionMemoryExhausted { source, .. } => source.status_code(),
IncompatibleWalProviderChange { .. } => StatusCode::InvalidArguments,
ScanSeries { source, .. } => source.status_code(),
ScanMultiTimes { .. } => StatusCode::InvalidArguments,
ConvertBulkWalEntry { source, .. } => source.status_code(),
Encode { source, .. } | Decode { source, .. } => source.status_code(),
#[cfg(feature = "enterprise")]
ScanExternalRange { source, .. } => source.status_code(),
InconsistentTimestampLength { .. } => StatusCode::InvalidArguments,
TooManyFilesToRead { .. } | TooManyGcJobs { .. } => StatusCode::RateLimited,
PruneFile { source, .. } => source.status_code(),
FlushableRegionState { .. } => StatusCode::RegionNotReady,
}
}
fn as_any(&self) -> &dyn Any {
self
}
fn retry_hint(&self) -> RetryHint {
use Error::*;
match self {
PartitionExprVersionMismatch { .. }
| RegionNotFound { .. }
| RejectWriteInStaging { .. }
| RegionState {
state: RegionRoleState::Follower,
..
} => RetryHint::RetryAfterRouteRefresh,
ReadParquet { .. }
| WriteParquet { .. }
| RejectWrite { .. }
| Download { .. }
| Upload { .. }
| RegionState { .. }
| UpdateManifest { .. }
| RegionStopped { .. }
| RegionBusy { .. }
| ManualCompactionAlreadyRunning { .. }
| FlushableRegionState { .. }
| ManifestDeltaNotFound { .. } => RetryHint::Retryable,
OpenDal { error, .. }
| DeleteSsts { error, .. }
| DeleteIndex { error, .. }
| DeleteIndexes { error, .. } => retry_hint_from_opendal_error(error),
WriteWal { source, .. }
| ReadWal { source, .. }
| DeleteWal { source, .. }
| WaitWalDurable { source, .. }
| FetchManifests { source, .. }
| External { source, .. } => source.retry_hint(),
OpenRegion { source, .. }
| WriteGroup { source, .. }
| FlushRegion { source, .. }
| BuildIndexAsync { source, .. }
| CompactRegion { source, .. }
| EditRegion { source, .. }
| ScanSeries { source, .. }
| PruneFile { source, .. } => source.retry_hint(),
DataTypeMismatch { source, .. }
| ConvertVector { source, .. }
| ConvertValue { source, .. }
| IndexOptions { source, .. }
| CastVector { source, .. } => source.retry_hint(),
BuildIndexApplier { source, .. }
| PushIndexValue { source, .. }
| ApplyInvertedIndex { source, .. }
| IndexFinish { source, .. } => source.retry_hint(),
ApplyBloomFilterIndex { source, .. }
| PushBloomFilterValue { source, .. }
| BloomFilterFinish { source, .. } => source.retry_hint(),
PuffinReadBlob { source, .. }
| PuffinAddBlob { source, .. }
| PuffinInitStager { source, .. }
| PuffinBuildReader { source, .. }
| PuffinPurgeStager { source, .. } => source.retry_hint(),
CreateFulltextCreator { source, .. }
| FulltextPushText { source, .. }
| FulltextFinish { source, .. }
| ApplyFulltextIndex { source, .. } => source.retry_hint(),
InvalidRegionRequest { source, .. } => source.retry_hint(),
InvalidPartitionExpr { source, .. } => source.retry_hint(),
RecordBatch { source, .. } => source.retry_hint(),
GetSchemaMetadata { source, .. } => source.retry_hint(),
CompactionMemoryExhausted { source, .. } => source.retry_hint(),
ConvertBulkWalEntry { source, .. } => source.retry_hint(),
Encode { source, .. } | Decode { source, .. } => source.retry_hint(),
#[cfg(feature = "enterprise")]
ScanExternalRange { source, .. } => source.retry_hint(),
_ => RetryHint::NonRetryable,
}
}
}
#[cfg(test)]
mod tests {
use snafu::IntoError;
use super::*;
use crate::region::RegionLeaderState;
#[test]
fn test_partition_version_mismatch_retry_hint() {
let error = PartitionExprVersionMismatchSnafu {
region_id: RegionId::new(1, 1),
request_version: 1u64,
expected_version: 2u64,
}
.build();
assert_eq!(error.status_code(), StatusCode::InvalidArguments);
assert_eq!(error.retry_hint(), RetryHint::RetryAfterRouteRefresh);
}
#[test]
fn test_region_state_retry_hint() {
let region_id = RegionId::new(1, 1);
let writable = RegionRoleState::Leader(RegionLeaderState::Writable);
let follower_error = RegionStateSnafu {
region_id,
state: RegionRoleState::Follower,
expect: writable,
}
.build();
assert_eq!(
follower_error.retry_hint(),
RetryHint::RetryAfterRouteRefresh
);
assert_eq!(follower_error.status_code(), StatusCode::RegionNotReady);
let leader_error = RegionStateSnafu {
region_id,
state: writable,
expect: RegionRoleState::Follower,
}
.build();
assert_eq!(leader_error.retry_hint(), RetryHint::Retryable);
for (error, status, hint) in [
(
RejectWriteSnafu { region_id }.build(),
StatusCode::StorageUnavailable,
RetryHint::Retryable,
),
(
RejectWriteInStagingSnafu { region_id }.build(),
StatusCode::StorageUnavailable,
RetryHint::RetryAfterRouteRefresh,
),
(
RegionNotFoundSnafu { region_id }.build(),
StatusCode::RegionNotFound,
RetryHint::RetryAfterRouteRefresh,
),
] {
assert_eq!(error.status_code(), status);
assert_eq!(error.retry_hint(), hint);
}
}
#[test]
fn test_manifest_update_persistence() {
let rejected_kinds = [
ErrorKind::Unsupported,
ErrorKind::ConfigInvalid,
ErrorKind::NotFound,
ErrorKind::PermissionDenied,
ErrorKind::IsADirectory,
ErrorKind::NotADirectory,
ErrorKind::AlreadyExists,
ErrorKind::RateLimited,
ErrorKind::IsSameFile,
ErrorKind::ConditionNotMatch,
ErrorKind::RangeNotSatisfied,
];
for kind in rejected_kinds {
let error = OpenDalSnafu {}.into_error(object_store::Error::new(kind, "test"));
assert!(
!error.may_have_persisted_manifest_update(),
"error kind {kind:?} should prove the manifest was not persisted"
);
}
let error =
OpenDalSnafu {}.into_error(object_store::Error::new(ErrorKind::Unexpected, "test"));
assert!(error.may_have_persisted_manifest_update());
let region_id = RegionId::new(1, 1);
let error = RegionTruncatedSnafu { region_id }.build();
assert!(!error.may_have_persisted_manifest_update());
// This can be raised while applying an edit after its manifest file was saved.
let error = RegionMetadataNotFoundSnafu {}.build();
assert!(error.may_have_persisted_manifest_update());
}
}