define a separate enum for the monotonic/non-monotonic read flag

This commit is contained in:
Armaël Guéneau
2026-05-13 18:36:45 +02:00
committed by Alex
parent d0f89068c6
commit 555e0826a2
3 changed files with 33 additions and 23 deletions
+7 -5
View File
@@ -5,6 +5,7 @@ use serde::{Deserialize, Serialize};
use garage_table::{EnumerationOrder, TableSchema}; use garage_table::{EnumerationOrder, TableSchema};
use garage_model::k2v::item_table::*; use garage_model::k2v::item_table::*;
use garage_model::k2v::rpc::K2VMonotonicRead;
use garage_api_common::helpers::*; use garage_api_common::helpers::*;
@@ -68,7 +69,7 @@ pub async fn handle_read_batch(
async fn handle_read_batch_query( async fn handle_read_batch_query(
ctx: &ReqCtx, ctx: &ReqCtx,
query: ReadBatchQuery, query: ReadBatchQuery,
monotonic_read: bool, monotonic_read: K2VMonotonicRead,
) -> Result<ReadBatchResponse, Error> { ) -> Result<ReadBatchResponse, Error> {
let ReqCtx { let ReqCtx {
garage, bucket_id, .. garage, bucket_id, ..
@@ -92,10 +93,11 @@ async fn handle_read_batch_query(
.start .start
.as_ref() .as_ref()
.ok_or_bad_request("start should be specified if single_item is set")?; .ok_or_bad_request("start should be specified if single_item is set")?;
let item = if monotonic_read { let item = match monotonic_read {
garage.k2v.item_table.get_monotonic(&partition, sk).await? K2VMonotonicRead::Monotonic => {
} else { garage.k2v.item_table.get_monotonic(&partition, sk).await?
garage.k2v.item_table.get(&partition, sk).await? }
K2VMonotonicRead::NonMonotonic => garage.k2v.item_table.get(&partition, sk).await?,
} }
.filter(|e| K2VItemTable::matches_filter(e, &filter)); .filter(|e| K2VItemTable::matches_filter(e, &filter));
match item { match item {
+16 -14
View File
@@ -5,6 +5,7 @@ use hyper::{Request, Response, StatusCode};
use garage_model::k2v::causality::*; use garage_model::k2v::causality::*;
use garage_model::k2v::item_table::*; use garage_model::k2v::item_table::*;
use garage_model::k2v::rpc::K2VMonotonicRead;
use garage_api_common::helpers::*; use garage_api_common::helpers::*;
@@ -24,19 +25,17 @@ pub(crate) fn parse_causality_token(s: &str) -> Result<CausalContext, Error> {
CausalContext::parse(s).ok_or(Error::InvalidCausalityToken) CausalContext::parse(s).ok_or(Error::InvalidCausalityToken)
} }
pub(crate) fn is_monotonic_read(req: &Request<ReqBody>) -> Result<bool, Error> { pub(crate) fn is_monotonic_read(req: &Request<ReqBody>) -> Result<K2VMonotonicRead, Error> {
let v_opt = req let v_opt = req
.headers() .headers()
.get(X_GARAGE_NON_MONOTONIC_READ) .get(X_GARAGE_NON_MONOTONIC_READ)
.map(|s| s.to_str()) .map(|s| s.to_str())
.transpose()?; .transpose()?;
// The header is set to 'true' if the read may be *non*-monotonic,
// and this function returns if we must do a *monotonic* read; hence
// the boolean negation.
match v_opt { match v_opt {
Some("true") => Ok(false), Some("true") => Ok(K2VMonotonicRead::NonMonotonic),
Some("false") | None => Ok(true), // Reads are monotonic by default
Some("false") | None => Ok(K2VMonotonicRead::Monotonic),
Some(s) => Err(Error::InvalidNonMonotonicRead(s.to_string())), Some(s) => Err(Error::InvalidNonMonotonicRead(s.to_string())),
} }
} }
@@ -133,14 +132,17 @@ pub async fn handle_read_item(
partition_key: partition_key.to_string(), partition_key: partition_key.to_string(),
}; };
let item = if monotonic_read { let item = match monotonic_read {
garage K2VMonotonicRead::Monotonic => {
.k2v garage
.item_table .k2v
.get_monotonic(&partition_key, sort_key) .item_table
.await? .get_monotonic(&partition_key, sort_key)
} else { .await?
garage.k2v.item_table.get(&partition_key, sort_key).await? }
K2VMonotonicRead::NonMonotonic => {
garage.k2v.item_table.get(&partition_key, sort_key).await?
}
} }
.ok_or(Error::NoSuchKey)?; .ok_or(Error::NoSuchKey)?;
+10 -4
View File
@@ -38,6 +38,12 @@ const POLL_RANGE_EXTRA_DELAY: Duration = Duration::from_millis(200);
const TIMESTAMP_KEY: &[u8] = b"timestamp"; const TIMESTAMP_KEY: &[u8] = b"timestamp";
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum K2VMonotonicRead {
Monotonic,
NonMonotonic,
}
/// RPC messages for K2V /// RPC messages for K2V
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
enum K2VRpc { enum K2VRpc {
@@ -210,7 +216,7 @@ impl K2VRpcHandler {
sort_key: String, sort_key: String,
causal_context: CausalContext, causal_context: CausalContext,
timeout_msec: u64, timeout_msec: u64,
monotonic_read: bool, monotonic_read: K2VMonotonicRead,
) -> Result<Option<K2VItem>, Error> { ) -> Result<Option<K2VItem>, Error> {
let poll_key = PollKey { let poll_key = PollKey {
partition: K2VItemPartition { partition: K2VItemPartition {
@@ -272,7 +278,7 @@ impl K2VRpcHandler {
} }
if let Some(v) = &resp { if let Some(v) = &resp {
if monotonic_read && not_all_same { if monotonic_read == K2VMonotonicRead::Monotonic && not_all_same {
self.item_table.repair_on_read(&nodes, &[&v]).await?; self.item_table.repair_on_read(&nodes, &[&v]).await?;
} }
} }
@@ -285,7 +291,7 @@ impl K2VRpcHandler {
range: PollRange, range: PollRange,
seen_str: Option<String>, seen_str: Option<String>,
timeout_msec: u64, timeout_msec: u64,
monotonic_read: bool, monotonic_read: K2VMonotonicRead,
) -> Result<Option<(BTreeMap<String, K2VItem>, String)>, HelperError> { ) -> Result<Option<(BTreeMap<String, K2VItem>, String)>, HelperError> {
let has_seen_marker = seen_str.is_some(); let has_seen_marker = seen_str.is_some();
@@ -402,7 +408,7 @@ impl K2VRpcHandler {
} }
} }
if monotonic_read && !to_repair.is_empty() { if monotonic_read == K2VMonotonicRead::Monotonic && !to_repair.is_empty() {
let to_repair: Vec<_> = to_repair let to_repair: Vec<_> = to_repair
.into_iter() .into_iter()
.map(|k| new_items.get(&k).unwrap()) .map(|k| new_items.get(&k).unwrap())