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}