use std::backtrace::Backtrace; use std::collections::TryReserveError; use std::io::{Error as IoError, ErrorKind}; use std::sync::atomic::{AtomicBool, Ordering}; use atomicwrites::Error as AtomicIoError; use io::file_operations::FileStorageError; use memory::mmap_type::Error as MmapError; use rayon::ThreadPoolBuildError; use thiserror::Error; use crate::types::{PayloadKeyType, PointIdType, SeqNumberType}; use crate::utils::mem::Mem; pub const PROCESS_CANCELLED_BY_SERVICE_MESSAGE: &str = "process cancelled by service"; #[derive(Error, Debug, Clone)] #[error("{0}")] pub enum OperationError { #[error("Vector dimension error: expected dim: {expected_dim}, got {received_dim}")] WrongVectorDimension { expected_dim: usize, received_dim: usize, }, #[error("Not existing vector name error: {received_name}")] VectorNameNotExists { received_name: String }, #[error("Missed vector name error: {received_name}")] MissedVectorName { received_name: String }, #[error("No point with id {missed_point_id}")] PointIdError { missed_point_id: PointIdType }, #[error("Payload type does not match with previously given for field {field_name}. Expected: {expected_type}")] TypeError { field_name: PayloadKeyType, expected_type: String, }, #[error("Unable to infer type for the field '{field_name}'. Please specify `field_type`")] TypeInferenceError { field_name: PayloadKeyType }, /// Service Error prevents further update of the collection until it is fixed. /// Should only be used for hardware, data corruption, IO, or other unexpected internal errors. #[error("Service runtime error: {description}")] ServiceError { description: String, backtrace: Option, }, #[error("Inconsistent storage: {description}")] InconsistentStorage { description: String }, #[error("Out of memory, free: {free}, {description}")] OutOfMemory { description: String, free: u64 }, #[error("Operation cancelled: {description}")] Cancelled { description: String }, #[error("Validation failed: {description}")] ValidationError { description: String }, #[error("Wrong usage of sparse vectors")] WrongSparse, #[error("Wrong usage of multi vectors")] WrongMulti, #[error("No range index for `order_by` key: `{key}`. Please create one to use `order_by`. Check https://qdrant.tech/documentation/concepts/indexing/#payload-index to see which payload schemas support Range conditions")] MissingRangeIndexForOrderBy { key: String }, #[error("No appropriate index for faceting: `{key}`. Please create one to facet on this field. Check https://qdrant.tech/documentation/concepts/indexing/#payload-index to see which payload schemas support Match conditions")] MissingMapIndexForFacet { key: String }, } impl OperationError { /// Create a new service error with a description and a backtrace /// Warning: capturing a backtrace can be an expensive operation on some platforms, so this should be used with caution in performance-sensitive parts of code. pub fn service_error(description: impl Into) -> OperationError { OperationError::ServiceError { description: description.into(), backtrace: Some(Backtrace::force_capture().to_string()), } } /// Create a new service error with a description and no backtrace pub fn service_error_light(description: impl Into) -> OperationError { OperationError::ServiceError { description: description.into(), backtrace: None, } } } pub fn check_process_stopped(stopped: &AtomicBool) -> OperationResult<()> { if stopped.load(Ordering::Relaxed) { return Err(OperationError::Cancelled { description: PROCESS_CANCELLED_BY_SERVICE_MESSAGE.to_string(), }); } Ok(()) } /// Contains information regarding last operation error, which should be fixed before next operation could be processed #[derive(Debug, Clone)] pub struct SegmentFailedState { pub version: SeqNumberType, pub point_id: Option, pub error: OperationError, } impl From for OperationError { fn from(error: ThreadPoolBuildError) -> Self { OperationError::ServiceError { description: format!("{error}"), backtrace: Some(Backtrace::force_capture().to_string()), } } } impl From for OperationError { fn from(err: FileStorageError) -> Self { Self::service_error(err.to_string()) } } impl From for OperationError { fn from(err: MmapError) -> Self { Self::service_error(err.to_string()) } } impl From for OperationError { fn from(err: serde_cbor::Error) -> Self { OperationError::service_error(format!("Failed to parse data: {err}")) } } impl From> for OperationError { fn from(err: AtomicIoError) -> Self { match err { AtomicIoError::Internal(io_err) => OperationError::from(io_err), AtomicIoError::User(_user_err) => { OperationError::service_error("Unknown atomic write error") } } } } impl From for OperationError { fn from(err: IoError) -> Self { match err.kind() { ErrorKind::OutOfMemory => { let free_memory = Mem::new().available_memory_bytes(); OperationError::OutOfMemory { description: format!("IO Error: {err}"), free: free_memory, } } _ => OperationError::service_error(format!("IO Error: {err}")), } } } impl From for OperationError { fn from(err: serde_json::Error) -> Self { OperationError::service_error(format!("Json error: {err}")) } } impl From for OperationError { fn from(err: fs_extra::error::Error) -> Self { OperationError::service_error(format!("File system error: {err}")) } } impl From for OperationError { fn from(err: geohash::GeohashError) -> Self { OperationError::service_error(format!("Geohash error: {err}")) } } impl From for OperationError { fn from(err: quantization::EncodingError) -> Self { match err { quantization::EncodingError::IOError(err) | quantization::EncodingError::EncodingError(err) | quantization::EncodingError::ArgumentsError(err) => { OperationError::service_error(format!("Quantization encoding error: {err}")) } quantization::EncodingError::Stopped => OperationError::Cancelled { description: PROCESS_CANCELLED_BY_SERVICE_MESSAGE.to_string(), }, } } } impl From for OperationError { fn from(err: TryReserveError) -> Self { let free_memory = Mem::new().available_memory_bytes(); OperationError::OutOfMemory { description: format!("Failed to reserve memory: {err}"), free: free_memory, } } } pub type OperationResult = Result; pub fn get_service_error(err: &OperationResult) -> Option { match err { Ok(_) => None, Err(error) => match error { OperationError::ServiceError { .. } => Some(error.clone()), _ => None, }, } }