K2V #293
5 changed files with 99 additions and 15 deletions
|
@ -13,6 +13,7 @@ use garage_table::replication::TableFullReplication;
|
||||||
use garage_table::replication::TableShardedReplication;
|
use garage_table::replication::TableShardedReplication;
|
||||||
use garage_table::*;
|
use garage_table::*;
|
||||||
|
|
||||||
|
use crate::k2v::counter_table::*;
|
||||||
use crate::k2v::item_table::*;
|
use crate::k2v::item_table::*;
|
||||||
use crate::k2v::rpc::*;
|
use crate::k2v::rpc::*;
|
||||||
use crate::s3::block_ref_table::*;
|
use crate::s3::block_ref_table::*;
|
||||||
|
@ -22,6 +23,7 @@ use crate::s3::version_table::*;
|
||||||
use crate::bucket_alias_table::*;
|
use crate::bucket_alias_table::*;
|
||||||
use crate::bucket_table::*;
|
use crate::bucket_table::*;
|
||||||
use crate::helper;
|
use crate::helper;
|
||||||
|
use crate::index_counter::*;
|
||||||
use crate::key_table::*;
|
use crate::key_table::*;
|
||||||
|
|
||||||
/// An entire Garage full of data
|
/// An entire Garage full of data
|
||||||
|
@ -54,6 +56,8 @@ pub struct Garage {
|
||||||
|
|
||||||
/// Table containing K2V items
|
/// Table containing K2V items
|
||||||
pub k2v_item_table: Arc<Table<K2VItemTable, TableShardedReplication>>,
|
pub k2v_item_table: Arc<Table<K2VItemTable, TableShardedReplication>>,
|
||||||
|
/// Indexing table containing K2V item counters
|
||||||
|
pub k2v_counter_table: Arc<IndexCounter<K2VCounterTable>>,
|
||||||
/// K2V RPC handler
|
/// K2V RPC handler
|
||||||
pub k2v_rpc: Arc<K2VRpcHandler>,
|
pub k2v_rpc: Arc<K2VRpcHandler>,
|
||||||
}
|
}
|
||||||
|
@ -154,7 +158,15 @@ impl Garage {
|
||||||
);
|
);
|
||||||
|
|
||||||
// ---- K2V ----
|
// ---- K2V ----
|
||||||
let k2v_item_table = Table::new(K2VItemTable {}, meta_rep_param, system.clone(), &db);
|
let k2v_counter_table = IndexCounter::new(system.clone(), meta_rep_param.clone(), &db);
|
||||||
|
let k2v_item_table = Table::new(
|
||||||
|
K2VItemTable {
|
||||||
|
counter_table: k2v_counter_table.clone(),
|
||||||
|
},
|
||||||
|
meta_rep_param,
|
||||||
|
system.clone(),
|
||||||
|
&db,
|
||||||
|
);
|
||||||
let k2v_rpc = K2VRpcHandler::new(system.clone(), k2v_item_table.clone());
|
let k2v_rpc = K2VRpcHandler::new(system.clone(), k2v_item_table.clone());
|
||||||
|
|
||||||
info!("Initialize Garage...");
|
info!("Initialize Garage...");
|
||||||
|
@ -172,6 +184,7 @@ impl Garage {
|
||||||
version_table,
|
version_table,
|
||||||
block_ref_table,
|
block_ref_table,
|
||||||
k2v_item_table,
|
k2v_item_table,
|
||||||
|
k2v_counter_table,
|
||||||
k2v_rpc,
|
k2v_rpc,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,8 +4,8 @@ use std::sync::Arc;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use garage_rpc::system::System;
|
|
||||||
use garage_rpc::ring::Ring;
|
use garage_rpc::ring::Ring;
|
||||||
|
use garage_rpc::system::System;
|
||||||
use garage_util::data::*;
|
use garage_util::data::*;
|
||||||
use garage_util::error::*;
|
use garage_util::error::*;
|
||||||
|
|
||||||
|
@ -42,13 +42,14 @@ impl<T: CounterSchema> Entry<T::P, T::S> for CounterEntry<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: CounterSchema> CounterEntry<T> {
|
impl<T: CounterSchema> CounterEntry<T> {
|
||||||
pub fn filtered_values(&self, sys: System) -> HashMap<String, i64> {
|
pub fn filtered_values(&self, ring: &Ring) -> HashMap<String, i64> {
|
||||||
let ring: Arc<Ring> = sys.ring.borrow().clone();
|
|
||||||
let nodes = &ring.layout.node_id_vec;
|
let nodes = &ring.layout.node_id_vec;
|
||||||
|
|
||||||
let mut ret = HashMap::new();
|
let mut ret = HashMap::new();
|
||||||
for (name, vals) in self.values.iter() {
|
for (name, vals) in self.values.iter() {
|
||||||
let new_vals = vals.node_values.iter()
|
let new_vals = vals
|
||||||
|
.node_values
|
||||||
|
.iter()
|
||||||
.filter(|(n, _)| nodes.contains(n))
|
.filter(|(n, _)| nodes.contains(n))
|
||||||
.map(|(_, (_, v))| v)
|
.map(|(_, (_, v))| v)
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
@ -123,8 +124,12 @@ pub struct IndexCounter<T: CounterSchema> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: CounterSchema> IndexCounter<T> {
|
impl<T: CounterSchema> IndexCounter<T> {
|
||||||
pub fn new(system: Arc<System>, replication: TableShardedReplication, db: &sled::Db) -> Self {
|
pub fn new(
|
||||||
Self {
|
system: Arc<System>,
|
||||||
|
replication: TableShardedReplication,
|
||||||
|
db: &sled::Db,
|
||||||
|
) -> Arc<Self> {
|
||||||
|
Arc::new(Self {
|
||||||
this_node: system.id,
|
this_node: system.id,
|
||||||
local_counter: db
|
local_counter: db
|
||||||
.open_tree(format!("local_counter:{}", T::NAME))
|
.open_tree(format!("local_counter:{}", T::NAME))
|
||||||
|
@ -137,10 +142,10 @@ impl<T: CounterSchema> IndexCounter<T> {
|
||||||
system,
|
system,
|
||||||
db,
|
db,
|
||||||
),
|
),
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn count(&self, pk: &T::P, sk: &T::S, counts: &[(String, i64)]) -> Result<(), Error> {
|
pub fn count(&self, pk: &T::P, sk: &T::S, counts: &[(&str, i64)]) -> Result<(), Error> {
|
||||||
let tree_key = self.table.data.tree_key(pk, sk);
|
let tree_key = self.table.data.tree_key(pk, sk);
|
||||||
|
|
||||||
let new_entry = self.local_counter.transaction(|tx| {
|
let new_entry = self.local_counter.transaction(|tx| {
|
||||||
|
@ -156,7 +161,7 @@ impl<T: CounterSchema> IndexCounter<T> {
|
||||||
};
|
};
|
||||||
|
|
||||||
for (s, inc) in counts.iter() {
|
for (s, inc) in counts.iter() {
|
||||||
let mut ent = entry.values.entry(s.clone()).or_insert((0, 0));
|
let mut ent = entry.values.entry(s.to_string()).or_insert((0, 0));
|
||||||
ent.0 += 1;
|
ent.0 += 1;
|
||||||
ent.1 += *inc;
|
ent.1 += *inc;
|
||||||
}
|
}
|
||||||
|
|
15
src/model/k2v/counter_table.rs
Normal file
15
src/model/k2v/counter_table.rs
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
use garage_util::data::*;
|
||||||
|
|
||||||
|
use crate::index_counter::*;
|
||||||
|
|
||||||
|
#[derive(PartialEq, Clone)]
|
||||||
|
pub struct K2VCounterTable;
|
||||||
|
|
||||||
|
impl CounterSchema for K2VCounterTable {
|
||||||
|
const NAME: &'static str = "k2v_index_counter";
|
||||||
|
|
||||||
|
// Partition key = bucket id
|
||||||
|
type P = Uuid;
|
||||||
|
// Sort key = K2V item's partition key
|
||||||
|
type S = String;
|
||||||
|
}
|
|
@ -1,12 +1,15 @@
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use garage_util::data::*;
|
use garage_util::data::*;
|
||||||
|
|
||||||
use garage_table::crdt::*;
|
use garage_table::crdt::*;
|
||||||
use garage_table::*;
|
use garage_table::*;
|
||||||
|
|
||||||
|
use crate::index_counter::*;
|
||||||
use crate::k2v::causality::*;
|
use crate::k2v::causality::*;
|
||||||
|
use crate::k2v::counter_table::*;
|
||||||
|
|
||||||
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)]
|
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)]
|
||||||
pub struct K2VItem {
|
pub struct K2VItem {
|
||||||
|
@ -105,6 +108,25 @@ impl K2VItem {
|
||||||
ent.discard();
|
ent.discard();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// returns counters: (non-deleted entries, non-tombstone values, bytes used)
|
||||||
|
fn stats(&self) -> (i64, i64, i64) {
|
||||||
|
let n_entries = if self.is_tombstone() { 0 } else { 1 };
|
||||||
|
let n_values = self
|
||||||
|
.values()
|
||||||
|
.iter()
|
||||||
|
.filter(|v| matches!(v, DvvsValue::Value(_)))
|
||||||
|
.count() as i64;
|
||||||
|
let n_bytes = self
|
||||||
|
.values()
|
||||||
|
.iter()
|
||||||
|
.map(|v| match v {
|
||||||
|
DvvsValue::Deleted => 0,
|
||||||
|
DvvsValue::Value(v) => v.len() as i64,
|
||||||
|
})
|
||||||
|
.sum();
|
||||||
|
(n_entries, n_values, n_bytes)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DvvsEntry {
|
impl DvvsEntry {
|
||||||
|
@ -175,7 +197,9 @@ impl Entry<K2VItemPartition, String> for K2VItem {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct K2VItemTable {}
|
pub struct K2VItemTable {
|
||||||
|
pub(crate) counter_table: Arc<IndexCounter<K2VCounterTable>>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
|
||||||
pub struct ItemFilter {
|
pub struct ItemFilter {
|
||||||
|
@ -191,8 +215,34 @@ impl TableSchema for K2VItemTable {
|
||||||
type E = K2VItem;
|
type E = K2VItem;
|
||||||
type Filter = ItemFilter;
|
type Filter = ItemFilter;
|
||||||
|
|
||||||
fn updated(&self, _old: Option<&Self::E>, _new: Option<&Self::E>) {
|
fn updated(&self, old: Option<&Self::E>, new: Option<&Self::E>) {
|
||||||
// nothing for now
|
let (old_entries, old_values, old_bytes) = match old {
|
||||||
|
None => (0, 0, 0),
|
||||||
|
Some(e) => e.stats(),
|
||||||
|
};
|
||||||
|
let (new_entries, new_values, new_bytes) = match new {
|
||||||
|
None => (0, 0, 0),
|
||||||
|
Some(e) => e.stats(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let count_pk = old
|
||||||
|
.map(|e| e.partition.bucket_id)
|
||||||
|
.unwrap_or_else(|| new.unwrap().partition.bucket_id);
|
||||||
|
let count_sk = old
|
||||||
|
.map(|e| &e.partition.partition_key)
|
||||||
|
.unwrap_or_else(|| &new.unwrap().partition.partition_key);
|
||||||
|
|
||||||
|
if let Err(e) = self.counter_table.count(
|
||||||
|
&count_pk,
|
||||||
|
count_sk,
|
||||||
|
&[
|
||||||
|
("entries", new_entries - old_entries),
|
||||||
|
("values", new_values - old_values),
|
||||||
|
("bytes", new_bytes - old_bytes),
|
||||||
|
],
|
||||||
|
) {
|
||||||
|
error!("Could not update K2V counter for bucket {:?} partition {}; counts will now be inconsistent. {}", count_pk, count_sk, e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::nonminimal_bool)]
|
#[allow(clippy::nonminimal_bool)]
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
pub mod causality;
|
pub mod causality;
|
||||||
|
|
||||||
|
pub mod counter_table;
|
||||||
pub mod item_table;
|
pub mod item_table;
|
||||||
|
|
||||||
pub mod rpc;
|
pub mod rpc;
|
||||||
|
|
Loading…
Reference in a new issue