Skip to content

Commit

Permalink
Add Integration Testing for correctness of scaling and non scaling fi…
Browse files Browse the repository at this point in the history
…lters, and maxmemory, memory usage, type, encoding, etc
  • Loading branch information
KarthikSubbarao committed Oct 9, 2024
1 parent f299216 commit c7a3aae
Show file tree
Hide file tree
Showing 5 changed files with 334 additions and 42 deletions.
67 changes: 26 additions & 41 deletions src/bloom/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -254,14 +254,17 @@ mod tests {
) -> (i64, i64) {
let mut new_item_idx = starting_item_idx;
let mut fp_count = 0;
while bf.cardinality() < capacity_needed {
let mut cardinality = bf.cardinality();
while cardinality < capacity_needed {
let item = format!("{}{}", rand_prefix, new_item_idx);
let result = bf.add_item(item.as_bytes());
match result {
Ok(0) => {
fp_count += 1;
}
Ok(1) => {}
Ok(1) => {
cardinality += 1;
}
Ok(i64::MIN..=-1_i64) | Ok(2_i64..=i64::MAX) => {
panic!("We do not expect add_item to return any Integer other than 0 or 1.")
}
Expand All @@ -271,22 +274,21 @@ mod tests {
};
new_item_idx += 1;
}
(fp_count, new_item_idx)
(fp_count, new_item_idx - 1)
}

/// Loops from the start index till the end index and uses the exists operation on the provided bloom filter.
/// The item name used in exists operations is rand_prefix + the index (based on the iteration).
/// The results are matched against the `expected_result` and an error_count tracks the wrong results.
/// Asserts that the error_count is within the expected false positive (+ margin) rate.
fn item_exists_test(
/// Returns the error count and number of operations performed.
fn check_items_exist(
bf: &BloomFilterType,
start_idx: i64,
end_idx: i64,
expected_fp_rate: f32,
fp_margin: f32,
expected_result: bool,
rand_prefix: &String,
) {
) -> (i64, i64) {
let mut error_count = 0;
for i in start_idx..=end_idx {
let item = format!("{}{}", rand_prefix, i);
Expand All @@ -296,8 +298,7 @@ mod tests {
}
}
let num_operations = (end_idx - start_idx) + 1;
// Validate that the real fp_rate is not much more than the configured fp_rate.
fp_assert(error_count, num_operations, expected_fp_rate, fp_margin);
(error_count, num_operations)
}

fn fp_assert(error_count: i64, num_operations: i64, expected_fp_rate: f32, fp_margin: f32) {
Expand Down Expand Up @@ -364,24 +365,22 @@ mod tests {
.filters
.iter()
.any(|filter| filter.bloom.bitmap() == restore_filter.bloom.bitmap())));
item_exists_test(
let (error_count, _) = check_items_exist(
restored_bloom_filter_type,
1,
add_operation_idx,
expected_fp_rate,
fp_margin,
true,
rand_prefix,
);
item_exists_test(
assert!(error_count == 0);
let (error_count, num_operations) = check_items_exist(
restored_bloom_filter_type,
add_operation_idx + 1,
add_operation_idx * 2,
expected_fp_rate,
fp_margin,
false,
rand_prefix,
);
fp_assert(error_count, num_operations, expected_fp_rate, fp_margin);
}

#[test]
Expand Down Expand Up @@ -410,25 +409,18 @@ mod tests {
fp_assert(error_count, add_operation_idx, expected_fp_rate, fp_margin);
// Validate item "exists" operations on bloom filters are ensuring correctness.
// This tests for items already added to the filter and expects them to exist.
item_exists_test(
&bf,
1,
add_operation_idx,
expected_fp_rate,
fp_margin,
true,
&rand_prefix,
);
let (error_count, _) = check_items_exist(&bf, 1, add_operation_idx, true, &rand_prefix);
assert!(error_count == 0);
// This tests for items which are not added to the filter and expects them to not exist.
item_exists_test(
let (error_count, num_operations) = check_items_exist(
&bf,
add_operation_idx + 1,
add_operation_idx * 2,
expected_fp_rate,
fp_margin,
false,
&rand_prefix,
);
// Validate that the real fp_rate is not much more than the configured fp_rate.
fp_assert(error_count, num_operations, expected_fp_rate, fp_margin);

// Verify restore
let mut restore_bf = BloomFilterType::create_copy_from(&bf);
Expand Down Expand Up @@ -458,14 +450,14 @@ mod tests {
assert_eq!(bf.capacity(), initial_capacity as i64);
assert_eq!(bf.cardinality(), 0);
let mut total_error_count = 0;
let mut add_operation_idx = 1;
let mut add_operation_idx = 0;
// Validate the scaling behavior of the bloom filter.
for filter_idx in 1..=num_filters_to_scale {
let expected_total_capacity = initial_capacity * (expansion.pow(filter_idx) - 1);
let (error_count, new_add_operation_idx) = add_items_till_capacity(
&mut bf,
expected_total_capacity as i64,
add_operation_idx,
add_operation_idx + 1,
&rand_prefix,
);
add_operation_idx = new_add_operation_idx;
Expand All @@ -486,25 +478,18 @@ mod tests {
);
// Validate item "exists" operations on bloom filters are ensuring correctness.
// This tests for items already added to the filter and expects them to exist.
item_exists_test(
&bf,
1,
add_operation_idx,
expected_fp_rate,
fp_margin,
true,
&rand_prefix,
);
let (error_count, _) = check_items_exist(&bf, 1, add_operation_idx, true, &rand_prefix);
assert!(error_count == 0);
// This tests for items which are not added to the filter and expects them to not exist.
item_exists_test(
let (error_count, num_operations) = check_items_exist(
&bf,
add_operation_idx + 1,
add_operation_idx * 2,
expected_fp_rate,
fp_margin,
false,
&rand_prefix,
);
// Validate that the real fp_rate is not much more than the configured fp_rate.
fp_assert(error_count, num_operations, expected_fp_rate, fp_margin);

// Verify restore
let restore_bloom_filter_type = BloomFilterType::create_copy_from(&bf);
Expand Down
34 changes: 34 additions & 0 deletions tests/test_basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ def get_custom_args(self):

def test_basic(self):
client = self.server.get_new_client()
# Validate that the valkey-bloom module is loaded.
module_list_data = client.execute_command('MODULE LIST')
module_list_count = len(module_list_data)
assert module_list_count == 1
Expand All @@ -25,9 +26,42 @@ def test_basic(self):
module_loaded = True
break
assert(module_loaded)
# Validate that all the BF.* commands are supported on the server.
command_cmd_result = client.execute_command('COMMAND')
bf_cmds = ["BF.ADD", "BF.EXISTS", "BF.MADD", "BF.MEXISTS", "BF.INFO", "BF.CARD", "BF.RESERVE", "BF.INSERT"]
assert all(item in command_cmd_result for item in bf_cmds)
# Basic bloom filter create, item add and item exists validation.
bf_add_result = client.execute_command('BF.ADD filter1 item1')
assert bf_add_result == 1
bf_exists_result = client.execute_command('BF.EXISTS filter1 item1')
assert bf_exists_result == 1
bf_exists_result = client.execute_command('BF.EXISTS filter1 item2')
assert bf_exists_result == 0

def test_copy_and_exists_cmd(self):
client = self.server.get_new_client()
madd_result = client.execute_command('BF.MADD filter item1 item2 item3 item4')
assert client.execute_command('EXISTS filter') == 1
mexists_result = client.execute_command('BF.MEXISTS filter item1 item2 item3 item4')
assert len(madd_result) == 4 and len(mexists_result) == 4
assert client.execute_command('COPY filter new_filter') == 1
assert client.execute_command('EXISTS new_filter') == 1
copy_mexists_result = client.execute_command('BF.MEXISTS new_filter item1 item2 item3 item4')
assert mexists_result == copy_mexists_result

def test_memory_usage_cmd(self):
client = self.server.get_new_client()
assert client.execute_command('BF.ADD filter item1') == 1
memory_usage = client.execute_command('MEMORY USAGE filter')
info_size = client.execute_command('BF.INFO filter SIZE')
assert memory_usage > info_size and info_size > 0 # DIFF is 32 bytes

def test_module_data_type(self):
# Validate the name of the Module data type.
client = self.server.get_new_client()
assert client.execute_command('BF.ADD filter item1') == 1
type_result = client.execute_command('TYPE filter')
assert type_result == b"bloomfltr"
# Validate the name of the Module data type.
encoding_result = client.execute_command('OBJECT ENCODING filter')
assert encoding_result == b"raw"
Loading

0 comments on commit c7a3aae

Please sign in to comment.