Функция cf_handle
часто вызывается очень часто, и для ее реализации требуется блокировка чтения для базовой карты. Если это вызывается по горячему пути, это может повлиять на производительность. Мы должны изучить возможность переключения этого подхода на подход, не требующий блокировки. Например, мы могли бы сделать create_cf
и drop_cf
и любую другую функцию, изменяющую внутреннюю карту, потребовать изменяемую ссылку на DB
и сделать вызывающего ответственным за получение любых блокировок по порядку. чтобы изменить DB
.
нравится?
use std::collections::HashMap;
#[derive(Debug)]
struct ColumnFamily(i32);
struct DB {
cfs:HashMap<i32,ColumnFamily>,
}
impl DB {
fn new()->DB{
let mut cfs = HashMap::new();
cfs.insert(1,ColumnFamily(1));
cfs.insert(2,ColumnFamily(2));
DB{
cfs
}
}
fn get(&self,key:i32)->&ColumnFamily{
self.cfs.get(&key).unwrap()
}
fn drop_key(&mut self,key:i32){
self.cfs.remove(&key);
}
}
fn main() {
let mut f = DB::new();
let a = f.get(1);
println!("{:?}",a);
f.drop_key(1);
let c = f.get(2);
println!("{:?}",c);
}
FWIW, в вилке paritech cf_handle
- это Copy
, что технически небезопасно (можно утверждать, что OTOH трудно использовать неправильно), но избегает использования блокировок.
@hjiayz Да, очень похоже на это.
Когда я попытался внести это изменение (т.е. взять &mut self
при создании / удалении семейств столбцов), я столкнулся с проблемами, связанными со способом определения ColumnFamily
. Он сохранит изменяемую ссылку в PhantomData
что приведет к сбою такого кода:
#[test]
fn property_cf_test() {
let n = DBPath::new("_rust_rocksdb_property_cf_test");
{
let opts = Options::default();
let mut db = DB::open_default(&n).unwrap(); // Notice the `mut`
let cf = db.create_cf("cf1", &opts).unwrap();
let value = db.property_value_cf(cf, "rocksdb.stats").unwrap().unwrap();
assert!(value.contains("Stats"));
}
}
Ошибка возникает при вызове метода в базе данных, который принимает &self
, например db.property_value_cf()
и передает ColumnFamily
(который сохраняет &mut
в db
field):
error[E0502]: cannot borrow `db` as immutable because it is also borrowed as mutable
--> tests/test_property.rs:39:21
|
38 | let cf = db.create_cf("cf1", &opts).unwrap();
| -- mutable borrow occurs here
39 | let value = db.property_value_cf(cf, "rocksdb.stats").unwrap().unwrap();
| ^^ -- mutable borrow later used here
| |
| immutable borrow occurs here
Я не могу придумать очень элегантный способ обойти это, но один из вариантов - не возвращать ColumnFamily
из create_cf
и вместо этого позволить потребляющему коду получить его с вызовом cf_handle()
:
#[test]
fn property_cf_test() {
let n = DBPath::new("_rust_rocksdb_property_cf_test");
{
let opts = Options::default();
let mut db = DB::open_default(&n).unwrap();
db.create_cf("cf1", &opts).unwrap();
let cf = db.cf_handle("cf1").unwrap(); // Get the CF
let value = db.property_value_cf(cf, "rocksdb.stats").unwrap().unwrap();
assert!(value.contains("Stats"));
}
}
Будет ли изменение create_cf()
на возврат Result<(), Error>
приемлемым изменением?
@dvdplm Добавление времени жизни к ColumnFamily
конечном итоге стало кошмаром с эргономической точки зрения. Я хотел бы придерживаться подхода, который не требует времени жизни, но также не сопряжен с риском использования указателя семейства столбцов после его удаления. Это изменение, вероятно, повлияет на то, как мы будем снимать блокировки с cf_handle
- вы, вероятно, не увидите этой проблемы.
Согласитесь с @iSynaptic. Очевидно, что возврат ссылки на ColumnFamily
безопасен только в этом случае.
Может, тогда # 298 можно будет объединить?
Это решено # 314? Есть ли планы выпустить, если так?
Да, это так. Мы планируем выпустить его в ближайшее время.
@aleksuss, каков статус его выпуска? :)
@bkchr Думаю, мы сделаем это на следующей неделе.
Самый полезный комментарий
Да, это так. Мы планируем выпустить его в ближайшее время.