refactor(mito): tidy memtable stats (#4982)

* wip: share same WriteMetrics struct between different memtable implementations

* refactor: extract function to update memtable timestamp range
This commit is contained in:
Lei, HUANG
2024-11-13 06:57:27 +00:00
committed by GitHub
parent 3bbcde8e58
commit 6afc4e778a
10 changed files with 108 additions and 154 deletions
+1
View File
@@ -42,6 +42,7 @@ use crate::sst::file::FileTimeRange;
pub mod bulk;
pub mod key_values;
pub mod partition_tree;
mod stats;
pub mod time_partition;
pub mod time_series;
pub(crate) mod version;
+2 -42
View File
@@ -18,7 +18,6 @@ pub(crate) mod data;
mod dedup;
mod dict;
mod merger;
mod metrics;
mod partition;
mod shard;
mod shard_builder;
@@ -38,8 +37,8 @@ use table::predicate::Predicate;
use crate::error::{Result, UnsupportedOperationSnafu};
use crate::flush::WriteBufferManagerRef;
use crate::memtable::key_values::KeyValue;
use crate::memtable::partition_tree::metrics::WriteMetrics;
use crate::memtable::partition_tree::tree::PartitionTree;
use crate::memtable::stats::WriteMetrics;
use crate::memtable::{
AllocTracker, BoxedBatchIterator, BulkPart, IterBuilder, KeyValues, Memtable, MemtableBuilder,
MemtableId, MemtableRange, MemtableRangeContext, MemtableRef, MemtableStats,
@@ -273,46 +272,7 @@ impl PartitionTreeMemtable {
fn update_stats(&self, metrics: &WriteMetrics) {
// Only let the tracker tracks value bytes.
self.alloc_tracker.on_allocation(metrics.value_bytes);
loop {
let current_min = self.min_timestamp.load(Ordering::Relaxed);
if metrics.min_ts >= current_min {
break;
}
let Err(updated) = self.min_timestamp.compare_exchange(
current_min,
metrics.min_ts,
Ordering::Relaxed,
Ordering::Relaxed,
) else {
break;
};
if updated == metrics.min_ts {
break;
}
}
loop {
let current_max = self.max_timestamp.load(Ordering::Relaxed);
if metrics.max_ts <= current_max {
break;
}
let Err(updated) = self.max_timestamp.compare_exchange(
current_max,
metrics.max_ts,
Ordering::Relaxed,
Ordering::Relaxed,
) else {
break;
};
if updated == metrics.max_ts {
break;
}
}
metrics.update_timestamp_range(&self.max_timestamp, &self.min_timestamp);
}
}
@@ -19,8 +19,8 @@ use std::sync::Arc;
use datatypes::arrow::array::{Array, ArrayBuilder, BinaryArray, BinaryBuilder};
use crate::memtable::partition_tree::metrics::WriteMetrics;
use crate::memtable::partition_tree::PkIndex;
use crate::memtable::stats::WriteMetrics;
use crate::metrics::MEMTABLE_DICT_BYTES;
/// Maximum keys in a [DictBlock].
@@ -1,38 +0,0 @@
// Copyright 2023 Greptime Team
//
// 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.
//! Internal metrics of the memtable.
/// Metrics of writing the partition tree.
pub struct WriteMetrics {
/// Size allocated by keys.
pub key_bytes: usize,
/// Size allocated by values.
pub value_bytes: usize,
/// Minimum timestamp.
pub min_ts: i64,
/// Maximum timestamp
pub max_ts: i64,
}
impl Default for WriteMetrics {
fn default() -> Self {
Self {
key_bytes: 0,
value_bytes: 0,
min_ts: i64::MAX,
max_ts: i64::MIN,
}
}
}
@@ -30,12 +30,12 @@ use crate::error::Result;
use crate::memtable::key_values::KeyValue;
use crate::memtable::partition_tree::data::{DataBatch, DataParts, DATA_INIT_CAP};
use crate::memtable::partition_tree::dedup::DedupReader;
use crate::memtable::partition_tree::metrics::WriteMetrics;
use crate::memtable::partition_tree::shard::{
BoxedDataBatchSource, Shard, ShardMerger, ShardNode, ShardSource,
};
use crate::memtable::partition_tree::shard_builder::ShardBuilder;
use crate::memtable::partition_tree::{PartitionTreeConfig, PkId};
use crate::memtable::stats::WriteMetrics;
use crate::metrics::PARTITION_TREE_READ_STAGE_ELAPSED;
use crate::read::{Batch, BatchBuilder};
use crate::row_converter::{McmpRowCodec, RowCodec};
@@ -428,8 +428,8 @@ mod tests {
use super::*;
use crate::memtable::partition_tree::data::timestamp_array_to_i64_slice;
use crate::memtable::partition_tree::dict::KeyDictBuilder;
use crate::memtable::partition_tree::metrics::WriteMetrics;
use crate::memtable::partition_tree::PkIndex;
use crate::memtable::stats::WriteMetrics;
use crate::memtable::KeyValues;
use crate::test_util::memtable_util::{
build_key_values_with_ts_seq_values, encode_keys, metadata_for_test,
@@ -26,10 +26,10 @@ use crate::memtable::partition_tree::data::{
DataBatch, DataBuffer, DataBufferReader, DataBufferReaderBuilder, DataParts, DATA_INIT_CAP,
};
use crate::memtable::partition_tree::dict::{DictBuilderReader, KeyDictBuilder};
use crate::memtable::partition_tree::metrics::WriteMetrics;
use crate::memtable::partition_tree::partition::PrimaryKeyFilter;
use crate::memtable::partition_tree::shard::Shard;
use crate::memtable::partition_tree::{PartitionTreeConfig, PkId, PkIndex, ShardId};
use crate::memtable::stats::WriteMetrics;
use crate::metrics::PARTITION_TREE_READ_STAGE_ELAPSED;
/// Builder to write keys and data to a shard that the key dictionary
@@ -318,7 +318,6 @@ mod tests {
use super::*;
use crate::memtable::partition_tree::data::timestamp_array_to_i64_slice;
use crate::memtable::partition_tree::metrics::WriteMetrics;
use crate::memtable::KeyValues;
use crate::test_util::memtable_util::{
build_key_values_with_ts_seq_values, encode_key_by_kv, metadata_for_test,
@@ -33,11 +33,11 @@ use table::predicate::Predicate;
use crate::error::{PrimaryKeyLengthMismatchSnafu, Result, SerializeFieldSnafu};
use crate::flush::WriteBufferManagerRef;
use crate::memtable::key_values::KeyValue;
use crate::memtable::partition_tree::metrics::WriteMetrics;
use crate::memtable::partition_tree::partition::{
Partition, PartitionKey, PartitionReader, PartitionRef, ReadPartitionContext,
};
use crate::memtable::partition_tree::PartitionTreeConfig;
use crate::memtable::stats::WriteMetrics;
use crate::memtable::{BoxedBatchIterator, KeyValues};
use crate::metrics::{PARTITION_TREE_READ_STAGE_ELAPSED, READ_ROWS_TOTAL, READ_STAGE_ELAPSED};
use crate::read::dedup::LastNonNullIter;
+85
View File
@@ -0,0 +1,85 @@
// Copyright 2023 Greptime Team
//
// 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.
//! Internal metrics of the memtable.
use std::sync::atomic::{AtomicI64, Ordering};
/// Metrics of writing memtables.
pub(crate) struct WriteMetrics {
/// Size allocated by keys.
pub(crate) key_bytes: usize,
/// Size allocated by values.
pub(crate) value_bytes: usize,
/// Minimum timestamp.
pub(crate) min_ts: i64,
/// Maximum timestamp
pub(crate) max_ts: i64,
}
impl WriteMetrics {
/// Update the min/max timestamp range according to current write metric.
pub(crate) fn update_timestamp_range(&self, prev_max_ts: &AtomicI64, prev_min_ts: &AtomicI64) {
loop {
let current_min = prev_min_ts.load(Ordering::Relaxed);
if self.min_ts >= current_min {
break;
}
let Err(updated) = prev_min_ts.compare_exchange(
current_min,
self.min_ts,
Ordering::Relaxed,
Ordering::Relaxed,
) else {
break;
};
if updated == self.min_ts {
break;
}
}
loop {
let current_max = prev_max_ts.load(Ordering::Relaxed);
if self.max_ts <= current_max {
break;
}
let Err(updated) = prev_max_ts.compare_exchange(
current_max,
self.max_ts,
Ordering::Relaxed,
Ordering::Relaxed,
) else {
break;
};
if updated == self.max_ts {
break;
}
}
}
}
impl Default for WriteMetrics {
fn default() -> Self {
Self {
key_bytes: 0,
value_bytes: 0,
min_ts: i64::MAX,
max_ts: i64::MIN,
}
}
}
+15 -68
View File
@@ -42,6 +42,7 @@ use crate::error::{
};
use crate::flush::WriteBufferManagerRef;
use crate::memtable::key_values::KeyValue;
use crate::memtable::stats::WriteMetrics;
use crate::memtable::{
AllocTracker, BoxedBatchIterator, BulkPart, IterBuilder, KeyValues, Memtable, MemtableBuilder,
MemtableId, MemtableRange, MemtableRangeContext, MemtableRef, MemtableStats,
@@ -140,51 +141,13 @@ impl TimeSeriesMemtable {
}
/// Updates memtable stats.
fn update_stats(&self, stats: LocalStats) {
self.alloc_tracker.on_allocation(stats.allocated);
loop {
let current_min = self.min_timestamp.load(Ordering::Relaxed);
if stats.min_ts >= current_min {
break;
}
let Err(updated) = self.min_timestamp.compare_exchange(
current_min,
stats.min_ts,
Ordering::Relaxed,
Ordering::Relaxed,
) else {
break;
};
if updated == stats.min_ts {
break;
}
}
loop {
let current_max = self.max_timestamp.load(Ordering::Relaxed);
if stats.max_ts <= current_max {
break;
}
let Err(updated) = self.max_timestamp.compare_exchange(
current_max,
stats.max_ts,
Ordering::Relaxed,
Ordering::Relaxed,
) else {
break;
};
if updated == stats.max_ts {
break;
}
}
fn update_stats(&self, stats: WriteMetrics) {
self.alloc_tracker
.on_allocation(stats.key_bytes + stats.value_bytes);
stats.update_timestamp_range(&self.max_timestamp, &self.min_timestamp);
}
fn write_key_value(&self, kv: KeyValue, stats: &mut LocalStats) -> Result<()> {
fn write_key_value(&self, kv: KeyValue, stats: &mut WriteMetrics) -> Result<()> {
ensure!(
kv.num_primary_keys() == self.row_codec.num_fields(),
PrimaryKeyLengthMismatchSnafu {
@@ -195,9 +158,9 @@ impl TimeSeriesMemtable {
let primary_key_encoded = self.row_codec.encode(kv.primary_keys())?;
let fields = kv.fields().collect::<Vec<_>>();
stats.allocated += fields.iter().map(|v| v.data_size()).sum::<usize>();
stats.value_bytes += fields.iter().map(|v| v.data_size()).sum::<usize>();
let (series, series_allocated) = self.series_set.get_or_add_series(primary_key_encoded);
stats.allocated += series_allocated;
stats.key_bytes += series_allocated;
// safety: timestamp of kv must be both present and a valid timestamp value.
let ts = kv.timestamp().as_timestamp().unwrap().unwrap().value();
@@ -223,13 +186,13 @@ impl Memtable for TimeSeriesMemtable {
}
fn write(&self, kvs: &KeyValues) -> Result<()> {
let mut local_stats = LocalStats::default();
let mut local_stats = WriteMetrics::default();
for kv in kvs.iter() {
self.write_key_value(kv, &mut local_stats)?;
}
local_stats.allocated += kvs.num_rows() * std::mem::size_of::<Timestamp>();
local_stats.allocated += kvs.num_rows() * std::mem::size_of::<OpType>();
local_stats.value_bytes += kvs.num_rows() * std::mem::size_of::<Timestamp>();
local_stats.value_bytes += kvs.num_rows() * std::mem::size_of::<OpType>();
// TODO(hl): this maybe inaccurate since for-iteration may return early.
// We may lift the primary key length check out of Memtable::write
@@ -241,11 +204,11 @@ impl Memtable for TimeSeriesMemtable {
}
fn write_one(&self, key_value: KeyValue) -> Result<()> {
let mut local_stats = LocalStats::default();
let res = self.write_key_value(key_value, &mut local_stats);
local_stats.allocated += std::mem::size_of::<Timestamp>() + std::mem::size_of::<OpType>();
let mut metrics = WriteMetrics::default();
let res = self.write_key_value(key_value, &mut metrics);
metrics.value_bytes += std::mem::size_of::<Timestamp>() + std::mem::size_of::<OpType>();
self.update_stats(local_stats);
self.update_stats(metrics);
self.num_rows.fetch_add(1, Ordering::Relaxed);
res
}
@@ -359,22 +322,6 @@ impl Memtable for TimeSeriesMemtable {
}
}
struct LocalStats {
allocated: usize,
min_ts: i64,
max_ts: i64,
}
impl Default for LocalStats {
fn default() -> Self {
LocalStats {
allocated: 0,
min_ts: i64::MAX,
max_ts: i64::MIN,
}
}
}
type SeriesRwLockMap = RwLock<BTreeMap<Vec<u8>, Arc<RwLock<Series>>>>;
#[derive(Clone)]