kitsune2_api/
peer_meta_store.rs

1use crate::{builder, config, BoxFut, K2Error, K2Result, Timestamp, Url};
2use bytes::Bytes;
3use futures::future::BoxFuture;
4use std::{collections::HashMap, sync::Arc};
5
6/// Key prefix for items at the root level of the peer meta store.
7pub const KEY_PREFIX_ROOT: &str = "root";
8
9/// Meta key for unresponsive URLs.
10pub const META_KEY_UNRESPONSIVE: &str = "unresponsive";
11
12/// A store for peer metadata.
13///
14/// This is expected to be backed by a key-value store that keys by space, peer URL and key.
15pub trait PeerMetaStore: 'static + Send + Sync + std::fmt::Debug {
16    /// Store a key-value pair for a peer.
17    fn put(
18        &self,
19        peer: Url,
20        key: String,
21        value: Bytes,
22        expiry: Option<Timestamp>,
23    ) -> BoxFuture<'_, K2Result<()>>;
24
25    /// Get a value by key for a peer.
26    fn get(
27        &self,
28        peer: Url,
29        key: String,
30    ) -> BoxFuture<'_, K2Result<Option<bytes::Bytes>>>;
31
32    /// Get all peer urls and values for a given key.
33    fn get_all_by_key(
34        &self,
35        key: String,
36    ) -> BoxFuture<'_, K2Result<HashMap<Url, bytes::Bytes>>>;
37
38    /// Mark a peer url unresponsive with an expiration timestamp.
39    ///
40    /// The value that will be stored with the peer key is the passed in timestamp
41    /// from when the URL became unresponsive.
42    ///
43    /// After the expiry timestamp has passed, the peer url is supposed to be removed
44    /// from the store.
45    fn set_unresponsive(
46        &self,
47        peer: Url,
48        expiry: Timestamp,
49        when: Timestamp,
50    ) -> BoxFuture<'_, K2Result<()>> {
51        Box::pin(async move {
52            self.put(
53                peer.clone(),
54                format!("{KEY_PREFIX_ROOT}:{META_KEY_UNRESPONSIVE}"),
55                serde_json::to_vec(&when).map_err(K2Error::other)?.into(),
56                Some(expiry),
57            )
58            .await?;
59            Ok(())
60        })
61    }
62
63    /// Get the timestamp of when a peer URL last was marked unresponsive, if it is present in the
64    /// store.
65    fn get_unresponsive(
66        &self,
67        peer: Url,
68    ) -> BoxFuture<'_, K2Result<Option<Timestamp>>> {
69        Box::pin(async move {
70            let maybe_value = self
71                .get(peer, format!("{KEY_PREFIX_ROOT}:{META_KEY_UNRESPONSIVE}"))
72                .await?;
73            match maybe_value {
74                None => Ok(None),
75                Some(value) => {
76                    match serde_json::from_slice::<Timestamp>(&value) {
77                        Ok(when) => Ok(Some(when)),
78                        Err(err) => Err(K2Error::other(err)),
79                    }
80                }
81            }
82        })
83    }
84
85    /// Delete a key-value pair for a given space and peer.
86    fn delete(&self, peer: Url, key: String) -> BoxFuture<'_, K2Result<()>>;
87}
88
89/// Trait-object version of kitsune2 [PeerMetaStore].
90pub type DynPeerMetaStore = Arc<dyn PeerMetaStore>;
91
92/// A factory for constructing [PeerMetaStore] instances.
93pub trait PeerMetaStoreFactory:
94    'static + Send + Sync + std::fmt::Debug
95{
96    /// Help the builder construct a default config from the chosen
97    /// module factories.
98    fn default_config(&self, config: &mut config::Config) -> K2Result<()>;
99
100    /// Validate configuration.
101    fn validate_config(&self, config: &config::Config) -> K2Result<()>;
102
103    /// Construct a meta store instance.
104    fn create(
105        &self,
106        builder: Arc<builder::Builder>,
107        space_id: crate::SpaceId,
108    ) -> BoxFut<'static, K2Result<DynPeerMetaStore>>;
109}
110
111/// Trait-object [PeerMetaStoreFactory].
112pub type DynPeerMetaStoreFactory = Arc<dyn PeerMetaStoreFactory>;