use crate::api::validator::{PollerError, PollerType};
use crate::implementation::constants::PRIMARY_INACTIVE_TIMEOUT;
use crate::implementation::model::contracts_storage::{
ContractsLocalStorage, ContractsState, ContractsStorage,
};
use data_storage_lib::{DataStorage, DataStorageError, RemoteDataStorage, StoreMode};
use lock_lib::Lock;
use pdk_core::classy::Clock;
use pdk_core::log::debug;
use std::rc::Rc;
const CONTRACTS_KEY: &str = "contracts";
pub struct ContractsCache {
clock: Rc<dyn Clock>,
remote: RemoteDataStorage,
local: Rc<ContractsLocalStorage>,
}
impl ContractsCache {
pub fn new(
clock: Rc<dyn Clock>,
remote: RemoteDataStorage,
local: Rc<ContractsLocalStorage>,
) -> Self {
Self {
clock,
remote,
local,
}
}
pub async fn get_state(&self) -> Option<ContractsState> {
match self.remote.get::<ContractsState>(CONTRACTS_KEY).await {
Ok(Some((state, _))) => Some(state),
Err(e) => {
debug!("Failed to get contracts state from the cache. {e}");
None
}
_ => {
debug!("No state saved in the cache.");
None
}
}
}
pub async fn try_primary(&self, api_lock: &'_ Lock<'_>) -> Result<PollerType, PollerError> {
debug!("Trying to become primary polling node.");
let state = self
.remote
.get::<ContractsState>(CONTRACTS_KEY)
.await
.map_err(|e| {
debug!("Failed to get contracts state from the cache. {e}");
PollerError::DataStorageError
})?;
if !api_lock.refresh_lock() {
debug!("Lost the api lock while getting contracts state from the cache.");
return Err(PollerError::LostLock);
}
let mode = match state {
None => {
debug!("No current primary found.");
StoreMode::Absent
}
Some((value, cas)) => {
if value.primary() + PRIMARY_INACTIVE_TIMEOUT > self.clock.get_current_time() {
debug!("Primary found, will become Secondary.");
self.local.set_primary(false);
self.local.set_primary_update(value.primary());
return Ok(PollerType::Secondary);
}
debug!("Expired primary found.");
StoreMode::Cas(cas)
}
};
let time = self.clock.get_current_time();
let mut local_state = self.local.get_state();
local_state.update_primary(time);
let response = self.remote.store(CONTRACTS_KEY, &mode, &local_state).await;
let response = match response {
Ok(()) => {
debug!("Successfully became primary.");
self.local.set_primary(true);
self.local.set_primary_update(time);
Ok(PollerType::Primary)
}
Err(DataStorageError::CasMismatch) => {
debug!("Another node became primary.");
self.local.set_primary(false);
self.local.set_primary_update(time);
Ok(PollerType::Secondary)
}
Err(e) => {
debug!("Failed to store contracts update to the cache. {e}");
Err(PollerError::DataStorageError)
}
};
if !api_lock.refresh_lock() {
debug!("Lost the api lock while storing contracts state in the cache.");
}
response
}
pub async fn save_state(&self, state: ContractsState) -> bool {
debug!("Storing contracts state in the cache.");
if let Err(result) = self
.remote
.store(CONTRACTS_KEY, &StoreMode::Always, &state)
.await
{
debug!("Failed to store contracts state in the cache. {result}");
false
} else {
debug!("Successfully stored contracts state in the cache.");
true
}
}
}