Skip to content

Commit

Permalink
Initial metadata store trait specification
Browse files Browse the repository at this point in the history
  • Loading branch information
tillrohrmann committed Mar 19, 2024
1 parent 37ec635 commit eb453e0
Show file tree
Hide file tree
Showing 4 changed files with 164 additions and 0 deletions.
12 changes: 12 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ restate-invoker-api = { path = "crates/invoker-api" }
restate-invoker-impl = { path = "crates/invoker-impl" }
restate-meta = { path = "crates/meta" }
restate-meta-rest-model = { path = "crates/meta-rest-model" }
restate-metadata-store = { path = "crates/metadata-store" }
restate-network = { path = "crates/network" }
restate-node = { path = "crates/node" }
restate-node-protocol = { path = "crates/node-protocol" }
Expand Down
21 changes: 21 additions & 0 deletions crates/metadata-store/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[package]
name = "restate-metadata-store"
version.workspace = true
authors.workspace = true
edition.workspace = true
rust-version.workspace = true
license.workspace = true
publish = false

[features]

[dependencies]
restate-types = { workspace = true }

async-trait = { workspace = true }
bytes = { workspace = true }
serde = { workspace = true }
static_assertions = { workspace = true }
thiserror = { workspace = true }

[dev-dependencies]
130 changes: 130 additions & 0 deletions crates/metadata-store/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
// Copyright (c) 2024 - Restate Software, Inc., Restate GmbH.
// All rights reserved.
//
// Use of this software is governed by the Business Source License
// included in the LICENSE file.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0.

#![allow(dead_code)]

use async_trait::async_trait;
use bytes::Bytes;
use restate_types::Version;
use serde::{Deserialize, Serialize};
use std::sync::Arc;

#[derive(Debug, thiserror::Error)]
pub enum NetworkError {
#[error("unreachable")]
Unreachable,
}

#[derive(Debug, thiserror::Error)]
pub enum ReadError {
#[error(transparent)]
Network(NetworkError),
}

#[derive(Debug, thiserror::Error)]
pub enum WriteError {
#[error("violated precondition: {0}")]
PreconditionViolation(String),
#[error(transparent)]
Network(NetworkError),
}

#[derive(Debug, Serialize, Deserialize)]
pub struct VersionedValue {
pub version: Version,
pub value: Bytes,
}

impl VersionedValue {
pub fn new(version: Version, value: Bytes) -> Self {
Self { version, value }
}
}

/// Preconditions for the write operations of the [`MetadataStore`].
#[derive(Debug, Serialize, Deserialize)]
pub enum Precondition {
/// No precondition
None,
/// Key-value pair must not exist for the write operation to succeed.
DoesNotExist,
/// Key-value pair must have the provided [`Version`] for the write operation to succeed.
MatchesVersion(Version),
}

/// Metadata store abstraction. The metadata store implementations need to support linearizable
/// reads and atomic compare and swap operations.
#[async_trait]
pub trait MetadataStore {
/// Gets the value and its current version for the given key. If key-value pair is not present,
/// then return [`None`].
async fn get(&self, key: &str) -> Result<Option<VersionedValue>, ReadError>;

/// Gets the current version for the given key. If key-value pair is not present, then return
/// [`None`].
async fn get_version(&self, key: &str) -> Result<Option<Version>, ReadError>;

/// Puts the given key-value pair following the provided precondition. If the operation succeeds,
/// then it returns the new [`Version`] of the key-value pair. If the precondition is not met,
/// then the operation returns a [`WriteError::PreconditionViolation`].
async fn put(
&self,
key: &str,
value: Bytes,
precondition: Precondition,
) -> Result<Version, WriteError>;

/// Deletes the key-value pair for the given key following the provided precondition. If the
/// precondition is not met, then the operation returns a [`WriteError::PreconditionViolation`].
async fn delete(&self, key: &str, precondition: Precondition) -> Result<(), WriteError>;
}

#[derive(Clone)]
struct MetadataStoreClient {
// premature optimization? Maybe introduce trait object once we have multiple implementations?
inner: Arc<dyn MetadataStore + Send + Sync>,
}

impl MetadataStoreClient {
fn new<S>(metadata_store: S) -> Self
where
S: MetadataStore + Send + Sync + 'static,
{
Self {
inner: Arc::new(metadata_store),
}
}
}

#[async_trait]
impl MetadataStore for MetadataStoreClient {
async fn get(&self, key: &str) -> Result<Option<VersionedValue>, ReadError> {
self.inner.get(key).await
}

async fn get_version(&self, key: &str) -> Result<Option<Version>, ReadError> {
self.inner.get_version(key).await
}

async fn put(
&self,
key: &str,
value: Bytes,
precondition: Precondition,
) -> Result<Version, WriteError> {
self.inner.put(key, value, precondition).await
}

async fn delete(&self, key: &str, precondition: Precondition) -> Result<(), WriteError> {
self.inner.delete(key, precondition).await
}
}

static_assertions::assert_impl_all!(MetadataStoreClient: Send, Sync, Clone);

0 comments on commit eb453e0

Please sign in to comment.