From ee1ddac70ce296a0161ea4fe0744f987ee260a8b Mon Sep 17 00:00:00 2001 From: lxl66566 Date: Tue, 6 Aug 2024 17:33:21 +0800 Subject: [PATCH] refactor(xlineapi): KeyRange refactor Signed-off-by: lxl66566 --- crates/utils/src/interval_map/mod.rs | 11 +- crates/utils/src/parser.rs | 5 +- crates/xline-client/src/clients/lock.rs | 3 +- crates/xline-client/src/types/auth.rs | 2 +- crates/xline-client/src/types/kv.rs | 2 +- crates/xline-client/src/types/range_end.rs | 2 +- crates/xline-client/src/types/watch.rs | 2 +- crates/xline/src/conflict/mod.rs | 4 +- crates/xline/src/conflict/spec_pool.rs | 2 +- crates/xline/src/conflict/uncommitted_pool.rs | 2 +- crates/xline/src/server/command.rs | 29 -- crates/xline/src/server/lock_server.rs | 3 +- crates/xline/src/server/watch_server.rs | 4 +- crates/xline/src/storage/auth_store/perms.rs | 12 +- crates/xline/src/storage/auth_store/store.rs | 15 +- crates/xline/src/storage/index.rs | 13 +- crates/xline/src/storage/kv_store.rs | 5 +- crates/xline/src/storage/kvwatcher.rs | 2 +- crates/xlineapi/src/command.rs | 267 +---------- crates/xlineapi/src/interval.rs | 87 ---- crates/xlineapi/src/keyrange.rs | 419 ++++++++++++++++++ crates/xlineapi/src/lib.rs | 25 +- crates/xlineapi/src/request_validation.rs | 4 +- 23 files changed, 492 insertions(+), 428 deletions(-) delete mode 100644 crates/xlineapi/src/interval.rs create mode 100644 crates/xlineapi/src/keyrange.rs diff --git a/crates/utils/src/interval_map/mod.rs b/crates/utils/src/interval_map/mod.rs index d03297c3e..a14f08fc9 100644 --- a/crates/utils/src/interval_map/mod.rs +++ b/crates/utils/src/interval_map/mod.rs @@ -1,6 +1,7 @@ use std::collections::VecDeque; use petgraph::graph::{DefaultIx, IndexType, NodeIndex}; +use serde::{Deserialize, Serialize}; #[cfg(test)] mod tests; @@ -979,7 +980,7 @@ where /// The Interval stored in `IntervalMap` /// Represents the interval [low, high) -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] #[non_exhaustive] pub struct Interval { /// Low value @@ -988,6 +989,14 @@ pub struct Interval { pub high: T, } +impl Interval { + /// Transfer the interval into tuple + #[inline] + pub fn into_parts(self) -> (T, T) { + (self.low, self.high) + } +} + impl Interval { /// Creates a new `Interval` /// diff --git a/crates/utils/src/parser.rs b/crates/utils/src/parser.rs index 15c0d7182..75b3cd2a9 100644 --- a/crates/utils/src/parser.rs +++ b/crates/utils/src/parser.rs @@ -70,7 +70,8 @@ pub fn parse_members(s: &str) -> Result>, ConfigPars Ok(map) } -/// Parse `ClusterRange` from the given string +/// Parse `ClusterRange` from the given string. The string should be in the +/// format of `start..end`, and start and end should be u64. /// /// # Errors /// @@ -185,7 +186,7 @@ pub fn parse_log_file(s: &str) -> Result { } // This regular expression matches file paths in a specific format. // Explanation of the regex pattern: - // ^(\.|\.\.)? - Matches an optional prefix consisting of a dot (.), two dots (..), or a tilde (~). + // ^(\.|\.\.)? - Matches an optional prefix consisting of a dot (.), two dots (..), or a tilde (~). // (/[a-zA-Z0-9._-]+)+/? - Matches one or more occurrences of a forward slash (/) followed by one or more alphanumeric characters, dots (.), underscores (_), or hyphens (-). // /?$ - Matches an optional trailing forward slash (/) at the end of the string. // Overall, this regex pattern is used to validate file paths that follow a specific format commonly used in Linux filesystem diff --git a/crates/xline-client/src/clients/lock.rs b/crates/xline-client/src/clients/lock.rs index 58af9764a..15e4e7117 100644 --- a/crates/xline-client/src/clients/lock.rs +++ b/crates/xline-client/src/clients/lock.rs @@ -6,7 +6,8 @@ use clippy_utilities::OverflowArithmetic; use tokio::{task::JoinHandle, time::sleep}; use tonic::transport::Channel; use xlineapi::{ - command::{Command, CommandResponse, KeyRange, SyncResponse}, + command::{Command, CommandResponse, SyncResponse}, + keyrange::KeyRange, Compare, CompareResult, CompareTarget, DeleteRangeRequest, EventType, PutRequest, RangeRequest, RangeResponse, Request, RequestOp, RequestWrapper, Response, ResponseHeader, SortOrder, SortTarget, TargetUnion, TxnRequest, TxnResponse, diff --git a/crates/xline-client/src/types/auth.rs b/crates/xline-client/src/types/auth.rs index ca10dc170..ebe6c0572 100644 --- a/crates/xline-client/src/types/auth.rs +++ b/crates/xline-client/src/types/auth.rs @@ -1,4 +1,4 @@ -use xlineapi::command::KeyRange; +use xlineapi::keyrange::KeyRange; pub use xlineapi::{ AuthDisableResponse, AuthEnableResponse, AuthRoleAddResponse, AuthRoleDeleteResponse, AuthRoleGetResponse, AuthRoleGrantPermissionResponse, AuthRoleListResponse, diff --git a/crates/xline-client/src/types/kv.rs b/crates/xline-client/src/types/kv.rs index f6f1bc14b..659979d99 100644 --- a/crates/xline-client/src/types/kv.rs +++ b/crates/xline-client/src/types/kv.rs @@ -1,4 +1,4 @@ -use xlineapi::command::KeyRange; +use xlineapi::keyrange::KeyRange; pub use xlineapi::{ CompactionResponse, CompareResult, CompareTarget, DeleteRangeResponse, PutResponse, RangeResponse, Response, ResponseOp, SortOrder, SortTarget, TargetUnion, TxnResponse, diff --git a/crates/xline-client/src/types/range_end.rs b/crates/xline-client/src/types/range_end.rs index d4c3d5f70..c4b154e81 100644 --- a/crates/xline-client/src/types/range_end.rs +++ b/crates/xline-client/src/types/range_end.rs @@ -1,4 +1,4 @@ -use xlineapi::command::KeyRange; +use xlineapi::keyrange::KeyRange; /// Range end options, indicates how to set `range_end` from a key. #[derive(Clone, Debug, PartialEq, Eq, Default)] diff --git a/crates/xline-client/src/types/watch.rs b/crates/xline-client/src/types/watch.rs index 7c7be55aa..087fc2d26 100644 --- a/crates/xline-client/src/types/watch.rs +++ b/crates/xline-client/src/types/watch.rs @@ -272,7 +272,7 @@ impl DerefMut for WatchStreaming { #[cfg(test)] mod tests { - use xlineapi::command::KeyRange; + use xlineapi::keyrange::KeyRange; use super::*; diff --git a/crates/xline/src/conflict/mod.rs b/crates/xline/src/conflict/mod.rs index ae16fe66b..4ddef0bf6 100644 --- a/crates/xline/src/conflict/mod.rs +++ b/crates/xline/src/conflict/mod.rs @@ -6,8 +6,8 @@ use curp::{ }; use utils::interval_map::Interval; use xlineapi::{ - command::{Command, KeyRange}, - interval::BytesAffine, + command::Command, + keyrange::{BytesAffine, KeyRange}, RequestWrapper, }; diff --git a/crates/xline/src/conflict/spec_pool.rs b/crates/xline/src/conflict/spec_pool.rs index 8bcfb41ec..5bd31d28a 100644 --- a/crates/xline/src/conflict/spec_pool.rs +++ b/crates/xline/src/conflict/spec_pool.rs @@ -7,7 +7,7 @@ use std::{collections::HashMap, sync::Arc}; use curp::server::conflict::CommandEntry; use curp_external_api::conflict::{ConflictPoolOp, EntryId, SpeculativePoolOp}; use utils::interval_map::{Interval, IntervalMap}; -use xlineapi::{command::Command, interval::BytesAffine}; +use xlineapi::{command::Command, keyrange::BytesAffine}; use crate::storage::lease_store::LeaseCollection; diff --git a/crates/xline/src/conflict/uncommitted_pool.rs b/crates/xline/src/conflict/uncommitted_pool.rs index ba02ed5ca..2c33d99c5 100644 --- a/crates/xline/src/conflict/uncommitted_pool.rs +++ b/crates/xline/src/conflict/uncommitted_pool.rs @@ -11,7 +11,7 @@ use curp::server::conflict::CommandEntry; use curp_external_api::conflict::{ConflictPoolOp, EntryId, UncommittedPoolOp}; use itertools::Itertools; use utils::interval_map::{Interval, IntervalMap}; -use xlineapi::{command::Command, interval::BytesAffine}; +use xlineapi::{command::Command, keyrange::BytesAffine}; use crate::storage::lease_store::LeaseCollection; diff --git a/crates/xline/src/server/command.rs b/crates/xline/src/server/command.rs index 1fb8fee57..49b98e7d8 100644 --- a/crates/xline/src/server/command.rs +++ b/crates/xline/src/server/command.rs @@ -31,35 +31,6 @@ use crate::{ /// Key of applied index pub(crate) const APPLIED_INDEX_KEY: &str = "applied_index"; -/// Range start and end to get all keys -const UNBOUNDED: &[u8] = &[0_u8]; -/// Range end to get one key -const ONE_KEY: &[u8] = &[]; - -/// Type of `KeyRange` -pub(crate) enum RangeType { - /// `KeyRange` contains only one key - OneKey, - /// `KeyRange` contains all keys - AllKeys, - /// `KeyRange` contains the keys in the range - Range, -} - -impl RangeType { - /// Get `RangeType` by given `key` and `range_end` - #[inline] - pub(crate) fn get_range_type(key: &[u8], range_end: &[u8]) -> Self { - if range_end == ONE_KEY { - RangeType::OneKey - } else if key == UNBOUNDED && range_end == UNBOUNDED { - RangeType::AllKeys - } else { - RangeType::Range - } - } -} - /// Command Executor #[derive(Debug)] pub(crate) struct CommandExecutor { diff --git a/crates/xline/src/server/lock_server.rs b/crates/xline/src/server/lock_server.rs index 578b03e1e..d5f7a4654 100644 --- a/crates/xline/src/server/lock_server.rs +++ b/crates/xline/src/server/lock_server.rs @@ -10,8 +10,9 @@ use utils::build_endpoint; #[cfg(madsim)] use utils::ClientTlsConfig; use xlineapi::{ - command::{Command, CommandResponse, CurpClient, KeyRange, SyncResponse}, + command::{Command, CommandResponse, CurpClient, SyncResponse}, execute_error::ExecuteError, + keyrange::KeyRange, AuthInfo, EventType, }; diff --git a/crates/xline/src/server/watch_server.rs b/crates/xline/src/server/watch_server.rs index 8476a5a38..4d3114b68 100644 --- a/crates/xline/src/server/watch_server.rs +++ b/crates/xline/src/server/watch_server.rs @@ -9,7 +9,7 @@ use tokio::sync::mpsc; use tokio_stream::{wrappers::ReceiverStream, Stream, StreamExt}; use tracing::{debug, warn}; use utils::task_manager::{tasks::TaskName, Listener, TaskManager}; -use xlineapi::command::KeyRange; +use xlineapi::keyrange::KeyRange; use crate::{ header_gen::HeaderGenerator, @@ -207,7 +207,7 @@ where return; }; - let key_range = KeyRange::new(req.key, req.range_end); + let key_range = KeyRange::new_etcd(req.key, req.range_end); self.kv_watcher.watch( watch_id, key_range, diff --git a/crates/xline/src/storage/auth_store/perms.rs b/crates/xline/src/storage/auth_store/perms.rs index afb29952b..19ac9058e 100644 --- a/crates/xline/src/storage/auth_store/perms.rs +++ b/crates/xline/src/storage/auth_store/perms.rs @@ -6,7 +6,7 @@ use jsonwebtoken::{ use merged_range::MergedRange; use serde::{Deserialize, Serialize}; use utils::timestamp; -use xlineapi::{command::KeyRange, AuthInfo}; +use xlineapi::{keyrange::KeyRange, AuthInfo}; use crate::rpc::{Permission, Type}; @@ -123,18 +123,18 @@ impl UserPermissions { /// Insert a permission to `UserPermissions` pub(super) fn insert(&mut self, perm: Permission) { - let range = KeyRange::new(perm.key, perm.range_end).unpack(); + let range = KeyRange::new_etcd(perm.key, perm.range_end); #[allow(clippy::unwrap_used)] // safe unwrap match Type::try_from(perm.perm_type).unwrap() { Type::Readwrite => { - self.read.insert(range.clone()); - self.write.insert(range); + self.read.insert(range.clone().into_bounds()); + self.write.insert(range.into_bounds()); } Type::Write => { - self.write.insert(range); + self.write.insert(range.into_bounds()); } Type::Read => { - self.read.insert(range); + self.read.insert(range.into_bounds()); } } } diff --git a/crates/xline/src/storage/auth_store/store.rs b/crates/xline/src/storage/auth_store/store.rs index 771b7c8b6..4ed70f2b4 100644 --- a/crates/xline/src/storage/auth_store/store.rs +++ b/crates/xline/src/storage/auth_store/store.rs @@ -18,8 +18,9 @@ use pbkdf2::{ }; use utils::parking_lot_lock::RwLockMap; use xlineapi::{ - command::{CommandResponse, KeyRange, SyncResponse}, + command::{CommandResponse, SyncResponse}, execute_error::ExecuteError, + keyrange::KeyRange, AuthInfo, }; @@ -1114,7 +1115,7 @@ impl AuthStore { if user.has_role(ROOT_ROLE) { return Ok(()); } - let key_range = KeyRange::new(key, range_end); + let key_range = KeyRange::new_etcd(key, range_end); if let Some(permissions) = self.permission_cache.read().user_permissions.get(username) { match perm_type { Type::Read => { @@ -1212,10 +1213,10 @@ mod test { user_permissions: HashMap::from([( "u".to_owned(), UserPermissions { - read: MergedRange::from_iter(vec![KeyRange::new("foo", "")]), + read: MergedRange::from_iter(vec![KeyRange::new_etcd("foo", "")]), write: MergedRange::from_iter(vec![ - KeyRange::new("foo", ""), - KeyRange::new("fop", "foz") + KeyRange::new_etcd("foo", ""), + KeyRange::new_etcd("fop", "foz") ]), }, )]), @@ -1372,8 +1373,8 @@ mod test { user_permissions: HashMap::from([( "u".to_owned(), UserPermissions { - read: MergedRange::from_iter(vec![KeyRange::new("foo", "")]), - write: MergedRange::from_iter(vec![KeyRange::new("foo", "")]), + read: MergedRange::from_iter(vec![KeyRange::new_etcd("foo", "")]), + write: MergedRange::from_iter(vec![KeyRange::new_etcd("foo", "")]), }, )]), role_to_users_map: HashMap::from([("r".to_owned(), vec!["u".to_owned()])]), diff --git a/crates/xline/src/storage/index.rs b/crates/xline/src/storage/index.rs index 27bc8138e..cbbe0c6f4 100644 --- a/crates/xline/src/storage/index.rs +++ b/crates/xline/src/storage/index.rs @@ -8,10 +8,9 @@ use crossbeam_skiplist::{map::Entry, SkipMap}; use itertools::Itertools; use parking_lot::{Mutex, RwLock}; use utils::parking_lot_lock::RwLockMap; -use xlineapi::command::KeyRange; +use xlineapi::keyrange::{EtcdKeyRange, KeyRange, RangeType, StdBoundRange}; use super::revision::{KeyRevision, Revision}; -use crate::server::command::RangeType; /// Operations for `Index` pub(crate) trait IndexOperate { @@ -163,7 +162,7 @@ impl Index { .collect(), RangeType::Range => self .inner - .range(KeyRange::new(key, range_end)) + .range(KeyRange::new_etcd(key, range_end)) .flat_map(|entry| { entry .value() @@ -280,7 +279,7 @@ impl IndexOperate for Index { .collect(), RangeType::Range => self .inner - .range(KeyRange::new(key, range_end)) + .range(KeyRange::new_etcd(key, range_end)) .filter_map(fmap_value(|revs| Index::get_revision(revs, revision))) .collect(), } @@ -374,7 +373,7 @@ impl IndexOperate for Index { .unzip(), RangeType::Range => self .inner - .range(KeyRange::new(key, range_end)) + .range(KeyRange::new_etcd(key, range_end)) .zip(0..) .filter_map(|(entry, i)| { entry.value().map_write(|mut revs| { @@ -529,7 +528,7 @@ impl IndexOperate for IndexState<'_> { .filter_map(|(_, revs)| Index::get_revision(revs.as_ref(), revision)) .collect(), RangeType::Range => self - .range_key_revisions(KeyRange::new(key, range_end)) + .range_key_revisions(KeyRange::new_etcd(key, range_end)) .into_iter() .filter_map(|(_, revs)| Index::get_revision(revs.as_ref(), revision)) .collect(), @@ -617,7 +616,7 @@ impl IndexOperate for IndexState<'_> { .unzip(), RangeType::AllKeys => self.delete_all(revision, sub_revision), RangeType::Range => { - self.delete_range(KeyRange::new(key, range_end), revision, sub_revision) + self.delete_range(KeyRange::new_etcd(key, range_end), revision, sub_revision) } }; diff --git a/crates/xline/src/storage/kv_store.rs b/crates/xline/src/storage/kv_store.rs index 0b35f7caf..fa330dc67 100644 --- a/crates/xline/src/storage/kv_store.rs +++ b/crates/xline/src/storage/kv_store.rs @@ -15,8 +15,9 @@ use tokio::sync::mpsc; use tracing::{debug, warn}; use utils::table_names::{KV_TABLE, META_TABLE}; use xlineapi::{ - command::{CommandResponse, KeyRange, SyncResponse}, + command::{CommandResponse, SyncResponse}, execute_error::ExecuteError, + keyrange::{KeyRange, ONE_KEY}, }; use super::{ @@ -145,7 +146,7 @@ impl KvStoreInner { /// Get previous `KeyValue` of a `KeyValue` pub(crate) fn get_prev_kv(&self, kv: &KeyValue) -> Option { - self.get_range(&kv.key, &[], kv.mod_revision.overflow_sub(1)) + self.get_range(&kv.key, ONE_KEY, kv.mod_revision.overflow_sub(1)) .ok()? .pop() } diff --git a/crates/xline/src/storage/kvwatcher.rs b/crates/xline/src/storage/kvwatcher.rs index ab6dd5955..3d8e578b8 100644 --- a/crates/xline/src/storage/kvwatcher.rs +++ b/crates/xline/src/storage/kvwatcher.rs @@ -20,7 +20,7 @@ use utils::{ task_manager::{tasks::TaskName, Listener, TaskManager}, write_vec, }; -use xlineapi::command::KeyRange; +use xlineapi::keyrange::KeyRange; use super::kv_store::KvStoreInner; use crate::rpc::{Event, KeyValue}; diff --git a/crates/xlineapi/src/command.rs b/crates/xlineapi/src/command.rs index 7021999c8..53d278baf 100644 --- a/crates/xlineapi/src/command.rs +++ b/crates/xlineapi/src/command.rs @@ -1,7 +1,4 @@ -use std::{ - collections::HashSet, - ops::{Bound, RangeBounds}, -}; +use std::collections::HashSet; use curp::{client::ClientApi, cmd::Command as CurpCommand}; use curp_external_api::cmd::{ConflictCheck, PbCodec, PbSerializeError}; @@ -9,9 +6,10 @@ use itertools::Itertools; use prost::Message; use serde::{Deserialize, Serialize}; +use crate::keyrange::KeyRange; use crate::{ - execute_error::ExecuteError, AuthInfo, PbCommand, PbCommandResponse, PbKeyRange, - PbSyncResponse, RequestWrapper, ResponseWrapper, + execute_error::ExecuteError, AuthInfo, PbCommand, PbCommandResponse, PbSyncResponse, + RequestWrapper, ResponseWrapper, }; /// The curp client trait object on the command of xline @@ -19,221 +17,6 @@ use crate::{ /// TODO: use `type CurpClient = impl ClientApi<...>` when `type_alias_impl_trait` stabilized pub type CurpClient = dyn ClientApi + Sync + Send + 'static; -/// Range start and end to get all keys -const UNBOUNDED: &[u8] = &[0_u8]; -/// Range end to get one key -const ONE_KEY: &[u8] = &[]; - -/// Key Range for Command -#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Hash)] -pub struct KeyRange { - /// Start of range - key: Bound>, - /// End of range - range_end: Bound>, -} - -impl KeyRange { - /// New `KeyRange` from `key` and `range_end` - #[inline] - pub fn new(start: impl Into>, end: impl Into>) -> Self { - let key_vec = start.into(); - let range_end_vec = end.into(); - let range_end = match range_end_vec.as_slice() { - UNBOUNDED => Bound::Unbounded, - ONE_KEY => Bound::Included(key_vec.clone()), - _ => Bound::Excluded(range_end_vec), - }; - let key = match key_vec.as_slice() { - UNBOUNDED => Bound::Unbounded, - _ => Bound::Included(key_vec), - }; - KeyRange { key, range_end } - } - - /// New `KeyRange` only contains one key - /// - /// # Panics - /// - /// Will panic if key is equal to `UNBOUNDED` - #[inline] - pub fn new_one_key(key: impl Into>) -> Self { - let key_vec = key.into(); - assert!( - key_vec.as_slice() != UNBOUNDED, - "Unbounded key is not allowed: {key_vec:?}", - ); - Self { - key: Bound::Included(key_vec.clone()), - range_end: Bound::Included(key_vec), - } - } - - /// Construct `KeyRange` directly from `start` and `end`, both included - /// - /// # Panics - /// - /// Will panic if `start` or `end` is `UNBOUNDED` - #[inline] - pub fn new_included(start: impl Into>, end: impl Into>) -> Self { - let key_vec = start.into(); - let range_end_vec = end.into(); - assert!( - key_vec.as_slice() != UNBOUNDED && range_end_vec != UNBOUNDED, - "Unbounded key is not allowed: {key_vec:?}" - ); - assert!( - range_end_vec.as_slice() != ONE_KEY, - "One key range is not allowed: {key_vec:?}" - ); - let range_end = Bound::Included(range_end_vec); - let key = Bound::Included(key_vec); - KeyRange { key, range_end } - } - - /// Check if `KeyRange` contains a key - #[must_use] - #[inline] - pub fn contains_key(&self, key: &[u8]) -> bool { - (match self.start_bound() { - Bound::Included(start) => start.as_slice() <= key, - Bound::Excluded(start) => start.as_slice() < key, - Bound::Unbounded => true, - }) && (match self.end_bound() { - Bound::Included(end) => key <= end.as_slice(), - Bound::Excluded(end) => key < end.as_slice(), - Bound::Unbounded => true, - }) - } - - /// Get end of range with prefix - /// - /// User will provide a start key when prefix is true, we need calculate the end key of `KeyRange` - #[allow(clippy::indexing_slicing)] // end[i] is always valid - #[must_use] - #[inline] - pub fn get_prefix(key: impl AsRef<[u8]>) -> Vec { - let key = key.as_ref(); - let mut end = key.to_vec(); - for i in (0..key.len()).rev() { - if key[i] < 0xFF { - end[i] = end[i].wrapping_add(1); - end.truncate(i.wrapping_add(1)); - return end; - } - } - // next prefix does not exist (e.g., 0xffff); - vec![0] - } - - /// unpack `KeyRange` to tuple - #[must_use] - #[inline] - pub fn unpack(self) -> (Bound>, Bound>) { - (self.key, self.range_end) - } - - /// start key of `KeyRange` - #[must_use] - #[inline] - pub fn range_start(&self) -> &[u8] { - match self.key { - Bound::Included(ref k) => k.as_slice(), - Bound::Excluded(_) => unreachable!("KeyRange::start_bound() cannot be Excluded"), - Bound::Unbounded => &[0], - } - } - - /// end key of `KeyRange` - #[must_use] - #[inline] - pub fn range_end(&self) -> &[u8] { - match self.range_end { - Bound::Included(_) => &[], - Bound::Excluded(ref k) => k.as_slice(), - Bound::Unbounded => &[0], - } - } -} - -impl RangeBounds> for KeyRange { - #[inline] - fn start_bound(&self) -> Bound<&Vec> { - match self.key { - Bound::Unbounded => Bound::Unbounded, - Bound::Included(ref k) => Bound::Included(k), - Bound::Excluded(_) => unreachable!("KeyRange::start_bound() cannot be Excluded"), - } - } - #[inline] - fn end_bound(&self) -> Bound<&Vec> { - match self.range_end { - Bound::Unbounded => Bound::Unbounded, - Bound::Included(ref k) => Bound::Included(k), - Bound::Excluded(ref k) => Bound::Excluded(k), - } - } -} - -impl From for KeyRange { - #[inline] - fn from(range: PbKeyRange) -> Self { - Self::new(range.key, range.range_end) - } -} - -impl From for PbKeyRange { - #[inline] - fn from(range: KeyRange) -> Self { - Self { - key: range.range_start().to_vec(), - range_end: range.range_end().to_vec(), - } - } -} - -impl ConflictCheck for KeyRange { - /// if `KeyRange` is overlapping (conflict) with another `KeyRange`, return true - #[inline] - fn is_conflict(&self, other: &Self) -> bool { - // s1 < s2 ? - if match (self.start_bound(), other.start_bound()) { - (Bound::Included(s1), Bound::Included(s2)) => { - if s1 == s2 { - return true; - } - s1 < s2 - } - (Bound::Included(_), Bound::Unbounded) => false, - (Bound::Unbounded, Bound::Included(_)) => true, - (Bound::Unbounded, Bound::Unbounded) => return true, - _ => unreachable!("KeyRange::start_bound() cannot be Excluded"), - } { - // s1 < s2 - // s2 < e1 ? - match (other.start_bound(), self.end_bound()) { - (Bound::Included(s2), Bound::Included(e1)) => s2 <= e1, - (Bound::Included(s2), Bound::Excluded(e1)) => s2 < e1, - (Bound::Included(_), Bound::Unbounded) => true, - // if other.start_bound() is Unbounded, program cannot enter this branch - // KeyRange::start_bound() cannot be Excluded - _ => unreachable!("other.start_bound() should be Include"), - } - } else { - // s2 < s1 - // s1 < e2 ? - match (self.start_bound(), other.end_bound()) { - (Bound::Included(s1), Bound::Included(e2)) => s1 <= e2, - (Bound::Included(s1), Bound::Excluded(e2)) => s1 < e2, - (Bound::Included(_), Bound::Unbounded) => true, - // if self.start_bound() is Unbounded, program cannot enter this branch - // KeyRange::start_bound() cannot be Excluded - _ => unreachable!("self.start_bound() should be Include"), - } - } - } -} - /// Command to run consensus protocol #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub struct Command { @@ -537,44 +320,6 @@ mod test { PutResponse, RangeRequest, Request, RequestOp, TxnRequest, }; - #[test] - fn test_key_range_conflict() { - let kr1 = KeyRange::new("a", "e"); - let kr2 = KeyRange::new_one_key("c"); - let kr3 = KeyRange::new_one_key("z"); - assert!(kr1.is_conflict(&kr2)); - assert!(!kr1.is_conflict(&kr3)); - assert!(KeyRange::new_included("a", "z").is_conflict(&KeyRange::new_included("a", "y"))); - assert!(KeyRange::new_included("c", "z").is_conflict(&KeyRange::new_included("a", "d"))); - assert!(KeyRange::new_included("c", "z").is_conflict(&KeyRange::new_included("a", "d"))); - assert!(KeyRange::new_included("a", "g").is_conflict(&KeyRange::new_included("e", "z"))); - assert!(!KeyRange::new_included("a", "c").is_conflict(&KeyRange::new_included("e", "z"))); - assert!(!KeyRange::new_included("c", "f").is_conflict(&KeyRange::new_included("i", "n"))); - } - - #[test] - fn test_key_range_prefix() { - assert_eq!(KeyRange::get_prefix(b"key"), b"kez"); - assert_eq!(KeyRange::get_prefix(b"z"), b"\x7b"); - assert_eq!(KeyRange::get_prefix(&[255]), b"\0"); - } - - #[test] - fn test_key_range_contains() { - let kr1 = KeyRange::new("a", "e"); - assert!(kr1.contains_key(b"b")); - assert!(!kr1.contains_key(b"e")); - let kr2 = KeyRange::new_one_key("c"); - assert!(kr2.contains_key(b"c")); - assert!(!kr2.contains_key(b"d")); - let kr3 = KeyRange::new("c", [0]); - assert!(kr3.contains_key(b"d")); - assert!(!kr3.contains_key(b"a")); - let kr4 = KeyRange::new([0], "e"); - assert!(kr4.contains_key(b"d")); - assert!(!kr4.contains_key(b"e")); - } - #[test] fn test_command_conflict() { let cmd1 = Command::new(RequestWrapper::DeleteRangeRequest(DeleteRangeRequest { @@ -785,9 +530,9 @@ mod test { let keys = txn_req.keys(); assert!(keys.contains(&KeyRange::new_one_key("a"))); - assert!(keys.contains(&KeyRange::new("b", "e"))); + assert!(keys.contains(&KeyRange::new_etcd("b", "e"))); assert!(keys.contains(&KeyRange::new_one_key("1"))); assert!(keys.contains(&KeyRange::new_one_key("2"))); - assert!(keys.contains(&KeyRange::new("3", "4"))); + assert!(keys.contains(&KeyRange::new_etcd("3", "4"))); } } diff --git a/crates/xlineapi/src/interval.rs b/crates/xlineapi/src/interval.rs deleted file mode 100644 index b9ec0e643..000000000 --- a/crates/xlineapi/src/interval.rs +++ /dev/null @@ -1,87 +0,0 @@ -use std::cmp; - -use utils::interval_map::Interval; - -use crate::command::KeyRange; - -impl From for Interval { - fn from(range: KeyRange) -> Self { - let start = range.range_start().to_vec(); - let end = match range.range_end() { - &[] => { - let mut end = start.clone(); - end.push(0); - BytesAffine::Bytes(end) - } - &[0] => BytesAffine::Unbounded, - bytes => BytesAffine::Bytes(bytes.to_vec()), - }; - Interval::new(BytesAffine::Bytes(start), end) - } -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum BytesAffine { - /// Bytes - Bytes(Vec), - /// Unbounded - Unbounded, -} - -impl BytesAffine { - pub fn new_key(bytes: impl Into>) -> Self { - Self::Bytes(bytes.into()) - } - - pub fn new_unbounded() -> Self { - Self::Unbounded - } -} - -impl PartialOrd for BytesAffine { - fn partial_cmp(&self, other: &Self) -> Option { - match (self, other) { - (BytesAffine::Bytes(x), BytesAffine::Bytes(y)) => x.partial_cmp(y), - (BytesAffine::Bytes(_), BytesAffine::Unbounded) => Some(cmp::Ordering::Less), - (BytesAffine::Unbounded, BytesAffine::Bytes(_)) => Some(cmp::Ordering::Greater), - (BytesAffine::Unbounded, BytesAffine::Unbounded) => Some(cmp::Ordering::Equal), - } - } -} - -impl Ord for BytesAffine { - fn cmp(&self, other: &Self) -> cmp::Ordering { - match (self, other) { - (BytesAffine::Bytes(x), BytesAffine::Bytes(y)) => x.cmp(y), - (BytesAffine::Bytes(_), BytesAffine::Unbounded) => cmp::Ordering::Less, - (BytesAffine::Unbounded, BytesAffine::Bytes(_)) => cmp::Ordering::Greater, - (BytesAffine::Unbounded, BytesAffine::Unbounded) => cmp::Ordering::Equal, - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn bytes_affine_cmp_is_ok() { - assert_eq!(BytesAffine::new_key("abc"), BytesAffine::new_key("abc")); - assert!(BytesAffine::new_key("a") < BytesAffine::new_key("b")); - assert!(BytesAffine::new_key("abcd") < BytesAffine::new_key("b")); - assert!(BytesAffine::new_key("abcd") < BytesAffine::new_unbounded()); - assert_eq!(BytesAffine::new_unbounded(), BytesAffine::new_unbounded()); - } - - #[test] - fn convert_from_key_range_is_ok() { - let range0 = KeyRange::new("a", "e"); - let range1 = KeyRange::new_one_key("f"); - let interval0: Interval = range0.into(); - let interval1: Interval = range1.into(); - assert_eq!(interval0.low, BytesAffine::new_key("a")); - assert_eq!(interval0.high, BytesAffine::new_key("e")); - assert_eq!(interval1.low, BytesAffine::new_key("f")); - assert_eq!(interval1.high, BytesAffine::new_key("f\0")); - } -} diff --git a/crates/xlineapi/src/keyrange.rs b/crates/xlineapi/src/keyrange.rs new file mode 100644 index 000000000..d23678604 --- /dev/null +++ b/crates/xlineapi/src/keyrange.rs @@ -0,0 +1,419 @@ +pub use crate::commandpb::KeyRange as EtcdKeyRange; +use curp_external_api::cmd::ConflictCheck; +use serde::{Deserialize, Serialize}; +use std::{cmp, ops::Bound}; +use utils::interval_map::Interval; + +pub type StdBoundRange = std::ops::Range>>; + +/// Range start and end to get all keys +pub const UNBOUNDED: &[u8] = &[0_u8]; +/// Range end to get one key +pub const ONE_KEY: &[u8] = &[]; + +/// Impl Sub1 for Vec, to make Excluded bound into Included bound. +trait Sub1 { + fn sub1(self) -> Self; +} + +impl Sub1 for Vec { + /// Sub 1 from the last byte of Vec + /// + /// # Example + /// + /// ```rust + /// use xlineapi::keyrange::Sub1; + /// let mut key = vec![5, 6, 7]; + /// assert_eq!(key.sub1(), vec![5, 6, 6]); + /// let mut key = vec![5, 6, 0]; + /// assert_eq!(key.sub1(), vec![5, 5, 255]); + /// ``` + fn sub1(mut self) -> Self { + debug_assert!( + self != UNBOUNDED && self != ONE_KEY, + "we cannot calculate the result without knowing the key" + ); + for i in self.iter_mut().rev() { + if *i != 0 { + *i -= 1; + return self; + } else { + *i = 0xff; + } + } + unreachable!("self cannot be a zero vector"); + } +} + +trait Add1 { + fn add1(self) -> Self; +} + +impl Add1 for Vec { + /// Add 1 from the last byte of Vec + /// + /// # Example + /// + /// ```rust + /// use xlineapi::keyrange::Add1; + /// assert_eq!(vec![5, 6, 7].add1(), vec![5, 6, 8]); + /// assert_eq!(vec![5, 6, 255].add1(), vec![5, 6]); + /// assert_eq!(vec![255, 255].add1(), vec![0]); + /// ``` + fn add1(mut self) -> Self { + for i in (0..self.len()).rev() { + if self[i] < 0xFF { + self[i] = self[i].wrapping_add(1); + self.truncate(i.wrapping_add(1)); + return self; + } + } + // next prefix does not exist (e.g., 0xffff); + vec![0] + } +} + +#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Hash)] +pub enum BytesAffine { + /// Bytes bound, could be either Included or Excluded + Bytes(Vec), + /// Unbounded + Unbounded, +} + +impl BytesAffine { + pub fn new_key(bytes: impl Into>) -> Self { + Self::Bytes(bytes.into()) + } + + pub fn new_unbounded() -> Self { + Self::Unbounded + } +} + +impl PartialOrd for BytesAffine { + fn partial_cmp(&self, other: &Self) -> Option { + match (self, other) { + (BytesAffine::Bytes(x), BytesAffine::Bytes(y)) => x.partial_cmp(y), + (BytesAffine::Bytes(_), BytesAffine::Unbounded) => Some(cmp::Ordering::Less), + (BytesAffine::Unbounded, BytesAffine::Bytes(_)) => Some(cmp::Ordering::Greater), + (BytesAffine::Unbounded, BytesAffine::Unbounded) => Some(cmp::Ordering::Equal), + } + } +} + +impl Ord for BytesAffine { + fn cmp(&self, other: &Self) -> cmp::Ordering { + match (self, other) { + (BytesAffine::Bytes(x), BytesAffine::Bytes(y)) => x.cmp(y), + (BytesAffine::Bytes(_), BytesAffine::Unbounded) => cmp::Ordering::Less, + (BytesAffine::Unbounded, BytesAffine::Bytes(_)) => cmp::Ordering::Greater, + (BytesAffine::Unbounded, BytesAffine::Unbounded) => cmp::Ordering::Equal, + } + } +} + +// since we use `BytesAffine` for both Included and Excluded, we don't need to implement `Into`. + +/// A Range of Vec, represent a [start, end) range. +#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Hash)] +pub struct KeyRange(Interval); + +impl KeyRange { + /// New `KeyRange` from `key` and `range_end` which are in etcd form. + #[inline] + pub fn new_etcd(start: impl Into>, end: impl Into>) -> Self { + let key_vec = start.into(); + let range_end_vec = end.into(); + let range_end = match range_end_vec.as_slice() { + UNBOUNDED => BytesAffine::Unbounded, + ONE_KEY => BytesAffine::Bytes(key_vec.clone().add1()), // turn into [key, key+1) + _ => BytesAffine::Bytes(range_end_vec), + }; + let key = match key_vec.as_slice() { + UNBOUNDED => BytesAffine::Unbounded, + _ => BytesAffine::Bytes(key_vec), + }; + Self(Interval::new(key, range_end)) + } + + /// New `KeyRange` only contains one key + /// + /// # Panics + /// + /// Will panic if key is equal to `UNBOUNDED` + #[inline] + pub fn new_one_key(key: impl Into>) -> Self { + let key_vec = key.into(); + assert!( + key_vec.as_slice() != UNBOUNDED, + "Unbounded key is not allowed: {key_vec:?}", + ); + Self(Interval::new( + BytesAffine::Bytes(key_vec.clone()), + BytesAffine::Bytes(key_vec.add1()), + )) + } + + /// Construct `KeyRange` directly from [`start`, `end`], both included + /// + /// # Panics + /// + /// Will panic if `start` or `end` is `UNBOUNDED` + #[inline] + pub fn new_included(start: impl Into>, end: impl Into>) -> Self { + let key_vec = start.into(); + let range_end_vec = end.into(); + assert!( + key_vec.as_slice() != UNBOUNDED && range_end_vec != UNBOUNDED, + "Unbounded key is not allowed: {key_vec:?}" + ); + assert!( + range_end_vec.as_slice() != ONE_KEY, + "One key range is not allowed: {key_vec:?}" + ); + let range_end = BytesAffine::Bytes(range_end_vec); + let key = BytesAffine::Bytes(key_vec.add1()); + KeyRange(Interval::new(key, range_end)) + } + + /// Check if `KeyRange` contains a key + #[must_use] + #[inline] + pub fn contains_key(&self, key: &[u8]) -> bool { + let key_aff = BytesAffine::Bytes(key.to_vec()); + self.0.low <= key_aff && key_aff < self.0.high + } + + /// Check if `KeyRange` overlaps with another `KeyRange` + #[inline] + pub fn overlap(&self, other: &Self) -> bool { + self.0.overlap(&other.0) + } + + /// Get end of range with prefix + /// + /// User will provide a start key when prefix is true, we need calculate the end key of `KeyRange` + #[must_use] + #[inline] + pub fn get_prefix(key: impl AsRef<[u8]>) -> Vec { + key.as_ref().to_vec().add1() + } + + /// unpack `KeyRange` to `BytesAffine` tuple + #[must_use] + #[inline] + pub fn into_parts(self) -> (BytesAffine, BytesAffine) { + self.0.into_parts() + } + + /// unpack `KeyRange` to `BytesAffine` tuple + #[must_use] + #[inline] + pub fn into_bounds(self) -> (std::ops::Bound>, std::ops::Bound>) { + ( + match self.0.low { + BytesAffine::Bytes(k) => std::collections::Bound::Included(k), + BytesAffine::Unbounded => std::collections::Bound::Unbounded, + }, + match self.0.high { + BytesAffine::Bytes(k) => std::collections::Bound::Excluded(k), + BytesAffine::Unbounded => std::collections::Bound::Unbounded, + }, + ) + } + + /// get the start slice in etcd form of `KeyRange` + #[must_use] + #[inline] + pub fn range_start(&self) -> &[u8] { + match self.0.low { + BytesAffine::Bytes(ref k) => k.as_slice(), + BytesAffine::Unbounded => &[0], + } + } + + /// get the end slice in etcd form of `KeyRange` + #[must_use] + #[inline] + pub fn range_end(&self) -> &[u8] { + match self.0.high { + BytesAffine::Bytes(ref k) => k.as_slice(), + BytesAffine::Unbounded => &[0], + } + } +} + +macro_rules! impl_trait_for_key_range { + ($($struct:ty),*) => { + $( + impl std::ops::RangeBounds> for $struct { + /// get the Bound of start in `KeyRange` + fn start_bound(&self) -> std::collections::Bound<&Vec> { + match self.0.low { + BytesAffine::Bytes(ref k) => std::collections::Bound::Included(k), + BytesAffine::Unbounded => std::collections::Bound::Unbounded, + } + } + /// get the Bound of end in `KeyRange` + fn end_bound(&self) -> std::collections::Bound<&Vec> { + match self.0.high { + BytesAffine::Bytes(ref k) => std::collections::Bound::Excluded(k), + BytesAffine::Unbounded => std::collections::Bound::Unbounded, + } + } + } + )* + }; +} +impl_trait_for_key_range!(KeyRange, &KeyRange); + +impl From for KeyRange { + #[inline] + fn from(range: EtcdKeyRange) -> Self { + Self::new_etcd(range.key, range.range_end) + } +} + +impl From for EtcdKeyRange { + #[inline] + fn from(range: KeyRange) -> Self { + Self { + key: range.range_start().to_vec(), + range_end: range.range_end().to_vec(), + } + } +} + +impl From for Interval { + #[inline] + fn from(range: KeyRange) -> Self { + range.0 + } +} + +impl From> for KeyRange { + #[inline] + fn from(range: Interval) -> Self { + Self(range) + } +} + +impl From for StdBoundRange { + fn from(value: KeyRange) -> Self { + let start = match value.0.low { + BytesAffine::Bytes(k) => std::ops::Bound::Included(k), + BytesAffine::Unbounded => std::ops::Bound::Unbounded, + }; + let end = match value.0.high { + BytesAffine::Bytes(k) => std::ops::Bound::Excluded(k), + BytesAffine::Unbounded => std::ops::Bound::Unbounded, + }; + start..end + } +} + +impl std::ops::Deref for KeyRange { + type Target = Interval; + #[inline] + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl ConflictCheck for KeyRange { + /// if `KeyRange` is overlapping (conflict) with another `KeyRange`, return true + #[inline] + fn is_conflict(&self, other: &Self) -> bool { + self.0.overlap(&other.0) + } +} + +/// Type of `KeyRange` +#[derive(Debug)] +pub enum RangeType { + /// `KeyRange` contains only one key + OneKey, + /// `KeyRange` contains all keys + AllKeys, + /// `KeyRange` contains the keys in the range + Range, +} + +impl RangeType { + /// Get `RangeType` by given `key` and `range_end` + #[inline] + pub fn get_range_type(key: &[u8], range_end: &[u8]) -> Self { + if range_end == ONE_KEY { + RangeType::OneKey + } else if key == UNBOUNDED && range_end == UNBOUNDED { + RangeType::AllKeys + } else { + RangeType::Range + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn bytes_affine_cmp_is_ok() { + assert_eq!(BytesAffine::new_key("abc"), BytesAffine::new_key("abc")); + assert!(BytesAffine::new_key("a") < BytesAffine::new_key("b")); + assert!(BytesAffine::new_key("abcd") < BytesAffine::new_key("b")); + assert!(BytesAffine::new_key("abcd") < BytesAffine::new_unbounded()); + assert_eq!(BytesAffine::new_unbounded(), BytesAffine::new_unbounded()); + } + + #[test] + fn convert_from_key_range_is_ok() { + let range0 = KeyRange::new_etcd("a", "e"); + let range1 = KeyRange::new_one_key("f"); + let interval0: Interval = range0.into(); + let interval1: Interval = range1.into(); + assert_eq!(interval0.low, BytesAffine::new_key("a")); + assert_eq!(interval0.high, BytesAffine::new_key("e")); + assert_eq!(interval1.low, BytesAffine::new_key("f")); + assert_eq!(interval1.high, BytesAffine::new_key("f\0")); + } + + #[test] + fn test_key_range_conflict() { + let kr1 = KeyRange::new_etcd("a", "e"); + let kr2 = KeyRange::new_one_key("c"); + let kr3 = KeyRange::new_one_key("z"); + assert!(kr1.is_conflict(&kr2)); + assert!(!kr1.is_conflict(&kr3)); + assert!(KeyRange::new_included("a", "z").is_conflict(&KeyRange::new_included("a", "y"))); + assert!(KeyRange::new_included("c", "z").is_conflict(&KeyRange::new_included("a", "d"))); + assert!(KeyRange::new_included("c", "z").is_conflict(&KeyRange::new_included("a", "d"))); + assert!(KeyRange::new_included("a", "g").is_conflict(&KeyRange::new_included("e", "z"))); + assert!(!KeyRange::new_included("a", "c").is_conflict(&KeyRange::new_included("e", "z"))); + assert!(!KeyRange::new_included("c", "f").is_conflict(&KeyRange::new_included("i", "n"))); + } + + #[test] + fn test_key_range_prefix() { + assert_eq!(KeyRange::get_prefix(b"key"), b"kez"); + assert_eq!(KeyRange::get_prefix(b"z"), b"\x7b"); + assert_eq!(KeyRange::get_prefix(&[255]), b"\0"); + } + + #[test] + fn test_key_range_contains() { + let kr1 = KeyRange::new_etcd("a", "e"); + assert!(kr1.contains_key(b"b")); + assert!(!kr1.contains_key(b"e")); + let kr2 = KeyRange::new_one_key("c"); + assert!(kr2.contains_key(b"c")); + assert!(!kr2.contains_key(b"d")); + let kr3 = KeyRange::new_etcd("c", [0]); + assert!(kr3.contains_key(b"d")); + assert!(!kr3.contains_key(b"a")); + let kr4 = KeyRange::new_etcd([0], "e"); + assert!(kr4.contains_key(b"d")); + assert!(!kr4.contains_key(b"e")); + } +} diff --git a/crates/xlineapi/src/lib.rs b/crates/xlineapi/src/lib.rs index c152912b8..b29a46d91 100644 --- a/crates/xlineapi/src/lib.rs +++ b/crates/xlineapi/src/lib.rs @@ -174,7 +174,7 @@ pub mod command; pub mod execute_error; -pub mod interval; +pub mod keyrange; pub mod request_validation; mod etcdserverpb { @@ -207,7 +207,7 @@ mod errorpb { use std::fmt::Display; -use command::KeyRange; +use keyrange::KeyRange; use utils::write_vec; pub use self::{ @@ -215,8 +215,7 @@ pub use self::{ commandpb::{ command::{AuthInfo, RequestWrapper}, command_response::ResponseWrapper, - Command as PbCommand, CommandResponse as PbCommandResponse, KeyRange as PbKeyRange, - SyncResponse as PbSyncResponse, + Command as PbCommand, CommandResponse as PbCommandResponse, SyncResponse as PbSyncResponse, }, errorpb::{ execute_error::Error as PbExecuteError, ExecuteError as PbExecuteErrorOuter, @@ -341,7 +340,7 @@ pub trait CommandAttr { impl CommandAttr for RangeRequest { fn keys(&self) -> Vec { - vec![KeyRange::new( + vec![KeyRange::new_etcd( self.key.as_slice(), self.range_end.as_slice(), )] @@ -364,7 +363,7 @@ impl CommandAttr for PutRequest { impl CommandAttr for DeleteRangeRequest { fn keys(&self) -> Vec { - vec![KeyRange::new( + vec![KeyRange::new_etcd( self.key.as_slice(), self.range_end.as_slice(), )] @@ -380,7 +379,7 @@ impl CommandAttr for TxnRequest { let mut keys: Vec<_> = self .compare .iter() - .map(|cmp| KeyRange::new(cmp.key.as_slice(), cmp.range_end.as_slice())) + .map(|cmp| KeyRange::new_etcd(cmp.key.as_slice(), cmp.range_end.as_slice())) .collect(); for op in self @@ -392,14 +391,18 @@ impl CommandAttr for TxnRequest { { match *op { Request::RequestRange(ref req) => { - keys.push(KeyRange::new(req.key.as_slice(), req.range_end.as_slice())); + keys.push(KeyRange::new_etcd( + req.key.as_slice(), + req.range_end.as_slice(), + )); } Request::RequestPut(ref req) => { keys.push(KeyRange::new_one_key(req.key.as_slice())) } - Request::RequestDeleteRange(ref req) => { - keys.push(KeyRange::new(req.key.as_slice(), req.range_end.as_slice())) - } + Request::RequestDeleteRange(ref req) => keys.push(KeyRange::new_etcd( + req.key.as_slice(), + req.range_end.as_slice(), + )), Request::RequestTxn(ref req) => keys.append(&mut req.keys()), } } diff --git a/crates/xlineapi/src/request_validation.rs b/crates/xlineapi/src/request_validation.rs index ff6ff9a86..d1f9b693b 100644 --- a/crates/xlineapi/src/request_validation.rs +++ b/crates/xlineapi/src/request_validation.rs @@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize}; use thiserror::Error; use crate::{ - command::KeyRange, AuthRoleAddRequest, AuthRoleGrantPermissionRequest, AuthUserAddRequest, + keyrange::KeyRange, AuthRoleAddRequest, AuthRoleGrantPermissionRequest, AuthUserAddRequest, DeleteRangeRequest, PutRequest, RangeRequest, Request, RequestOp, SortOrder, SortTarget, TxnRequest, }; @@ -101,7 +101,7 @@ fn check_intervals(ops: &[RequestOp]) -> Result<(HashSet<&[u8]>, Vec), for op in ops { if let Some(Request::RequestDeleteRange(ref req)) = op.request { // collect dels - let del = KeyRange::new(req.key.as_slice(), req.range_end.as_slice()); + let del = KeyRange::new_etcd(req.key.as_slice(), req.range_end.as_slice()); dels.push(del); } }