Skip to content

Commit 74c1cc9

Browse files
authored
Merge pull request #195 from muzarski/rack-awareness
lbp: rack awareness
2 parents 6ac2ec3 + 8ed8d15 commit 74c1cc9

File tree

3 files changed

+288
-25
lines changed

3 files changed

+288
-25
lines changed

include/cassandra.h

Lines changed: 84 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1087,6 +1087,50 @@ cass_execution_profile_set_load_balance_dc_aware_n(CassExecProfile* profile,
10871087
unsigned used_hosts_per_remote_dc,
10881088
cass_bool_t allow_remote_dcs_for_local_cl);
10891089

1090+
1091+
/**
1092+
* Configures the execution profile to use Rack-aware load balancing.
1093+
* For each query, all live nodes in a primary 'local' rack are tried first,
1094+
* followed by nodes from local DC and then nodes from other DCs.
1095+
*
1096+
* <b>Note:</b> Profile-based load balancing policy is disabled by default.
1097+
* cluster load balancing policy is used when profile does not contain a policy.
1098+
*
1099+
* @public @memberof CassExecProfile
1100+
*
1101+
* @param[in] profile
1102+
* @param[in] local_dc The primary data center to try first
1103+
* @param[in] local_rack The primary rack to try first
1104+
* @return CASS_OK if successful, otherwise an error occurred
1105+
*/
1106+
CASS_EXPORT CassError
1107+
cass_execution_profile_set_load_balance_rack_aware(CassExecProfile* profile,
1108+
const char* local_dc,
1109+
const char* local_rack);
1110+
1111+
1112+
/**
1113+
* Same as cass_execution_profile_set_load_balance_rack_aware(), but with lengths for string
1114+
* parameters.
1115+
*
1116+
* @public @memberof CassExecProfile
1117+
*
1118+
* @param[in] profile
1119+
* @param[in] local_dc
1120+
* @param[in] local_dc_length
1121+
* @return same cass_execution_profile_set_load_balance_rack_aware()
1122+
*
1123+
* @see cass_execution_profile_set_load_balance_rack_aware()
1124+
* @see cass_cluster_set_load_balance_rack_aware_n()
1125+
*/
1126+
CASS_EXPORT CassError
1127+
cass_execution_profile_set_load_balance_rack_aware_n(CassExecProfile* profile,
1128+
const char* local_dc,
1129+
size_t local_dc_length,
1130+
const char* local_rack,
1131+
size_t local_rack_length);
1132+
1133+
10901134
/**
10911135
* Configures the execution profile to use token-aware request routing or not.
10921136
*
@@ -2176,13 +2220,6 @@ cass_cluster_set_load_balance_round_robin(CassCluster* cluster);
21762220
* For each query, all live nodes in a primary 'local' DC are tried first,
21772221
* followed by any node from other DCs.
21782222
*
2179-
* <b>Note:</b> This is the default, and does not need to be called unless
2180-
* switching an existing from another policy or changing settings.
2181-
* Without further configuration, a default local_dc is chosen from the
2182-
* first connected contact point, and no remote hosts are considered in
2183-
* query plans. If relying on this mechanism, be sure to use only contact
2184-
* points from the local DC.
2185-
*
21862223
* @deprecated The remote DC settings for DC-aware are not suitable for most
21872224
* scenarios that require DC failover. There is also unhandled gap between
21882225
* replication factor number of nodes failing and the full cluster failing. Only
@@ -2233,6 +2270,46 @@ cass_cluster_set_load_balance_dc_aware_n(CassCluster* cluster,
22332270
unsigned used_hosts_per_remote_dc,
22342271
cass_bool_t allow_remote_dcs_for_local_cl);
22352272

2273+
2274+
/**
2275+
* Configures the cluster to use Rack-aware load balancing.
2276+
* For each query, all live nodes in a primary 'local' rack are tried first,
2277+
* followed by nodes from local DC and then nodes from other DCs.
2278+
*
2279+
* @public @memberof CassCluster
2280+
*
2281+
* @param[in] cluster
2282+
* @param[in] local_dc The primary data center to try first
2283+
* @param[in] local_rack The primary rack to try first
2284+
* @return CASS_OK if successful, otherwise an error occurred
2285+
*/
2286+
CASS_EXPORT CassError
2287+
cass_cluster_set_load_balance_rack_aware(CassCluster* cluster,
2288+
const char* local_dc,
2289+
const char* local_rack);
2290+
2291+
2292+
/**
2293+
* Same as cass_cluster_set_load_balance_rack_aware(), but with lengths for string
2294+
* parameters.
2295+
*
2296+
* @public @memberof CassCluster
2297+
*
2298+
* @param[in] cluster
2299+
* @param[in] local_dc
2300+
* @param[in] local_dc_length
2301+
* @return same as cass_cluster_set_load_balance_dc_aware()
2302+
*
2303+
* @see cass_cluster_set_load_balance_dc_aware()
2304+
*/
2305+
CASS_EXPORT CassError
2306+
cass_cluster_set_load_balance_rack_aware_n(CassCluster* cluster,
2307+
const char* local_dc,
2308+
size_t local_dc_length,
2309+
const char* local_rack,
2310+
size_t local_rack_length);
2311+
2312+
22362313
/**
22372314
* Configures the cluster to use token-aware request routing or not.
22382315
*

scylla-rust-wrapper/src/cluster.rs

Lines changed: 166 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,22 @@ impl LoadBalancingConfig {
7575
builder =
7676
builder.enable_shuffling_replicas(self.token_aware_shuffling_replicas_enabled);
7777
}
78-
if let LoadBalancingKind::DcAware { local_dc } = load_balancing_kind {
79-
builder = builder.prefer_datacenter(local_dc).permit_dc_failover(true)
78+
79+
match load_balancing_kind {
80+
LoadBalancingKind::DcAware { local_dc } => {
81+
builder = builder.prefer_datacenter(local_dc).permit_dc_failover(true)
82+
}
83+
LoadBalancingKind::RackAware {
84+
local_dc,
85+
local_rack,
86+
} => {
87+
builder = builder
88+
.prefer_datacenter_and_rack(local_dc, local_rack)
89+
.permit_dc_failover(true)
90+
}
91+
LoadBalancingKind::RoundRobin => {}
8092
}
93+
8194
if self.latency_awareness_enabled {
8295
builder = builder.latency_awareness(self.latency_awareness_builder);
8396
}
@@ -99,7 +112,13 @@ impl Default for LoadBalancingConfig {
99112
#[derive(Clone, Debug)]
100113
pub(crate) enum LoadBalancingKind {
101114
RoundRobin,
102-
DcAware { local_dc: String },
115+
DcAware {
116+
local_dc: String,
117+
},
118+
RackAware {
119+
local_dc: String,
120+
local_rack: String,
121+
},
103122
}
104123

105124
#[derive(Clone)]
@@ -553,6 +572,68 @@ pub unsafe extern "C" fn cass_cluster_set_load_balance_dc_aware_n(
553572
)
554573
}
555574

575+
#[no_mangle]
576+
pub unsafe extern "C" fn cass_cluster_set_load_balance_rack_aware(
577+
cluster_raw: *mut CassCluster,
578+
local_dc_raw: *const c_char,
579+
local_rack_raw: *const c_char,
580+
) -> CassError {
581+
cass_cluster_set_load_balance_rack_aware_n(
582+
cluster_raw,
583+
local_dc_raw,
584+
strlen(local_dc_raw),
585+
local_rack_raw,
586+
strlen(local_rack_raw),
587+
)
588+
}
589+
590+
#[no_mangle]
591+
pub unsafe extern "C" fn cass_cluster_set_load_balance_rack_aware_n(
592+
cluster_raw: *mut CassCluster,
593+
local_dc_raw: *const c_char,
594+
local_dc_length: size_t,
595+
local_rack_raw: *const c_char,
596+
local_rack_length: size_t,
597+
) -> CassError {
598+
let cluster = ptr_to_ref_mut(cluster_raw);
599+
600+
set_load_balance_rack_aware_n(
601+
&mut cluster.load_balancing_config,
602+
local_dc_raw,
603+
local_dc_length,
604+
local_rack_raw,
605+
local_rack_length,
606+
)
607+
}
608+
609+
pub(crate) unsafe fn set_load_balance_rack_aware_n(
610+
load_balancing_config: &mut LoadBalancingConfig,
611+
local_dc_raw: *const c_char,
612+
local_dc_length: size_t,
613+
local_rack_raw: *const c_char,
614+
local_rack_length: size_t,
615+
) -> CassError {
616+
let (local_dc, local_rack) = match (
617+
ptr_to_cstr_n(local_dc_raw, local_dc_length),
618+
ptr_to_cstr_n(local_rack_raw, local_rack_length),
619+
) {
620+
(Some(local_dc_str), Some(local_rack_str))
621+
if local_dc_length > 0 && local_rack_length > 0 =>
622+
{
623+
(local_dc_str.to_owned(), local_rack_str.to_owned())
624+
}
625+
// One of them either is a null pointer, is an empty string or is not a proper utf-8.
626+
_ => return CassError::CASS_ERROR_LIB_BAD_PARAMS,
627+
};
628+
629+
load_balancing_config.load_balancing_kind = Some(LoadBalancingKind::RackAware {
630+
local_dc,
631+
local_rack,
632+
});
633+
634+
CassError::CASS_OK
635+
}
636+
556637
#[no_mangle]
557638
pub unsafe extern "C" fn cass_cluster_set_cloud_secure_connection_bundle_n(
558639
_cluster_raw: *mut CassCluster,
@@ -891,12 +972,7 @@ mod tests {
891972
{
892973
cass_cluster_set_token_aware_routing(cluster_raw, 0);
893974
assert_cass_error_eq!(
894-
cass_cluster_set_load_balance_dc_aware(
895-
cluster_raw,
896-
"eu\0".as_ptr() as *const i8,
897-
0,
898-
0
899-
),
975+
cass_cluster_set_load_balance_dc_aware(cluster_raw, c"eu".as_ptr(), 0, 0),
900976
CassError::CASS_OK
901977
);
902978
cass_cluster_set_latency_aware_routing(cluster_raw, 1);
@@ -920,25 +996,98 @@ mod tests {
920996
}
921997
assert!(!cluster.load_balancing_config.token_awareness_enabled);
922998
assert!(cluster.load_balancing_config.latency_awareness_enabled);
999+
1000+
// set preferred rack+dc
1001+
assert_cass_error_eq!(
1002+
cass_cluster_set_load_balance_rack_aware(
1003+
cluster_raw,
1004+
c"eu-east".as_ptr(),
1005+
c"rack1".as_ptr(),
1006+
),
1007+
CassError::CASS_OK
1008+
);
1009+
1010+
let node_location_preference =
1011+
&cluster.load_balancing_config.load_balancing_kind;
1012+
match node_location_preference {
1013+
Some(LoadBalancingKind::RackAware {
1014+
local_dc,
1015+
local_rack,
1016+
}) => {
1017+
assert_eq!(local_dc, "eu-east");
1018+
assert_eq!(local_rack, "rack1");
1019+
}
1020+
_ => panic!("Expected preferred dc and rack"),
1021+
}
1022+
1023+
// set back to preferred dc
1024+
assert_cass_error_eq!(
1025+
cass_cluster_set_load_balance_dc_aware(cluster_raw, c"eu".as_ptr(), 0, 0),
1026+
CassError::CASS_OK
1027+
);
1028+
1029+
let node_location_preference =
1030+
&cluster.load_balancing_config.load_balancing_kind;
1031+
match node_location_preference {
1032+
Some(LoadBalancingKind::DcAware { local_dc }) => {
1033+
assert_eq!(local_dc, "eu")
1034+
}
1035+
_ => panic!("Expected preferred dc"),
1036+
}
9231037
}
9241038
/* Test invalid configurations */
9251039
{
9261040
// Nonzero deprecated parameters
9271041
assert_cass_error_eq!(
928-
cass_cluster_set_load_balance_dc_aware(
1042+
cass_cluster_set_load_balance_dc_aware(cluster_raw, c"eu".as_ptr(), 1, 0),
1043+
CassError::CASS_ERROR_LIB_BAD_PARAMS
1044+
);
1045+
assert_cass_error_eq!(
1046+
cass_cluster_set_load_balance_dc_aware(cluster_raw, c"eu".as_ptr(), 0, 1),
1047+
CassError::CASS_ERROR_LIB_BAD_PARAMS
1048+
);
1049+
1050+
// null pointers
1051+
assert_cass_error_eq!(
1052+
cass_cluster_set_load_balance_dc_aware(cluster_raw, std::ptr::null(), 0, 0),
1053+
CassError::CASS_ERROR_LIB_BAD_PARAMS
1054+
);
1055+
assert_cass_error_eq!(
1056+
cass_cluster_set_load_balance_rack_aware(
1057+
cluster_raw,
1058+
c"eu".as_ptr(),
1059+
std::ptr::null(),
1060+
),
1061+
CassError::CASS_ERROR_LIB_BAD_PARAMS
1062+
);
1063+
assert_cass_error_eq!(
1064+
cass_cluster_set_load_balance_rack_aware(
1065+
cluster_raw,
1066+
std::ptr::null(),
1067+
c"rack".as_ptr(),
1068+
),
1069+
CassError::CASS_ERROR_LIB_BAD_PARAMS
1070+
);
1071+
1072+
// empty strings
1073+
let empty_str = "\0".as_ptr() as *const i8;
1074+
assert_cass_error_eq!(
1075+
cass_cluster_set_load_balance_dc_aware(cluster_raw, std::ptr::null(), 0, 0),
1076+
CassError::CASS_ERROR_LIB_BAD_PARAMS
1077+
);
1078+
assert_cass_error_eq!(
1079+
cass_cluster_set_load_balance_rack_aware(
9291080
cluster_raw,
930-
"eu\0".as_ptr() as *const i8,
931-
1,
932-
0
1081+
c"eu".as_ptr(),
1082+
empty_str,
9331083
),
9341084
CassError::CASS_ERROR_LIB_BAD_PARAMS
9351085
);
9361086
assert_cass_error_eq!(
937-
cass_cluster_set_load_balance_dc_aware(
1087+
cass_cluster_set_load_balance_rack_aware(
9381088
cluster_raw,
939-
"eu\0".as_ptr() as *const i8,
940-
0,
941-
1
1089+
empty_str,
1090+
c"rack".as_ptr(),
9421091
),
9431092
CassError::CASS_ERROR_LIB_BAD_PARAMS
9441093
);

scylla-rust-wrapper/src/exec_profile.rs

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@ use crate::argconv::{free_boxed, ptr_to_cstr_n, ptr_to_ref, ptr_to_ref_mut, strl
1717
use crate::batch::CassBatch;
1818
use crate::cass_error::CassError;
1919
use crate::cass_types::CassConsistency;
20-
use crate::cluster::{set_load_balance_dc_aware_n, LoadBalancingConfig, LoadBalancingKind};
20+
use crate::cluster::{
21+
set_load_balance_dc_aware_n, set_load_balance_rack_aware_n, LoadBalancingConfig,
22+
LoadBalancingKind,
23+
};
2124
use crate::retry_policy::CassRetryPolicy;
2225
use crate::retry_policy::RetryPolicy::{
2326
DefaultRetryPolicy, DowngradingConsistencyRetryPolicy, FallthroughRetryPolicy,
@@ -357,6 +360,40 @@ pub unsafe extern "C" fn cass_execution_profile_set_load_balance_dc_aware_n(
357360
)
358361
}
359362

363+
#[no_mangle]
364+
pub unsafe extern "C" fn cass_execution_profile_set_load_balance_rack_aware(
365+
profile: *mut CassExecProfile,
366+
local_dc_raw: *const c_char,
367+
local_rack_raw: *const c_char,
368+
) -> CassError {
369+
cass_execution_profile_set_load_balance_rack_aware_n(
370+
profile,
371+
local_dc_raw,
372+
strlen(local_dc_raw),
373+
local_rack_raw,
374+
strlen(local_rack_raw),
375+
)
376+
}
377+
378+
#[no_mangle]
379+
pub unsafe extern "C" fn cass_execution_profile_set_load_balance_rack_aware_n(
380+
profile: *mut CassExecProfile,
381+
local_dc_raw: *const c_char,
382+
local_dc_length: size_t,
383+
local_rack_raw: *const c_char,
384+
local_rack_length: size_t,
385+
) -> CassError {
386+
let profile_builder = ptr_to_ref_mut(profile);
387+
388+
set_load_balance_rack_aware_n(
389+
&mut profile_builder.load_balancing_config,
390+
local_dc_raw,
391+
local_dc_length,
392+
local_rack_raw,
393+
local_rack_length,
394+
)
395+
}
396+
360397
#[no_mangle]
361398
pub unsafe extern "C" fn cass_execution_profile_set_load_balance_round_robin(
362399
profile: *mut CassExecProfile,

0 commit comments

Comments
 (0)