use std::sync::Arc;
use crate::{
acl::user_handle::UserHandle,
storage::session::common::array_key_iteration_functions::cluster_slot, types::RespCommand,
};
pub const SINGLE_NODE_CURRENT_EPOCH: i64 = 1;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SlotVerifiedState {
Ok,
CrossSlot,
TryAgain,
Moved,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct CachedSlotVerification {
state: SlotVerifiedState,
slot: u16,
}
const RESP_ERR_CROSSSLOT: &str = "CROSSSLOT Keys in request don't hash to the same slot";
const RESP_ERR_TRYAGAIN: &str = "TRYAGAIN Multiple keys request during rehashing of slot";
pub struct IClusterSession {
remote_node_id: Option<String>,
read_write_session: bool,
is_replicating: bool,
local_current_epoch: i64,
user_handle: Option<Arc<UserHandle>>,
cached_verification: Option<CachedSlotVerification>,
}
impl Default for IClusterSession {
fn default() -> Self {
Self::new()
}
}
impl IClusterSession {
pub fn new() -> Self {
Self {
remote_node_id: None,
read_write_session: false,
is_replicating: false,
local_current_epoch: 0,
user_handle: None,
cached_verification: None,
}
}
pub fn remote_node_id(&self) -> Option<&str> {
self.remote_node_id.as_deref()
}
pub fn read_write_session(&self) -> bool {
true
}
pub fn read_write_session_flag(&self) -> bool {
self.read_write_session
}
pub fn is_replicating(&self) -> bool {
self.is_replicating
}
pub fn local_current_epoch(&self) -> i64 {
self.local_current_epoch
}
pub fn set_read_only_session(&mut self) {
self.read_write_session = false;
}
pub fn set_read_write_session(&mut self) {
self.read_write_session = true;
}
pub fn acquire_current_epoch(&mut self) {
self.local_current_epoch = SINGLE_NODE_CURRENT_EPOCH;
}
pub fn release_current_epoch(&mut self) {
self.local_current_epoch = 0;
}
pub fn process_cluster_commands(&mut self, _command: RespCommand) -> bool {
false
}
pub fn reset_cached_slot_verification_result(&mut self) {
self.cached_verification = None;
}
pub fn network_iterative_slot_verify(
&mut self,
key: &[u8],
read_only: bool,
session_asking: u8,
wait_for_stable_slot: bool,
) -> bool {
let slot = cluster_slot(key);
let verify = CachedSlotVerification {
state: self.verify_single_key(slot, read_only, session_asking != 0, wait_for_stable_slot),
slot,
};
match self.cached_verification {
None => {
self.cached_verification = Some(verify);
verify.state == SlotVerifiedState::Ok
}
Some(cached) if cached.state != SlotVerifiedState::Ok => false,
Some(cached) => {
if verify.slot != cached.slot {
self.cached_verification = Some(CachedSlotVerification {
state: SlotVerifiedState::CrossSlot,
slot: cached.slot,
});
return false;
}
if verify.state != cached.state {
self.cached_verification = Some(CachedSlotVerification {
state: SlotVerifiedState::TryAgain,
slot: cached.slot,
});
return false;
}
verify.state == SlotVerifiedState::Ok
}
}
}
fn verify_single_key(
&self,
_slot: u16,
_read_only: bool,
_session_asking: bool,
_wait_for_stable_slot: bool,
) -> SlotVerifiedState {
SlotVerifiedState::Ok
}
pub fn write_cached_slot_verification_message(&self, output: &mut Vec<u8>) {
if let Some(cached) = self
.cached_verification
.filter(|c| c.state != SlotVerifiedState::Ok)
{
let message = match cached.state {
SlotVerifiedState::CrossSlot => RESP_ERR_CROSSSLOT,
SlotVerifiedState::TryAgain => RESP_ERR_TRYAGAIN,
SlotVerifiedState::Moved => "MOVED",
SlotVerifiedState::Ok => unreachable!("filtered above"),
};
output.push(b'-');
output.extend_from_slice(message.as_bytes());
output.extend_from_slice(b"\r\n");
}
}
pub fn network_multi_key_slot_verify(
&mut self,
_keys: &[&[u8]],
_is_txn: bool,
_output: &mut Vec<u8>,
) -> bool {
false
}
pub fn network_multi_key_slot_verify_no_response(
&mut self,
_keys: &[&[u8]],
_is_txn: bool,
) -> bool {
false
}
pub fn set_user_handle(&mut self, user_handle: Arc<UserHandle>) {
self.user_handle = Some(user_handle);
}
pub fn user_handle(&self) -> Option<&Arc<UserHandle>> {
self.user_handle.as_ref()
}
pub fn process_primary_stream(
&mut self,
_physical_sublog_idx: usize,
_record: &[u8],
_previous_address: i64,
_current_address: i64,
_next_address: i64,
) {
}
pub fn dispose(&mut self) {
self.is_replicating = false;
self.cached_verification = None;
self.remote_node_id = None;
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::acl::user::User;
#[test]
fn read_write_semantics_follow_primary_short_circuit() {
let mut s = IClusterSession::new();
assert!(s.read_write_session());
assert!(!s.read_write_session_flag());
s.set_read_write_session();
assert!(s.read_write_session_flag());
s.set_read_only_session();
assert!(!s.read_write_session_flag());
assert!(s.read_write_session());
}
#[test]
fn epoch_acquire_release_roundtrip() {
let mut s = IClusterSession::new();
assert_eq!(s.local_current_epoch(), 0);
s.acquire_current_epoch();
assert_eq!(s.local_current_epoch(), SINGLE_NODE_CURRENT_EPOCH);
s.release_current_epoch();
assert_eq!(s.local_current_epoch(), 0);
}
#[test]
fn cluster_command_family_is_passthrough_false() {
let mut s = IClusterSession::new();
assert!(!s.process_cluster_commands(RespCommand::None));
assert!(!s.process_cluster_commands(RespCommand::Migrate));
}
#[test]
fn iterative_verify_passes_same_slot_and_flags_cross_slot() {
let mut s = IClusterSession::new();
s.reset_cached_slot_verification_result();
assert!(s.network_iterative_slot_verify(b"user:{100}", true, 0, false));
assert!(s.network_iterative_slot_verify(b"order:{100}", true, 0, false));
let slot = cluster_slot(b"user:{100}");
let mut probe = 0u32;
let other_key = loop {
let key = format!("probe-{probe}");
if cluster_slot(key.as_bytes()) != slot {
break key;
}
probe += 1;
};
assert!(!s.network_iterative_slot_verify(other_key.as_bytes(), true, 0, false));
assert!(!s.network_iterative_slot_verify(b"another", true, 0, false));
let mut out = Vec::new();
s.write_cached_slot_verification_message(&mut out);
assert!(String::from_utf8_lossy(&out).contains("CROSSSLOT"));
}
#[test]
fn cached_verification_message_silent_on_ok() {
let mut s = IClusterSession::new();
s.reset_cached_slot_verification_result();
assert!(s.network_iterative_slot_verify(b"k", false, 0, false));
let mut out = Vec::new();
s.write_cached_slot_verification_message(&mut out);
assert!(out.is_empty());
}
#[test]
fn reset_clears_cached_state() {
let mut s = IClusterSession::new();
s.reset_cached_slot_verification_result();
assert!(s.network_iterative_slot_verify(b"k", false, 0, false));
s.reset_cached_slot_verification_result();
assert!(s.network_iterative_slot_verify(b"fresh", false, 0, false));
}
#[test]
fn multi_key_verify_skipped_without_cluster() {
let mut s = IClusterSession::new();
let keys = [b"k1".as_slice(), b"k2".as_slice()];
let mut out = Vec::new();
assert!(!s.network_multi_key_slot_verify(&keys, false, &mut out));
assert!(out.is_empty());
assert!(!s.network_multi_key_slot_verify_no_response(&keys, true));
}
#[test]
fn primary_stream_and_remote_id_are_single_node_noops() {
let mut s = IClusterSession::new();
assert_eq!(s.remote_node_id(), None);
assert!(!s.is_replicating());
s.process_primary_stream(0, b"record", 0, 64, 128);
assert!(!s.is_replicating());
}
#[test]
fn user_handle_swap_and_dispose() {
let mut s = IClusterSession::new();
assert!(s.user_handle().is_none());
s.set_user_handle(Arc::new(UserHandle::new(Arc::new(User::new(
"default".to_string(),
)))));
assert!(s.user_handle().is_some());
s.dispose();
assert!(s.user_handle().is_some());
assert!(!s.is_replicating());
assert_eq!(s.remote_node_id(), None);
}
}