millipede_core/storage/
mod.rs1mod auto_saved;
4mod dataset;
5mod handle;
6mod kvs;
7mod queue;
8pub mod rate_limit;
9
10pub use auto_saved::AutoSaved;
11pub use dataset::{Dataset, DatasetExt, DatasetInfo, ListOptions, Page};
12pub use handle::StorageHandle;
13pub use kvs::{KeyInfo, KeyList, KeyValueStore, KeyValueStoreExt, KvEntry, ListKeysOptions};
14pub use queue::{
15 AddOptions, AddRequestsBatchedResult, BatchAddHandle, Lease, LeaseId, ProcessedRequest,
16 QueueOpInfo, ReclaimOptions, RequestQueue, RequestSource,
17};
18pub use rate_limit::RateLimitReportingClient;
19
20use std::sync::Arc;
21
22#[derive(Debug, thiserror::Error)]
24#[non_exhaustive]
25pub enum StorageError {
26 #[error("serialization: {0}")]
28 Serialization(#[from] serde_json::Error),
29 #[error("io: {0}")]
31 Io(#[from] std::io::Error),
32 #[error("lease {lease_id} not found (already completed, abandoned, or expired)")]
34 LeaseNotFound {
35 lease_id: LeaseId,
37 },
38 #[error("storage backend: {0}")]
40 Backend(#[source] anyhow::Error),
41 #[error("operation not supported by this backend: {0}")]
43 Unsupported(&'static str),
44 #[error("storage backend rate limited")]
46 RateLimited {
47 retry_after: Option<std::time::Duration>,
49 },
50}
51
52impl StorageError {
53 pub fn is_rate_limited(&self) -> bool {
55 matches!(self, StorageError::RateLimited { .. })
56 }
57}
58
59pub type StorageResult<T> = Result<T, StorageError>;
61
62impl From<StorageError> for crate::errors::CrawlError {
63 fn from(error: StorageError) -> Self {
64 match error {
65 StorageError::Serialization(error) => Self::NonRetryable(anyhow::Error::new(error)),
66 StorageError::Io(error) => Self::Retry(anyhow::Error::new(error)),
67 error @ StorageError::LeaseNotFound { .. } => {
68 Self::NonRetryable(anyhow::Error::new(error))
69 }
70 StorageError::Backend(error) => Self::Retry(error),
71 error @ StorageError::Unsupported(_) => Self::NonRetryable(anyhow::Error::new(error)),
72 error @ StorageError::RateLimited { .. } => Self::Retry(anyhow::Error::new(error)),
73 }
74 }
75}
76
77#[async_trait::async_trait]
79pub trait StorageClient: Send + Sync + 'static {
80 async fn open_dataset(&self, name: Option<&str>) -> StorageResult<Arc<dyn Dataset>>;
82 async fn open_key_value_store(
84 &self,
85 name: Option<&str>,
86 ) -> StorageResult<Arc<dyn KeyValueStore>>;
87 async fn open_request_queue(&self, name: Option<&str>) -> StorageResult<Arc<dyn RequestQueue>>;
89 async fn purge(&self) -> StorageResult<()>;
91}