mirror of
https://github.com/GreptimeTeam/greptimedb.git
synced 2026-06-01 21:00:38 +00:00
feat: impl table manifest (#157)
* feat: impl TableManifest and refactor table engine, object store etc.
* feat: persist table metadata when creating it
* fix: remove unused file src/storage/src/manifest/impl.rs
* feat: impl recover table info from manifest
* test: add open table test and table manifest test
* fix: resolve CR problems
* fix: compile error and remove region id
* doc: describe parent_dir
* fix: address CR problems
* fix: typo
* Revert "fix: compile error and remove region id"
This reverts commit c14c250f8a.
* fix: compile error and generate region id by table_id and region number
This commit is contained in:
@@ -1,56 +1,4 @@
|
||||
//! Engine config
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FileStoreConfig {
|
||||
/// Storage path
|
||||
pub store_dir: String,
|
||||
}
|
||||
//! storage engine config
|
||||
|
||||
impl Default for FileStoreConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
store_dir: "/tmp/greptimedb/".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ObjectStoreConfig {
|
||||
File(FileStoreConfig),
|
||||
}
|
||||
|
||||
impl Default for ObjectStoreConfig {
|
||||
fn default() -> Self {
|
||||
ObjectStoreConfig::File(FileStoreConfig::default())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct EngineConfig {
|
||||
pub store_config: ObjectStoreConfig,
|
||||
}
|
||||
|
||||
impl EngineConfig {
|
||||
pub fn with_store_dir(store_dir: &str) -> Self {
|
||||
Self {
|
||||
store_config: ObjectStoreConfig::File(FileStoreConfig {
|
||||
store_dir: store_dir.to_string(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_default_engine_config() {
|
||||
let engine_config = EngineConfig::default();
|
||||
|
||||
let store_dir = match &engine_config.store_config {
|
||||
ObjectStoreConfig::File(file) => &file.store_dir,
|
||||
};
|
||||
|
||||
assert_eq!("/tmp/greptimedb/", store_dir);
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct EngineConfig {}
|
||||
|
||||
@@ -3,15 +3,15 @@ use std::sync::{Arc, RwLock};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use common_telemetry::logging::info;
|
||||
use object_store::{backend::fs::Backend, util, ObjectStore};
|
||||
use object_store::{util, ObjectStore};
|
||||
use snafu::ResultExt;
|
||||
use store_api::{
|
||||
logstore::LogStore,
|
||||
storage::{EngineContext, OpenOptions, RegionDescriptor, StorageEngine},
|
||||
storage::{CreateOptions, EngineContext, OpenOptions, RegionDescriptor, StorageEngine},
|
||||
};
|
||||
|
||||
use crate::background::JobPoolImpl;
|
||||
use crate::config::{EngineConfig, ObjectStoreConfig};
|
||||
use crate::config::EngineConfig;
|
||||
use crate::error::{self, Error, Result};
|
||||
use crate::flush::{FlushSchedulerImpl, FlushSchedulerRef, FlushStrategyRef, SizeBasedStrategy};
|
||||
use crate::manifest::region::RegionManifest;
|
||||
@@ -55,8 +55,9 @@ impl<S: LogStore> StorageEngine for EngineImpl<S> {
|
||||
&self,
|
||||
_ctx: &EngineContext,
|
||||
descriptor: RegionDescriptor,
|
||||
opts: &CreateOptions,
|
||||
) -> Result<Self::Region> {
|
||||
self.inner.create_region(descriptor).await
|
||||
self.inner.create_region(descriptor, opts).await
|
||||
}
|
||||
|
||||
async fn drop_region(&self, _ctx: &EngineContext, _region: Self::Region) -> Result<()> {
|
||||
@@ -69,36 +70,25 @@ impl<S: LogStore> StorageEngine for EngineImpl<S> {
|
||||
}
|
||||
|
||||
impl<S: LogStore> EngineImpl<S> {
|
||||
pub async fn new(config: EngineConfig, log_store: Arc<S>) -> Result<Self> {
|
||||
Ok(Self {
|
||||
inner: Arc::new(EngineInner::new(config, log_store).await?),
|
||||
})
|
||||
pub fn new(config: EngineConfig, log_store: Arc<S>, object_store: ObjectStore) -> Self {
|
||||
Self {
|
||||
inner: Arc::new(EngineInner::new(config, log_store, object_store)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn new_object_store(store_config: &ObjectStoreConfig) -> Result<ObjectStore> {
|
||||
// TODO(dennis): supports other backend
|
||||
let store_dir = util::normalize_dir(match store_config {
|
||||
ObjectStoreConfig::File(file) => &file.store_dir,
|
||||
});
|
||||
|
||||
let accessor = Backend::build()
|
||||
.root(&store_dir)
|
||||
.finish()
|
||||
.await
|
||||
.context(error::InitBackendSnafu { dir: &store_dir })?;
|
||||
|
||||
Ok(ObjectStore::new(accessor))
|
||||
/// Generate region sst path,
|
||||
/// parent_dir is resolved in function `region_store_config` to ensure it's ended with '/'.
|
||||
#[inline]
|
||||
pub fn region_sst_dir(parent_dir: &str, region_name: &str) -> String {
|
||||
format!("{}{}/", parent_dir, region_name)
|
||||
}
|
||||
|
||||
/// Generate region manifest path,
|
||||
/// parent_dir is resolved in function `region_store_config` to ensure it's ended with '/'.
|
||||
#[inline]
|
||||
pub fn region_sst_dir(region_name: &str) -> String {
|
||||
format!("{}/", region_name)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn region_manifest_dir(region_name: &str) -> String {
|
||||
format!("{}/manifest/", region_name)
|
||||
pub fn region_manifest_dir(parent_dir: &str, region_name: &str) -> String {
|
||||
format!("{}{}/manifest/", parent_dir, region_name)
|
||||
}
|
||||
|
||||
/// A slot for region in the engine.
|
||||
@@ -209,19 +199,18 @@ struct EngineInner<S: LogStore> {
|
||||
}
|
||||
|
||||
impl<S: LogStore> EngineInner<S> {
|
||||
pub async fn new(config: EngineConfig, log_store: Arc<S>) -> Result<Self> {
|
||||
pub fn new(_config: EngineConfig, log_store: Arc<S>, object_store: ObjectStore) -> Self {
|
||||
let job_pool = Arc::new(JobPoolImpl {});
|
||||
let flush_scheduler = Arc::new(FlushSchedulerImpl::new(job_pool));
|
||||
let object_store = new_object_store(&config.store_config).await?;
|
||||
|
||||
Ok(Self {
|
||||
Self {
|
||||
object_store,
|
||||
log_store,
|
||||
regions: RwLock::new(Default::default()),
|
||||
memtable_builder: Arc::new(DefaultMemtableBuilder {}),
|
||||
flush_scheduler,
|
||||
flush_strategy: Arc::new(SizeBasedStrategy::default()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the `Some(slot)` if there is existing slot with given `name`, or insert
|
||||
@@ -256,8 +245,7 @@ impl<S: LogStore> EngineInner<S> {
|
||||
|
||||
let mut guard = SlotGuard::new(name, &self.regions);
|
||||
|
||||
// FIXME(yingwen): Get region id or remove dependency of region id.
|
||||
let store_config = self.region_store_config(name);
|
||||
let store_config = self.region_store_config(&opts.parent_dir, name);
|
||||
|
||||
let region = match RegionImpl::open(name.to_string(), store_config, opts).await? {
|
||||
None => return Ok(None),
|
||||
@@ -268,7 +256,11 @@ impl<S: LogStore> EngineInner<S> {
|
||||
Ok(Some(region))
|
||||
}
|
||||
|
||||
async fn create_region(&self, descriptor: RegionDescriptor) -> Result<RegionImpl<S>> {
|
||||
async fn create_region(
|
||||
&self,
|
||||
descriptor: RegionDescriptor,
|
||||
opts: &CreateOptions,
|
||||
) -> Result<RegionImpl<S>> {
|
||||
if let Some(slot) = self.get_or_occupy_slot(&descriptor.name, RegionSlot::Creating) {
|
||||
return slot.try_get_ready_region();
|
||||
}
|
||||
@@ -283,7 +275,7 @@ impl<S: LogStore> EngineInner<S> {
|
||||
.context(error::InvalidRegionDescSnafu {
|
||||
region: ®ion_name,
|
||||
})?;
|
||||
let store_config = self.region_store_config(®ion_name);
|
||||
let store_config = self.region_store_config(&opts.parent_dir, ®ion_name);
|
||||
|
||||
let region = RegionImpl::create(metadata, store_config).await?;
|
||||
|
||||
@@ -299,10 +291,12 @@ impl<S: LogStore> EngineInner<S> {
|
||||
slot.get_ready_region()
|
||||
}
|
||||
|
||||
fn region_store_config(&self, region_name: &str) -> StoreConfig<S> {
|
||||
let sst_dir = ®ion_sst_dir(region_name);
|
||||
fn region_store_config(&self, parent_dir: &str, region_name: &str) -> StoreConfig<S> {
|
||||
let parent_dir = util::normalize_dir(parent_dir);
|
||||
|
||||
let sst_dir = ®ion_sst_dir(&parent_dir, region_name);
|
||||
let sst_layer = Arc::new(FsAccessLayer::new(sst_dir, self.object_store.clone()));
|
||||
let manifest_dir = region_manifest_dir(region_name);
|
||||
let manifest_dir = region_manifest_dir(&parent_dir, region_name);
|
||||
let manifest = RegionManifest::new(&manifest_dir, self.object_store.clone());
|
||||
|
||||
StoreConfig {
|
||||
@@ -320,6 +314,7 @@ impl<S: LogStore> EngineInner<S> {
|
||||
mod tests {
|
||||
use datatypes::type_id::LogicalTypeId;
|
||||
use log_store::test_util::log_store_util;
|
||||
use object_store::backend::fs::Backend;
|
||||
use store_api::storage::Region;
|
||||
use tempdir::TempDir;
|
||||
|
||||
@@ -332,9 +327,12 @@ mod tests {
|
||||
log_store_util::create_tmp_local_file_log_store("test_engine_wal").await;
|
||||
let dir = TempDir::new("test_create_new_region").unwrap();
|
||||
let store_dir = dir.path().to_string_lossy();
|
||||
let config = EngineConfig::with_store_dir(&store_dir);
|
||||
let accessor = Backend::build().root(&store_dir).finish().await.unwrap();
|
||||
let object_store = ObjectStore::new(accessor);
|
||||
|
||||
let engine = EngineImpl::new(config, Arc::new(log_store)).await.unwrap();
|
||||
let config = EngineConfig::default();
|
||||
|
||||
let engine = EngineImpl::new(config, Arc::new(log_store), object_store);
|
||||
|
||||
let region_name = "region-0";
|
||||
let desc = RegionDescBuilder::new(region_name)
|
||||
@@ -342,7 +340,10 @@ mod tests {
|
||||
.push_value_column(("v1", LogicalTypeId::Float32, true))
|
||||
.build();
|
||||
let ctx = EngineContext::default();
|
||||
let region = engine.create_region(&ctx, desc).await.unwrap();
|
||||
let region = engine
|
||||
.create_region(&ctx, desc, &CreateOptions::default())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(region_name, region.name());
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ use store_api::storage::SequenceNumber;
|
||||
use crate::metadata::Error as MetadataError;
|
||||
|
||||
#[derive(Debug, Snafu)]
|
||||
#[snafu(visibility(pub(crate)))]
|
||||
#[snafu(visibility(pub))]
|
||||
pub enum Error {
|
||||
#[snafu(display("Invalid region descriptor, region: {}, source: {}", region, source))]
|
||||
InvalidRegionDesc {
|
||||
@@ -43,13 +43,6 @@ pub enum Error {
|
||||
backtrace: Backtrace,
|
||||
},
|
||||
|
||||
#[snafu(display("Failed to init backend, source: {}", source))]
|
||||
InitBackend {
|
||||
dir: String,
|
||||
source: std::io::Error,
|
||||
backtrace: Backtrace,
|
||||
},
|
||||
|
||||
#[snafu(display("Failed to write parquet file, source: {}", source))]
|
||||
WriteParquet {
|
||||
source: arrow::error::ArrowError,
|
||||
@@ -162,8 +155,8 @@ pub enum Error {
|
||||
backtrace: Backtrace,
|
||||
},
|
||||
|
||||
#[snafu(display("Failed to decode region action list, {}", msg))]
|
||||
DecodeRegionMetaActionList { msg: String, backtrace: Backtrace },
|
||||
#[snafu(display("Failed to decode action list, {}", msg))]
|
||||
DecodeMetaActionList { msg: String, backtrace: Backtrace },
|
||||
|
||||
#[snafu(display("Failed to read line, err: {}", source))]
|
||||
Readline { source: IoError },
|
||||
@@ -249,7 +242,7 @@ impl ErrorExt for Error {
|
||||
| DecodeJson { .. }
|
||||
| JoinTask { .. }
|
||||
| Cancelled { .. }
|
||||
| DecodeRegionMetaActionList { .. }
|
||||
| DecodeMetaActionList { .. }
|
||||
| Readline { .. }
|
||||
| InvalidParquetSchema { .. }
|
||||
| SequenceColumnNotFound { .. }
|
||||
@@ -258,7 +251,6 @@ impl ErrorExt for Error {
|
||||
| SequenceNotMonotonic { .. } => StatusCode::Unexpected,
|
||||
|
||||
FlushIo { .. }
|
||||
| InitBackend { .. }
|
||||
| WriteParquet { .. }
|
||||
| ReadObject { .. }
|
||||
| WriteObject { .. }
|
||||
|
||||
@@ -8,7 +8,7 @@ pub mod config;
|
||||
mod engine;
|
||||
pub mod error;
|
||||
mod flush;
|
||||
mod manifest;
|
||||
pub mod manifest;
|
||||
pub mod memtable;
|
||||
pub mod metadata;
|
||||
mod proto;
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
//! manifest storage
|
||||
pub(crate) mod action;
|
||||
pub(crate) mod checkpoint;
|
||||
pub mod helper;
|
||||
mod impl_;
|
||||
pub mod region;
|
||||
pub(crate) mod storage;
|
||||
#[cfg(test)]
|
||||
pub mod test_utils;
|
||||
|
||||
pub use self::impl_::*;
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
use std::io::{BufRead, BufReader, Write};
|
||||
use std::io::{BufRead, BufReader};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json as json;
|
||||
use serde_json::ser::to_writer;
|
||||
use snafu::{ensure, OptionExt, ResultExt};
|
||||
use store_api::manifest::action::ProtocolAction;
|
||||
use store_api::manifest::action::ProtocolVersion;
|
||||
use store_api::manifest::action::{ProtocolAction, ProtocolVersion, VersionHeader};
|
||||
use store_api::manifest::ManifestVersion;
|
||||
use store_api::manifest::MetaAction;
|
||||
use store_api::storage::RegionId;
|
||||
use store_api::storage::SequenceNumber;
|
||||
|
||||
use crate::error::{
|
||||
DecodeJsonSnafu, DecodeRegionMetaActionListSnafu, EncodeJsonSnafu,
|
||||
ManifestProtocolForbidReadSnafu, ReadlineSnafu, Result,
|
||||
self, DecodeJsonSnafu, DecodeMetaActionListSnafu, ManifestProtocolForbidReadSnafu,
|
||||
ReadlineSnafu, Result,
|
||||
};
|
||||
use crate::manifest::helper;
|
||||
use crate::metadata::{RegionMetadataRef, VersionNumber};
|
||||
use crate::sst::FileMeta;
|
||||
|
||||
@@ -50,13 +49,6 @@ pub enum RegionMetaAction {
|
||||
Edit(RegionEdit),
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
||||
struct VersionHeader {
|
||||
prev_version: ManifestVersion,
|
||||
}
|
||||
|
||||
const NEWLINE: &[u8] = b"\n";
|
||||
|
||||
impl RegionMetaActionList {
|
||||
pub fn with_action(action: RegionMetaAction) -> Self {
|
||||
Self {
|
||||
@@ -71,31 +63,21 @@ impl RegionMetaActionList {
|
||||
prev_version: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Encode self into json in the form of string lines, starts with prev_version and then action json list.
|
||||
pub(crate) fn encode(&self) -> Result<Vec<u8>> {
|
||||
let mut bytes = Vec::default();
|
||||
impl MetaAction for RegionMetaActionList {
|
||||
type Error = error::Error;
|
||||
|
||||
{
|
||||
// Encode prev_version
|
||||
let v = VersionHeader {
|
||||
prev_version: self.prev_version,
|
||||
};
|
||||
|
||||
to_writer(&mut bytes, &v).context(EncodeJsonSnafu)?;
|
||||
// unwrap is fine here, because we write into a buffer.
|
||||
bytes.write_all(NEWLINE).unwrap();
|
||||
}
|
||||
|
||||
for action in &self.actions {
|
||||
to_writer(&mut bytes, action).context(EncodeJsonSnafu)?;
|
||||
bytes.write_all(NEWLINE).unwrap();
|
||||
}
|
||||
|
||||
Ok(bytes)
|
||||
fn set_prev_version(&mut self, version: ManifestVersion) {
|
||||
self.prev_version = version;
|
||||
}
|
||||
|
||||
pub(crate) fn decode(
|
||||
/// Encode self into json in the form of string lines, starts with prev_version and then action json list.
|
||||
fn encode(&self) -> Result<Vec<u8>> {
|
||||
helper::encode_actions(self.prev_version, &self.actions)
|
||||
}
|
||||
|
||||
fn decode(
|
||||
bs: &[u8],
|
||||
reader_version: ProtocolVersion,
|
||||
) -> Result<(Self, Option<ProtocolAction>)> {
|
||||
@@ -109,7 +91,7 @@ impl RegionMetaActionList {
|
||||
{
|
||||
let first_line = lines
|
||||
.next()
|
||||
.with_context(|| DecodeRegionMetaActionListSnafu {
|
||||
.with_context(|| DecodeMetaActionListSnafu {
|
||||
msg: format!(
|
||||
"Invalid content in manifest: {}",
|
||||
std::str::from_utf8(bs).unwrap_or("**invalid bytes**")
|
||||
@@ -148,12 +130,6 @@ impl RegionMetaActionList {
|
||||
}
|
||||
}
|
||||
|
||||
impl MetaAction for RegionMetaActionList {
|
||||
fn set_prev_version(&mut self, version: ManifestVersion) {
|
||||
self.prev_version = version;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use common_telemetry::logging;
|
||||
|
||||
33
src/storage/src/manifest/helper.rs
Normal file
33
src/storage/src/manifest/helper.rs
Normal file
@@ -0,0 +1,33 @@
|
||||
use std::io::Write;
|
||||
|
||||
use serde::Serialize;
|
||||
use serde_json::to_writer;
|
||||
use snafu::ResultExt;
|
||||
use store_api::manifest::action::VersionHeader;
|
||||
use store_api::manifest::ManifestVersion;
|
||||
|
||||
use crate::error::{EncodeJsonSnafu, Result};
|
||||
|
||||
pub const NEWLINE: &[u8] = b"\n";
|
||||
|
||||
pub fn encode_actions<T: Serialize>(
|
||||
prev_version: ManifestVersion,
|
||||
actions: &[T],
|
||||
) -> Result<Vec<u8>> {
|
||||
let mut bytes = Vec::default();
|
||||
{
|
||||
// Encode prev_version
|
||||
let v = VersionHeader { prev_version };
|
||||
|
||||
to_writer(&mut bytes, &v).context(EncodeJsonSnafu)?;
|
||||
// unwrap is fine here, because we write into a buffer.
|
||||
bytes.write_all(NEWLINE).unwrap();
|
||||
}
|
||||
|
||||
for action in actions {
|
||||
to_writer(&mut bytes, action).context(EncodeJsonSnafu)?;
|
||||
bytes.write_all(NEWLINE).unwrap();
|
||||
}
|
||||
|
||||
Ok(bytes)
|
||||
}
|
||||
177
src/storage/src/manifest/impl_.rs
Normal file
177
src/storage/src/manifest/impl_.rs
Normal file
@@ -0,0 +1,177 @@
|
||||
use std::marker::PhantomData;
|
||||
use std::sync::{
|
||||
atomic::{AtomicU64, Ordering},
|
||||
Arc,
|
||||
};
|
||||
|
||||
use arc_swap::ArcSwap;
|
||||
use async_trait::async_trait;
|
||||
use common_telemetry::logging;
|
||||
use object_store::ObjectStore;
|
||||
use snafu::ensure;
|
||||
use store_api::manifest::action::{self, ProtocolAction, ProtocolVersion};
|
||||
use store_api::manifest::*;
|
||||
|
||||
use crate::error::{Error, ManifestProtocolForbidWriteSnafu, Result};
|
||||
use crate::manifest::storage::ManifestObjectStore;
|
||||
use crate::manifest::storage::ObjectStoreLogIterator;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ManifestImpl<M: MetaAction<Error = Error>> {
|
||||
inner: Arc<ManifestImplInner<M>>,
|
||||
}
|
||||
|
||||
impl<M: MetaAction<Error = Error>> ManifestImpl<M> {
|
||||
pub fn new(manifest_dir: &str, object_store: ObjectStore) -> Self {
|
||||
ManifestImpl {
|
||||
inner: Arc::new(ManifestImplInner::new(manifest_dir, object_store)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Update inner state.
|
||||
pub fn update_state(&self, version: ManifestVersion, protocol: Option<ProtocolAction>) {
|
||||
self.inner.update_state(version, protocol);
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<M: 'static + MetaAction<Error = Error>> Manifest for ManifestImpl<M> {
|
||||
type Error = Error;
|
||||
type MetaAction = M;
|
||||
type MetaActionIterator = MetaActionIteratorImpl<M>;
|
||||
|
||||
async fn update(&self, action_list: M) -> Result<ManifestVersion> {
|
||||
self.inner.save(action_list).await
|
||||
}
|
||||
|
||||
async fn scan(
|
||||
&self,
|
||||
start: ManifestVersion,
|
||||
end: ManifestVersion,
|
||||
) -> Result<Self::MetaActionIterator> {
|
||||
self.inner.scan(start, end).await
|
||||
}
|
||||
|
||||
async fn checkpoint(&self) -> Result<ManifestVersion> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn last_version(&self) -> ManifestVersion {
|
||||
self.inner.last_version()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ManifestImplInner<M: MetaAction<Error = Error>> {
|
||||
store: Arc<ManifestObjectStore>,
|
||||
version: AtomicU64,
|
||||
/// Current using protocol
|
||||
protocol: ArcSwap<ProtocolAction>,
|
||||
/// Current node supported protocols (reader_version, writer_version)
|
||||
supported_reader_version: ProtocolVersion,
|
||||
supported_writer_version: ProtocolVersion,
|
||||
_phantom: PhantomData<M>,
|
||||
}
|
||||
|
||||
pub struct MetaActionIteratorImpl<M: MetaAction<Error = Error>> {
|
||||
log_iter: ObjectStoreLogIterator,
|
||||
reader_version: ProtocolVersion,
|
||||
last_protocol: Option<ProtocolAction>,
|
||||
_phantom: PhantomData<M>,
|
||||
}
|
||||
|
||||
impl<M: MetaAction<Error = Error>> MetaActionIteratorImpl<M> {
|
||||
pub fn last_protocol(&self) -> &Option<ProtocolAction> {
|
||||
&self.last_protocol
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<M: MetaAction<Error = Error>> MetaActionIterator for MetaActionIteratorImpl<M> {
|
||||
type Error = Error;
|
||||
type MetaAction = M;
|
||||
|
||||
async fn next_action(&mut self) -> Result<Option<(ManifestVersion, M)>> {
|
||||
match self.log_iter.next_log().await? {
|
||||
Some((v, bytes)) => {
|
||||
let (action_list, protocol) = M::decode(&bytes, self.reader_version)?;
|
||||
|
||||
if protocol.is_some() {
|
||||
self.last_protocol = protocol;
|
||||
}
|
||||
|
||||
Ok(Some((v, action_list)))
|
||||
}
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<M: MetaAction<Error = Error>> ManifestImplInner<M> {
|
||||
fn new(manifest_dir: &str, object_store: ObjectStore) -> Self {
|
||||
let (reader_version, writer_version) = action::supported_protocol_version();
|
||||
|
||||
Self {
|
||||
store: Arc::new(ManifestObjectStore::new(manifest_dir, object_store)),
|
||||
version: AtomicU64::new(0),
|
||||
protocol: ArcSwap::new(Arc::new(ProtocolAction::new())),
|
||||
supported_reader_version: reader_version,
|
||||
supported_writer_version: writer_version,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn inc_version(&self) -> ManifestVersion {
|
||||
self.version.fetch_add(1, Ordering::Relaxed)
|
||||
}
|
||||
|
||||
fn update_state(&self, version: ManifestVersion, protocol: Option<ProtocolAction>) {
|
||||
self.version.store(version, Ordering::Relaxed);
|
||||
if let Some(p) = protocol {
|
||||
self.protocol.store(Arc::new(p));
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn last_version(&self) -> ManifestVersion {
|
||||
self.version.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
async fn save(&self, action_list: M) -> Result<ManifestVersion> {
|
||||
let protocol = self.protocol.load();
|
||||
|
||||
ensure!(
|
||||
protocol.is_writable(self.supported_writer_version),
|
||||
ManifestProtocolForbidWriteSnafu {
|
||||
min_version: protocol.min_writer_version,
|
||||
supported_version: self.supported_writer_version,
|
||||
}
|
||||
);
|
||||
|
||||
let version = self.inc_version();
|
||||
|
||||
logging::debug!(
|
||||
"Save region metadata action: {:?}, version: {}",
|
||||
action_list,
|
||||
version
|
||||
);
|
||||
|
||||
self.store.save(version, &action_list.encode()?).await?;
|
||||
|
||||
Ok(version)
|
||||
}
|
||||
|
||||
async fn scan(
|
||||
&self,
|
||||
start: ManifestVersion,
|
||||
end: ManifestVersion,
|
||||
) -> Result<MetaActionIteratorImpl<M>> {
|
||||
Ok(MetaActionIteratorImpl {
|
||||
log_iter: self.store.scan(start, end).await?,
|
||||
reader_version: self.supported_reader_version,
|
||||
last_protocol: None,
|
||||
_phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,182 +1,15 @@
|
||||
//! Region manifest impl
|
||||
use std::sync::{
|
||||
atomic::{AtomicU64, Ordering},
|
||||
Arc,
|
||||
};
|
||||
|
||||
use arc_swap::ArcSwap;
|
||||
use async_trait::async_trait;
|
||||
use common_telemetry::logging;
|
||||
use object_store::ObjectStore;
|
||||
use snafu::ensure;
|
||||
use store_api::manifest::action::{self, ProtocolAction, ProtocolVersion};
|
||||
use store_api::manifest::*;
|
||||
|
||||
use crate::error::{Error, ManifestProtocolForbidWriteSnafu, Result};
|
||||
use crate::manifest::action::*;
|
||||
use crate::manifest::storage::ManifestObjectStore;
|
||||
use crate::manifest::storage::ObjectStoreLogIterator;
|
||||
use crate::manifest::ManifestImpl;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct RegionManifest {
|
||||
inner: Arc<RegionManifestInner>,
|
||||
}
|
||||
|
||||
impl RegionManifest {
|
||||
pub fn new(manifest_dir: &str, object_store: ObjectStore) -> Self {
|
||||
RegionManifest {
|
||||
inner: Arc::new(RegionManifestInner::new(manifest_dir, object_store)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Update inner state.
|
||||
pub fn update_state(&self, version: ManifestVersion, protocol: Option<ProtocolAction>) {
|
||||
self.inner.update_state(version, protocol);
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Manifest for RegionManifest {
|
||||
type Error = Error;
|
||||
type MetaAction = RegionMetaActionList;
|
||||
type MetaActionIterator = RegionMetaActionListIterator;
|
||||
|
||||
async fn update(&self, action_list: RegionMetaActionList) -> Result<ManifestVersion> {
|
||||
self.inner.save(action_list).await
|
||||
}
|
||||
|
||||
async fn scan(
|
||||
&self,
|
||||
start: ManifestVersion,
|
||||
end: ManifestVersion,
|
||||
) -> Result<RegionMetaActionListIterator> {
|
||||
self.inner.scan(start, end).await
|
||||
}
|
||||
|
||||
async fn checkpoint(&self) -> Result<ManifestVersion> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn last_version(&self) -> ManifestVersion {
|
||||
self.inner.last_version()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct RegionManifestInner {
|
||||
store: Arc<ManifestObjectStore>,
|
||||
version: AtomicU64,
|
||||
/// Current using protocol
|
||||
protocol: ArcSwap<ProtocolAction>,
|
||||
/// Current node supported protocols (reader_version, writer_version)
|
||||
supported_reader_version: ProtocolVersion,
|
||||
supported_writer_version: ProtocolVersion,
|
||||
}
|
||||
|
||||
pub struct RegionMetaActionListIterator {
|
||||
log_iter: ObjectStoreLogIterator,
|
||||
reader_version: ProtocolVersion,
|
||||
last_protocol: Option<ProtocolAction>,
|
||||
}
|
||||
|
||||
impl RegionMetaActionListIterator {
|
||||
pub fn last_protocol(&self) -> &Option<ProtocolAction> {
|
||||
&self.last_protocol
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl MetaActionIterator for RegionMetaActionListIterator {
|
||||
type Error = Error;
|
||||
type MetaAction = RegionMetaActionList;
|
||||
|
||||
async fn next_action(&mut self) -> Result<Option<(ManifestVersion, RegionMetaActionList)>> {
|
||||
match self.log_iter.next_log().await? {
|
||||
Some((v, bytes)) => {
|
||||
let (action_list, protocol) =
|
||||
RegionMetaActionList::decode(&bytes, self.reader_version)?;
|
||||
|
||||
if protocol.is_some() {
|
||||
self.last_protocol = protocol;
|
||||
}
|
||||
|
||||
Ok(Some((v, action_list)))
|
||||
}
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RegionManifestInner {
|
||||
fn new(manifest_dir: &str, object_store: ObjectStore) -> Self {
|
||||
let (reader_version, writer_version) = action::supported_protocol_version();
|
||||
|
||||
Self {
|
||||
store: Arc::new(ManifestObjectStore::new(manifest_dir, object_store)),
|
||||
version: AtomicU64::new(0),
|
||||
protocol: ArcSwap::new(Arc::new(ProtocolAction::new())),
|
||||
supported_reader_version: reader_version,
|
||||
supported_writer_version: writer_version,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn inc_version(&self) -> ManifestVersion {
|
||||
self.version.fetch_add(1, Ordering::Relaxed)
|
||||
}
|
||||
|
||||
fn update_state(&self, version: ManifestVersion, protocol: Option<ProtocolAction>) {
|
||||
self.version.store(version, Ordering::Relaxed);
|
||||
if let Some(p) = protocol {
|
||||
self.protocol.store(Arc::new(p));
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn last_version(&self) -> ManifestVersion {
|
||||
self.version.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
async fn save(&self, action_list: RegionMetaActionList) -> Result<ManifestVersion> {
|
||||
let protocol = self.protocol.load();
|
||||
|
||||
ensure!(
|
||||
protocol.is_writable(self.supported_writer_version),
|
||||
ManifestProtocolForbidWriteSnafu {
|
||||
min_version: protocol.min_writer_version,
|
||||
supported_version: self.supported_writer_version,
|
||||
}
|
||||
);
|
||||
|
||||
let version = self.inc_version();
|
||||
|
||||
logging::debug!(
|
||||
"Save region metadata action: {:?}, version: {}",
|
||||
action_list,
|
||||
version
|
||||
);
|
||||
|
||||
self.store.save(version, &action_list.encode()?).await?;
|
||||
|
||||
Ok(version)
|
||||
}
|
||||
|
||||
async fn scan(
|
||||
&self,
|
||||
start: ManifestVersion,
|
||||
end: ManifestVersion,
|
||||
) -> Result<RegionMetaActionListIterator> {
|
||||
Ok(RegionMetaActionListIterator {
|
||||
log_iter: self.store.scan(start, end).await?,
|
||||
reader_version: self.supported_reader_version,
|
||||
last_protocol: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
pub type RegionManifest = ManifestImpl<RegionMetaActionList>;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::sync::Arc;
|
||||
|
||||
use object_store::{backend::fs, ObjectStore};
|
||||
use store_api::manifest::{Manifest, MetaActionIterator, MAX_VERSION};
|
||||
use tempdir::TempDir;
|
||||
|
||||
use super::*;
|
||||
|
||||
@@ -103,7 +103,7 @@ async fn test_flush_and_stall() {
|
||||
tester.put(&data).await;
|
||||
|
||||
// Check parquet files.
|
||||
let sst_dir = format!("{}/{}", store_dir, engine::region_sst_dir(REGION_NAME));
|
||||
let sst_dir = format!("{}/{}", store_dir, engine::region_sst_dir("", REGION_NAME));
|
||||
let mut has_parquet_file = false;
|
||||
for entry in std::fs::read_dir(sst_dir).unwrap() {
|
||||
let entry = entry.unwrap();
|
||||
|
||||
@@ -20,8 +20,9 @@ pub async fn new_store_config(
|
||||
region_name: &str,
|
||||
store_dir: &str,
|
||||
) -> StoreConfig<LocalFileLogStore> {
|
||||
let sst_dir = engine::region_sst_dir(region_name);
|
||||
let manifest_dir = engine::region_manifest_dir(region_name);
|
||||
let parent_dir = "";
|
||||
let sst_dir = engine::region_sst_dir(parent_dir, region_name);
|
||||
let manifest_dir = engine::region_manifest_dir(parent_dir, region_name);
|
||||
|
||||
let accessor = Backend::build().root(store_dir).finish().await.unwrap();
|
||||
let object_store = ObjectStore::new(accessor);
|
||||
|
||||
Reference in New Issue
Block a user