wb_cache/
traits.rs

1use async_trait::async_trait;
2use std::fmt::Debug;
3use std::fmt::Display;
4use std::hash::Hash;
5use std::sync::Arc;
6
7use crate::types::DataControllerOp;
8use crate::types::DataControllerResponse;
9use crate::update_iterator::UpdateIterator;
10use crate::wbdc_response;
11
12/// The [data controller](crate#data-controller) implementation.
13///
14/// Use of the [`Cache`](crate::Cache) must start with creating a type that implements this trait.
15///
16/// This crate includes example implementations of data controllers in the simulation code. These can be found in the
17/// [`test::simulation::db::entity`](crate::test::simulation::db::entity) module as the `Manager` types.  Since they all
18/// use the same backend (SeaORM framework), they implement the trait
19/// [`DCCommon`](crate::test::simulation::db::cache::DCCommon) which provides a set of common methods for simulation
20/// data controllers.
21#[async_trait]
22pub trait DataController: Sized + Send + Sync + 'static {
23    /// The key type to be used with methods like [`Cache::get()`](crate::Cache::get) or
24    /// [`Cache::entry()`](crate::Cache::entry).
25    ///
26    /// To implement multi-key access you can use an enum as the key type. See, for example,
27    /// [`CustomerBy`](crate::test::simulation::db::entity::customer::CustomerBy) key type in the supplied simulation
28    /// code.
29    type Key: Debug + Display + Hash + Clone + Eq + Sized + Send + Sync + 'static;
30    /// The record type to be stored in the cache and maintained by the data controller.
31    type Value: Debug + Clone + Send + Sync + 'static;
32    /// The type of the update pool records that are produced by the data controller.
33    ///
34    /// The simulation code uses the [`CacheUpdates`](crate::test::simulation::db::cache::CacheUpdates) enum for this purpose.
35    /// Then, every every model's data controller uses this enum with respective active model type. See the implemenation
36    /// for [the customer model](crate::test::simulation::db::entity::customer::Manager::CacheUpdate) for example.
37    type CacheUpdate: Debug + Send + Sync + 'static;
38    /// The error type that the data controller can return. Note that this is the only error type the the cache
39    /// controller is using!
40    type Error: Display + Debug + Send + Sync + 'static;
41
42    /// Must return the value for the given key, if it exists, `None` otherwise.
43    ///
44    /// Example: [`customer model`](crate::test::simulation::db::entity::customer::Manager::get_for_key).
45    async fn get_for_key(&self, key: &Self::Key) -> Result<Option<Self::Value>, Self::Error>;
46    /// Given a list of update records, must apply these updates to the underlying backend.
47    ///
48    /// Example: [`DCCommon::wbdc_write_back()`](crate::test::simulation::db::cache::DCCommon::wbdc_write_back).
49    async fn write_back(&self, updates: Arc<UpdateIterator<Self>>) -> Result<(), Self::Error>;
50    /// Called when a new record is added to the cache. On success, it must return a [`DataControllerResponse`] with the
51    /// operation type set to what the data controller considers appropriate for the new record. For example, if it is
52    /// known that the new record will have exactly the same field values after being written to the backend as it had
53    /// when submitted to this method, then the operation can be set to [`DataControllerOp::Insert`].
54    ///
55    /// Example: [`DCCommon::wbdbc_on_new()`](crate::test::simulation::db::cache::DCCommon::wbdbc_on_new). The trait
56    /// classifies DB models into two categories: immutable and mutable. Immutable models do not modify written records
57    /// (e.g., when there is no primary key autoincrement) and their records are immediately cached without a roundtrip
58    /// to the database.
59    async fn on_new(&self, key: &Self::Key, value: &Self::Value) -> Result<DataControllerResponse<Self>, Self::Error>;
60    /// Called when there is a `delete` request for the given key has been received. Don't mix it up with the `invalidate`
61    /// request which is only clearing a cache entry.
62    ///
63    /// The most typical response operation for this method is [`DataControllerOp::Revoke`].
64    ///
65    /// Example: [`DCCommon::wbdc_on_delete()`](crate::test::simulation::db::cache::DCCommon::wbdc_on_delete).
66    async fn on_delete(
67        &self,
68        key: &Self::Key,
69        update: Option<&Self::CacheUpdate>,
70    ) -> Result<DataControllerResponse<Self>, Self::Error>;
71    /// Called when a record is modified by the user.
72    ///
73    /// Example: [`DCCommon::wbdc_on_change()`](crate::test::simulation::db::cache::DCCommon::wbdc_on_change).
74    async fn on_change(
75        &self,
76        key: &Self::Key,
77        value: &Self::Value,
78        old_value: Self::Value,
79        prev_handler: Option<Self::CacheUpdate>,
80    ) -> Result<DataControllerResponse<Self>, Self::Error>;
81    /// Returns the primary key for the given value.
82    ///
83    /// Example: [`customer model`](crate::test::simulation::db::entity::customer::Manager::primary_key_of).
84    fn primary_key_of(&self, value: &Self::Value) -> Self::Key;
85
86    /// Returns a list of secondary keys for the given value. Default implementation returns an empty vector.
87    ///
88    /// Example: [`customer model`](crate::test::simulation::db::entity::customer::Manager::secondary_keys_of).
89    fn secondary_keys_of(&self, _value: &Self::Value) -> Vec<Self::Key> {
90        Vec::new()
91    }
92
93    /// Take a key and return its corresponding primary key.  This operation may require a backend request.
94    ///
95    /// The default implementation covers the simple case where there are no secondary keys, simply returning the key
96    /// itself.
97    ///
98    /// Example: [`customer model`](crate::test::simulation::db::entity::customer::Manager::get_primary_key_for).
99    async fn get_primary_key_for(&self, key: &Self::Key) -> Result<Option<Self::Key>, Self::Error> {
100        Ok(Some(key.clone()))
101    }
102
103    /// Returns `true` if the given key is considered the primary key.
104    ///
105    /// The default implementation always returns `true`, which is appropriate when no secondary keys exist.
106    ///
107    /// Example: [`customer model`](crate::test::simulation::db::entity::customer::Manager::is_primary).
108    fn is_primary(&self, _key: &Self::Key) -> bool {
109        true
110    }
111
112    /// This method is called on every access to the given key in the cache. It can be used to implement various related
113    /// functionality like updateing the access time column in the backend, or calculating related metrics or
114    /// statistics.
115    ///
116    /// The default implementation does nothing and returns a no-op response.
117    #[inline(always)]
118    async fn on_access(
119        &self,
120        _key: &Self::Key,
121        _value: &Self::Value,
122        prev_update: Option<Self::CacheUpdate>,
123    ) -> Result<DataControllerResponse<Self>, Self::Error> {
124        // log::debug!("default DC on_access for '{_key}'");
125        Ok(wbdc_response!(DataControllerOp::Nop, prev_update))
126    }
127}
128
129/// The observer trait is used by the cache controller to notify user code about various events that happen in the
130/// cache controller.
131///
132/// _Note_: This functionality is currently rather limited and is considered experimental.
133#[async_trait]
134pub trait Observer<DC>: Send + Sync + 'static
135where
136    DC: DataController,
137{
138    async fn on_flush(&self, _cache_updates: Arc<UpdateIterator<DC>>) -> Result<(), DC::Error> {
139        Ok(())
140    }
141    async fn on_flush_one(&self, _updates: &DC::Key, _update: &DC::CacheUpdate) -> Result<(), Arc<DC::Error>> {
142        Ok(())
143    }
144    async fn on_monitor_error(&self, _error: &Arc<DC::Error>) {}
145    async fn on_error(&self, _error: Arc<DC::Error>) {}
146    async fn on_warning(&self, _message: &str) {}
147    async fn on_debug(&self, _message: &str) {}
148}