mirror of
https://github.com/GreptimeTeam/greptimedb.git
synced 2026-05-27 10:20:38 +00:00
Implement log store append and file set management (#43)
* add log store impl * add some test * delete failing test * fix: concurrent close issue * feat: use arcswap to replace unsafe AtomicPtr * fix: use lock to protect rolling procedure. fix: use try_recv to replace poll_recv on appender task. * chores: 1. use direct tmp dir instead of creating TempDir instance; 2. inline some short function; 3. rename some structs; 4. optimize namespace to arc wrapper inner struct.
This commit is contained in:
@@ -10,6 +10,7 @@ async-trait = "0.1"
|
||||
common-error = { path = "../common/error" }
|
||||
datatypes = { path = "../datatypes" }
|
||||
futures = "0.3"
|
||||
snafu = { version = "0.7", features = ["backtraces"] }
|
||||
|
||||
[dev-dependencies]
|
||||
async-stream = "0.3"
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
//! LogStore APIs.
|
||||
|
||||
use common_error::prelude::ErrorExt;
|
||||
use entry::Offset;
|
||||
|
||||
use crate::logstore::entry::Entry;
|
||||
use crate::logstore::entry::{Entry, Id, Offset};
|
||||
use crate::logstore::entry_stream::SendableEntryStream;
|
||||
use crate::logstore::namespace::Namespace;
|
||||
|
||||
@@ -17,23 +16,28 @@ pub trait LogStore {
|
||||
type Error: ErrorExt + Send + Sync;
|
||||
type Namespace: Namespace;
|
||||
type Entry: Entry;
|
||||
type AppendResponse: AppendResponse;
|
||||
|
||||
/// Append an `Entry` to WAL with given namespace
|
||||
async fn append(&mut self, ns: Self::Namespace, e: Self::Entry) -> Result<Offset, Self::Error>;
|
||||
async fn append(
|
||||
&self,
|
||||
ns: Self::Namespace,
|
||||
mut e: Self::Entry,
|
||||
) -> Result<Self::AppendResponse, Self::Error>;
|
||||
|
||||
// Append a batch of entries atomically and return the offset of first entry.
|
||||
async fn append_batch(
|
||||
&mut self,
|
||||
&self,
|
||||
ns: Self::Namespace,
|
||||
e: Vec<Self::Entry>,
|
||||
) -> Result<Offset, Self::Error>;
|
||||
) -> Result<Id, Self::Error>;
|
||||
|
||||
// Create a new `EntryStream` to asynchronously generates `Entry`.
|
||||
async fn read(
|
||||
&self,
|
||||
ns: Self::Namespace,
|
||||
offset: Offset,
|
||||
) -> Result<SendableEntryStream<Self::Entry>, Self::Error>;
|
||||
id: Id,
|
||||
) -> Result<SendableEntryStream<Self::Entry, Self::Error>, Self::Error>;
|
||||
|
||||
// Create a new `Namespace`.
|
||||
async fn create_namespace(&mut self, ns: Self::Namespace) -> Result<(), Self::Error>;
|
||||
@@ -44,3 +48,9 @@ pub trait LogStore {
|
||||
// List all existing namespaces.
|
||||
async fn list_namespaces(&self) -> Result<Vec<Self::Namespace>, Self::Error>;
|
||||
}
|
||||
|
||||
pub trait AppendResponse: Send + Sync {
|
||||
fn entry_id(&self) -> Id;
|
||||
|
||||
fn offset(&self) -> Offset;
|
||||
}
|
||||
|
||||
@@ -1,14 +1,36 @@
|
||||
pub type Offset = u64;
|
||||
use common_error::ext::ErrorExt;
|
||||
|
||||
pub type Offset = usize;
|
||||
pub type Epoch = u64;
|
||||
pub type Id = u64;
|
||||
|
||||
/// Entry is the minimal data storage unit in `LogStore`.
|
||||
pub trait Entry {
|
||||
pub trait Entry: Send + Sync {
|
||||
type Error: ErrorExt + Send + Sync;
|
||||
|
||||
/// Return contained data of entry.
|
||||
fn data(&self) -> &[u8];
|
||||
|
||||
/// Return offset of entry.
|
||||
/// Return entry id that monotonically increments.
|
||||
fn id(&self) -> Id;
|
||||
|
||||
/// Return file offset of entry.
|
||||
fn offset(&self) -> Offset;
|
||||
|
||||
fn set_offset(&mut self, offset: Offset);
|
||||
|
||||
fn set_id(&mut self, id: Id);
|
||||
|
||||
/// Returns epoch of entry.
|
||||
fn epoch(&self) -> Epoch;
|
||||
|
||||
fn len(&self) -> usize;
|
||||
|
||||
fn is_empty(&self) -> bool;
|
||||
|
||||
fn serialize(&self) -> Vec<u8>;
|
||||
|
||||
fn deserialize(b: impl AsRef<[u8]>) -> Result<Self, Self::Error>
|
||||
where
|
||||
Self: Sized;
|
||||
}
|
||||
|
||||
@@ -1,25 +1,28 @@
|
||||
use std::pin::Pin;
|
||||
|
||||
use common_error::prelude::ErrorExt;
|
||||
use futures::Stream;
|
||||
|
||||
use crate::logstore::entry::Entry;
|
||||
use crate::logstore::Offset;
|
||||
|
||||
pub trait EntryStream: Stream<Item = Vec<Self::Entry>> {
|
||||
pub trait EntryStream: Stream<Item = Result<Vec<Self::Entry>, Self::Error>> {
|
||||
type Error: ErrorExt;
|
||||
type Entry: Entry;
|
||||
fn start_offset(&self) -> Offset;
|
||||
|
||||
fn start_id(&self) -> u64;
|
||||
}
|
||||
|
||||
pub type SendableEntryStream<'a, E> = Pin<Box<dyn Stream<Item = Vec<E>> + Send + 'a>>;
|
||||
pub type SendableEntryStream<'a, I, E> = Pin<Box<dyn Stream<Item = Result<Vec<I>, E>> + Send + 'a>>;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::any::Any;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
use futures::StreamExt;
|
||||
|
||||
use super::*;
|
||||
use crate::logstore::entry::Epoch;
|
||||
use crate::logstore::entry::{Epoch, Id, Offset};
|
||||
|
||||
pub struct SimpleEntry {
|
||||
/// Offset of current entry
|
||||
@@ -30,18 +33,61 @@ mod tests {
|
||||
data: Vec<u8>,
|
||||
}
|
||||
|
||||
use common_error::prelude::{ErrorExt, Snafu};
|
||||
use snafu::{Backtrace, ErrorCompat};
|
||||
|
||||
#[derive(Debug, Snafu)]
|
||||
#[snafu(visibility(pub))]
|
||||
pub struct Error {}
|
||||
|
||||
impl ErrorExt for Error {
|
||||
fn backtrace_opt(&self) -> Option<&Backtrace> {
|
||||
ErrorCompat::backtrace(self)
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Entry for SimpleEntry {
|
||||
type Error = Error;
|
||||
|
||||
fn data(&self) -> &[u8] {
|
||||
self.data.as_slice()
|
||||
&self.data
|
||||
}
|
||||
|
||||
fn id(&self) -> Id {
|
||||
0u64
|
||||
}
|
||||
|
||||
fn offset(&self) -> Offset {
|
||||
self.offset
|
||||
}
|
||||
|
||||
fn set_offset(&mut self, _offset: Offset) {}
|
||||
|
||||
fn set_id(&mut self, _id: Id) {}
|
||||
|
||||
fn epoch(&self) -> Epoch {
|
||||
self.epoch
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.data.len()
|
||||
}
|
||||
|
||||
fn is_empty(&self) -> bool {
|
||||
self.data.is_empty()
|
||||
}
|
||||
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
self.data.clone()
|
||||
}
|
||||
|
||||
fn deserialize(_b: impl AsRef<[u8]>) -> Result<Self, Self::Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl SimpleEntry {
|
||||
@@ -56,20 +102,21 @@ mod tests {
|
||||
}
|
||||
|
||||
pub struct EntryStreamImpl<'a> {
|
||||
inner: SendableEntryStream<'a, SimpleEntry>,
|
||||
start_offset: Offset,
|
||||
inner: SendableEntryStream<'a, SimpleEntry, Error>,
|
||||
start_id: u64,
|
||||
}
|
||||
|
||||
impl<'a> EntryStream for EntryStreamImpl<'a> {
|
||||
type Error = Error;
|
||||
type Entry = SimpleEntry;
|
||||
|
||||
fn start_offset(&self) -> Offset {
|
||||
self.start_offset
|
||||
fn start_id(&self) -> u64 {
|
||||
self.start_id
|
||||
}
|
||||
}
|
||||
|
||||
impl Stream for EntryStreamImpl<'_> {
|
||||
type Item = Vec<SimpleEntry>;
|
||||
type Item = Result<Vec<SimpleEntry>, Error>;
|
||||
|
||||
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||
match Pin::new(&mut self.inner).poll_next(cx) {
|
||||
@@ -83,17 +130,18 @@ mod tests {
|
||||
#[tokio::test]
|
||||
pub async fn test_entry_stream() {
|
||||
let stream = async_stream::stream!({
|
||||
yield vec![SimpleEntry::new("test_entry".as_bytes(), 0, 128)]
|
||||
yield Ok(vec![SimpleEntry::new("test_entry".as_bytes(), 0, 128)])
|
||||
});
|
||||
|
||||
let mut stream_impl = EntryStreamImpl {
|
||||
inner: Box::pin(stream),
|
||||
start_offset: 1234,
|
||||
start_id: 1234,
|
||||
};
|
||||
|
||||
if let Some(v) = stream_impl.next().await {
|
||||
assert_eq!(1, v.len());
|
||||
assert_eq!(b"test_entry", v[0].data());
|
||||
let vec = v.unwrap();
|
||||
assert_eq!(1, vec.len());
|
||||
assert_eq!(b"test_entry", vec[0].data());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user