A simple, humane, typed key-value storage solution.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
rkv/tests/test_txn.rs

131 lines
4.8 KiB

// Copyright 2018-2019 Mozilla
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
// this file except in compliance with the License. You may obtain a copy of the
// License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#![cfg(feature = "db-dup-sort")]
use std::fs;
use tempfile::Builder;
use rkv::{
backend::{SafeMode, SafeModeDatabase, SafeModeRoCursor, SafeModeRwTransaction},
Readable, Rkv, StoreOptions, Value, Writer,
};
/// Consider a struct like this:
/// struct Sample {
/// id: u64,
/// value: String,
/// date: String,
/// }
/// We would like to index all of the fields so that we can search for the struct not only
/// by ID but also by value and date. When we index the fields individually in their own
/// tables, it is important that we run all operations within a single transaction to
/// ensure coherence of the indices.
/// This test features helper functions for reading and writing the parts of the struct.
/// Note that the reader functions take `Readable` because they might run within a Read
/// Transaction or a Write Transaction. The test demonstrates fetching values via both.
type SingleStore = rkv::SingleStore<SafeModeDatabase>;
type MultiStore = rkv::MultiStore<SafeModeDatabase>;
#[test]
fn read_many() {
let root = Builder::new()
.prefix("test_txns")
.tempdir()
.expect("tempdir");
fs::create_dir_all(root.path()).expect("dir created");
let k = Rkv::new::<SafeMode>(root.path()).expect("new succeeded");
let samplestore = k.open_single("s", StoreOptions::create()).expect("open");
let datestore = k.open_multi("m", StoreOptions::create()).expect("open");
let valuestore = k.open_multi("m", StoreOptions::create()).expect("open");
{
let mut writer = k.write().expect("env write lock");
for id in 0..30_u64 {
let value = format!("value{}", id);
let date = format!("2019-06-{}", id);
put_id_field(&mut writer, datestore, &date, id);
put_id_field(&mut writer, valuestore, &value, id);
put_sample(&mut writer, samplestore, id, &value);
}
// now we read in the same transaction
for id in 0..30_u64 {
let value = format!("value{}", id);
let date = format!("2019-06-{}", id);
let ids = get_ids_by_field(&writer, datestore, &date);
let ids2 = get_ids_by_field(&writer, valuestore, &value);
let samples = get_samples(&writer, samplestore, &ids);
let samples2 = get_samples(&writer, samplestore, &ids2);
println!("{:?}, {:?}", samples, samples2);
}
}
{
let reader = k.read().expect("env read lock");
for id in 0..30_u64 {
let value = format!("value{}", id);
let date = format!("2019-06-{}", id);
let ids = get_ids_by_field(&reader, datestore, &date);
let ids2 = get_ids_by_field(&reader, valuestore, &value);
let samples = get_samples(&reader, samplestore, &ids);
let samples2 = get_samples(&reader, samplestore, &ids2);
println!("{:?}, {:?}", samples, samples2);
}
}
}
fn get_ids_by_field<'t, T>(txn: &'t T, store: MultiStore, field: &'t str) -> Vec<u64>
where
T: Readable<'t, Database = SafeModeDatabase, RoCursor = SafeModeRoCursor<'t>>,
{
store
.get(txn, field)
.expect("get iterator")
.map(|id| match id.expect("field") {
(_, Value::U64(id)) => id,
_ => panic!("getting value in iter"),
})
.collect::<Vec<u64>>()
}
fn get_samples<'t, T>(txn: &'t T, samplestore: SingleStore, ids: &[u64]) -> Vec<String>
where
T: Readable<'t, Database = SafeModeDatabase, RoCursor = SafeModeRoCursor<'t>>,
{
ids.iter()
.map(|id| {
let bytes = id.to_be_bytes();
match samplestore.get(txn, &bytes).expect("fetch sample") {
Some(Value::Str(sample)) => String::from(sample),
Some(_) => panic!("wrong type"),
None => panic!("no sample for this id!"),
}
})
.collect::<Vec<String>>()
}
fn put_sample(
txn: &mut Writer<SafeModeRwTransaction>,
samplestore: SingleStore,
id: u64,
value: &str,
) {
let idbytes = id.to_be_bytes();
samplestore
.put(txn, &idbytes, &Value::Str(value))
.expect("put id");
}
fn put_id_field(txn: &mut Writer<SafeModeRwTransaction>, store: MultiStore, field: &str, id: u64) {
store.put(txn, field, &Value::U64(id)).expect("put id");
}