Skip to main content

slatedb_mirror/
policy.rs

1//! The policy that decides which objects the mirror keeps.
2
3use std::sync::Arc;
4
5use async_trait::async_trait;
6use bytes::Bytes;
7use object_store::path::Path;
8use object_store::{GetOptions, PutMultipartOptions, PutOptions};
9
10use crate::MirrorHandle;
11
12/// Where a GET or HEAD is served from.
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub enum ReadRoute {
15    /// Wrapped store only.
16    Remote,
17    /// Wrapped store. The whole object goes to [`MirrorPolicy::observe`]
18    /// before the caller gets a result. HEADs and GETs that return no body
19    /// skip `observe`. This route does not install a local copy.
20    Observe,
21    /// Local copy only. A miss is a [`MirrorError::NotLocal`](crate::MirrorError::NotLocal),
22    /// with no remote fallback.
23    /// Contents can be corrupt. Callers must detect corruption and arrange
24    /// a retry through [`ReadRoute::Refetch`].
25    Local,
26    /// Download the whole object again, replace the local copy, and serve the
27    /// request from it. Even a range request or HEAD downloads the whole object.
28    Refetch,
29}
30
31/// Where a PUT goes.
32#[derive(Debug, Clone, Copy, PartialEq, Eq)]
33pub enum WriteRoute {
34    /// Wrapped store only.
35    Remote,
36    /// Wrapped store, then the whole payload goes to [`MirrorPolicy::observe`]
37    /// before returning success.
38    Observe,
39    /// Write to both. Returns after the upload succeeds and the local file is
40    /// in place.
41    Mirror,
42}
43
44/// Where a multipart upload goes. There's no `Observe` route because it
45/// would mean buffering every part in memory.
46#[derive(Debug, Clone, Copy, PartialEq, Eq)]
47pub enum MultipartRoute {
48    /// Wrapped store only.
49    Remote,
50    /// Write to both. Returns after the upload completes and the local file
51    /// is in place.
52    Mirror,
53}
54
55/// Decides which objects an `ObjectStoreMirror` keeps locally.
56///
57/// Policies must only route paths that are never overwritten to
58/// [`ReadRoute::Local`], [`ReadRoute::Refetch`], [`WriteRoute::Mirror`], or
59/// [`MultipartRoute::Mirror`].
60///
61/// Errors from the policy reach the caller in three ways:
62///
63/// - A route error becomes `MirrorError::Policy`. If the error already
64///   contains a `MirrorError`, the mirror returns it unchanged.
65/// - An `observe` error on a read is returned unchanged. Nothing was written,
66///   so the caller can retry the read.
67/// - An `observe` error on a write becomes `MirrorError::WriteCommitted`,
68///   because the remote write already succeeded.
69#[async_trait]
70pub trait MirrorPolicy: Send + Sync + 'static {
71    /// Runs on every GET and HEAD. `options.head` is true for HEAD. Must be
72    /// cheap.
73    fn read_route(&self, path: &Path, options: &GetOptions) -> object_store::Result<ReadRoute>;
74
75    /// Runs on every PUT. Must be cheap.
76    fn put_route(&self, path: &Path, options: &PutOptions) -> object_store::Result<WriteRoute>;
77
78    /// Runs on every multipart upload. Must be cheap.
79    fn put_multipart_route(
80        &self,
81        path: &Path,
82        options: &PutMultipartOptions,
83    ) -> object_store::Result<MultipartRoute>;
84
85    /// Runs for `Observe` routes, after the remote call succeeds and before the
86    /// caller gets the result. `bytes` is the whole object.
87    async fn observe(
88        &self,
89        path: &Path,
90        bytes: &Bytes,
91        mirror: &MirrorHandle,
92    ) -> object_store::Result<()> {
93        let _ = (path, bytes, mirror);
94        Ok(())
95    }
96
97    /// Runs in the background for the life of the mirror. `build()` spawns
98    /// this task and it's cancelled when the mirror is dropped.
99    ///
100    /// The handle keeps the mirror's local state (and its `LOCK`) alive, so a
101    /// policy shouldn't store it anywhere that outlives this call.
102    async fn run(self: Arc<Self>, mirror: MirrorHandle) {
103        let _ = mirror;
104    }
105}