A função cf_handle
é freqüentemente chamada com muita freqüência e sua implementação requer a aquisição de um bloqueio de leitura para um mapa subjacente. Se for chamado em um caminho ativo, pode ter implicações de desempenho. Devemos investigar como mudar isso para uma abordagem que não exija um bloqueio. Por exemplo, poderíamos fazer create_cf
e drop_cf
e qualquer outra função que modifique o mapa interno exigir uma referência mutável a DB
e tornar o chamador responsável por adquirir quaisquer bloqueios em ordem para transformar DB
.
assim?
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, no fork da paritech cf_handle
é Copy
, o que não é tecnicamente seguro (OTOH diria que é difícil de usar indevidamente), mas evita o uso de bloqueios.
@hjiayz Sim, muito parecido com isso.
Quando tentei fazer essa alteração (ou seja, pegar &mut self
ao criar / descartar famílias de colunas), tive problemas com a maneira ColumnFamily
é definido. Ele armazenará uma referência mutável em PhantomData
que faz com que um código como este falhe:
#[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"));
}
}
O erro acontece ao fazer uma chamada de método no banco de dados que leva &self
, como db.property_value_cf()
e passando um ColumnFamily
(que armazena um &mut
no db
campo):
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
Não consigo pensar em uma maneira muito elegante de contornar isso, mas uma opção é não retornar um ColumnFamily
de create_cf
e, em vez disso, deixar o código consumir buscá-lo com uma chamada para 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"));
}
}
Mudar create_cf()
para retornar Result<(), Error>
seria uma mudança aceitável?
@dvdplm Adicionar vidas a ColumnFamily
acabou sendo um pesadelo do ponto de vista ergonômico. Eu gostaria de seguir uma abordagem que não precisa de uma vida inteira, mas também não corre o risco de usar um ponteiro de família de colunas depois de ser excluído. Essa mudança provavelmente afetará a forma como removemos os bloqueios de cf_handle
- você provavelmente não verá esse problema.
Concorde com @iSynaptic. Obviamente, retornar uma referência a ColumnFamily
é uma maneira segura apenas neste caso.
Talvez # 298 possa ser mesclado, então?
Foi resolvido por # 314? Algum plano de lançamento em caso afirmativo?
Sim, ele é. Planejamos lançá-lo em um futuro próximo.
@aleksuss qual é o status de liberá-lo? :)
@bkchr Acho que faremos isso na próxima semana.
Comentários muito úteis
Sim, ele é. Planejamos lançá-lo em um futuro próximo.