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}