K2V PollRange, version 2 #471
6 changed files with 87 additions and 18 deletions
|
@ -164,6 +164,9 @@ impl ApiHandler for K2VApiServer {
|
||||||
Endpoint::InsertBatch {} => handle_insert_batch(garage, bucket_id, req).await,
|
Endpoint::InsertBatch {} => handle_insert_batch(garage, bucket_id, req).await,
|
||||||
Endpoint::ReadBatch {} => handle_read_batch(garage, bucket_id, req).await,
|
Endpoint::ReadBatch {} => handle_read_batch(garage, bucket_id, req).await,
|
||||||
Endpoint::DeleteBatch {} => handle_delete_batch(garage, bucket_id, req).await,
|
Endpoint::DeleteBatch {} => handle_delete_batch(garage, bucket_id, req).await,
|
||||||
|
Endpoint::PollRange { partition_key } => {
|
||||||
|
handle_poll_range(garage, bucket_id, &partition_key, req).await
|
||||||
|
}
|
||||||
Endpoint::Options => unreachable!(),
|
Endpoint::Options => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,6 @@ use hyper::{Body, Request, Response, StatusCode};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use garage_util::data::*;
|
use garage_util::data::*;
|
||||||
use garage_util::error::Error as GarageError;
|
|
||||||
|
|
||||||
use garage_table::{EnumerationOrder, TableSchema};
|
use garage_table::{EnumerationOrder, TableSchema};
|
||||||
|
|
||||||
|
@ -65,10 +64,7 @@ pub async fn handle_read_batch(
|
||||||
resps.push(resp?);
|
resps.push(resp?);
|
||||||
}
|
}
|
||||||
|
|
||||||
let resp_json = serde_json::to_string_pretty(&resps).map_err(GarageError::from)?;
|
Ok(json_ok_response(&resps)?)
|
||||||
Ok(Response::builder()
|
|
||||||
.status(StatusCode::OK)
|
|
||||||
.body(Body::from(resp_json))?)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_read_batch_query(
|
async fn handle_read_batch_query(
|
||||||
|
@ -160,10 +156,7 @@ pub async fn handle_delete_batch(
|
||||||
resps.push(resp?);
|
resps.push(resp?);
|
||||||
}
|
}
|
||||||
|
|
||||||
let resp_json = serde_json::to_string_pretty(&resps).map_err(GarageError::from)?;
|
Ok(json_ok_response(&resps)?)
|
||||||
Ok(Response::builder()
|
|
||||||
.status(StatusCode::OK)
|
|
||||||
.body(Body::from(resp_json))?)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_delete_batch_query(
|
async fn handle_delete_batch_query(
|
||||||
|
@ -257,6 +250,53 @@ async fn handle_delete_batch_query(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn handle_poll_range(
|
||||||
|
garage: Arc<Garage>,
|
||||||
|
bucket_id: Uuid,
|
||||||
|
partition_key: &str,
|
||||||
|
req: Request<Body>,
|
||||||
|
) -> Result<Response<Body>, Error> {
|
||||||
|
use garage_model::k2v::sub::PollRange;
|
||||||
|
|
||||||
|
let query = parse_json_body::<PollRangeQuery>(req).await?;
|
||||||
|
|
||||||
|
let timeout_msec = query.timeout.unwrap_or(300).clamp(10, 600) * 1000;
|
||||||
|
|
||||||
|
let resp = garage
|
||||||
|
.k2v
|
||||||
|
.rpc
|
||||||
|
.poll_range(
|
||||||
|
PollRange {
|
||||||
|
partition: K2VItemPartition {
|
||||||
|
bucket_id,
|
||||||
|
partition_key: partition_key.to_string(),
|
||||||
|
},
|
||||||
|
start: query.start,
|
||||||
|
end: query.end,
|
||||||
|
prefix: query.prefix,
|
||||||
|
},
|
||||||
|
query.seen_marker,
|
||||||
|
timeout_msec,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
if let Some((items, seen_marker)) = resp {
|
||||||
|
let resp = PollRangeResponse {
|
||||||
|
items: items
|
||||||
|
.into_iter()
|
||||||
|
.map(|(_k, i)| ReadBatchResponseItem::from(i))
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
seen_marker,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(json_ok_response(&resp)?)
|
||||||
|
} else {
|
||||||
|
Ok(Response::builder()
|
||||||
|
.status(StatusCode::NOT_MODIFIED)
|
||||||
|
.body(Body::empty())?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct InsertBatchItem {
|
struct InsertBatchItem {
|
||||||
pk: String,
|
pk: String,
|
||||||
|
@ -361,3 +401,24 @@ struct DeleteBatchResponse {
|
||||||
#[serde(rename = "deletedItems")]
|
#[serde(rename = "deletedItems")]
|
||||||
deleted_items: usize,
|
deleted_items: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct PollRangeQuery {
|
||||||
|
#[serde(default)]
|
||||||
|
prefix: Option<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
start: Option<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
end: Option<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
timeout: Option<u64>,
|
||||||
|
#[serde(default, rename = "seenMarker")]
|
||||||
|
seen_marker: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct PollRangeResponse {
|
||||||
|
items: Vec<ReadBatchResponseItem>,
|
||||||
|
#[serde(rename = "seenMarker")]
|
||||||
|
seen_marker: String,
|
||||||
|
}
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use hyper::{Body, Response, StatusCode};
|
use hyper::{Body, Response};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
use garage_util::data::*;
|
use garage_util::data::*;
|
||||||
use garage_util::error::Error as GarageError;
|
|
||||||
|
|
||||||
use garage_rpc::ring::Ring;
|
use garage_rpc::ring::Ring;
|
||||||
use garage_table::util::*;
|
use garage_table::util::*;
|
||||||
|
@ -12,6 +11,7 @@ use garage_table::util::*;
|
||||||
use garage_model::garage::Garage;
|
use garage_model::garage::Garage;
|
||||||
use garage_model::k2v::item_table::{BYTES, CONFLICTS, ENTRIES, VALUES};
|
use garage_model::k2v::item_table::{BYTES, CONFLICTS, ENTRIES, VALUES};
|
||||||
|
|
||||||
|
use crate::helpers::*;
|
||||||
use crate::k2v::error::*;
|
use crate::k2v::error::*;
|
||||||
use crate::k2v::range::read_range;
|
use crate::k2v::range::read_range;
|
||||||
|
|
||||||
|
@ -68,10 +68,7 @@ pub async fn handle_read_index(
|
||||||
next_start,
|
next_start,
|
||||||
};
|
};
|
||||||
|
|
||||||
let resp_json = serde_json::to_string_pretty(&resp).map_err(GarageError::from)?;
|
Ok(json_ok_response(&resp)?)
|
||||||
Ok(Response::builder()
|
|
||||||
.status(StatusCode::OK)
|
|
||||||
.body(Body::from(resp_json))?)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
|
|
|
@ -208,6 +208,8 @@ pub async fn handle_poll_item(
|
||||||
let causal_context =
|
let causal_context =
|
||||||
CausalContext::parse(&causality_token).ok_or_bad_request("Invalid causality token")?;
|
CausalContext::parse(&causality_token).ok_or_bad_request("Invalid causality token")?;
|
||||||
|
|
||||||
|
let timeout_msec = timeout_secs.unwrap_or(300).clamp(10, 600) * 1000;
|
||||||
|
|
||||||
let item = garage
|
let item = garage
|
||||||
.k2v
|
.k2v
|
||||||
.rpc
|
.rpc
|
||||||
|
@ -216,7 +218,7 @@ pub async fn handle_poll_item(
|
||||||
partition_key,
|
partition_key,
|
||||||
sort_key,
|
sort_key,
|
||||||
causal_context,
|
causal_context,
|
||||||
timeout_secs.unwrap_or(300) * 1000,
|
timeout_msec,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,9 @@ pub enum Endpoint {
|
||||||
causality_token: String,
|
causality_token: String,
|
||||||
timeout: Option<u64>,
|
timeout: Option<u64>,
|
||||||
},
|
},
|
||||||
|
PollRange {
|
||||||
|
partition_key: String,
|
||||||
|
},
|
||||||
ReadBatch {
|
ReadBatch {
|
||||||
},
|
},
|
||||||
ReadIndex {
|
ReadIndex {
|
||||||
|
@ -113,6 +116,7 @@ impl Endpoint {
|
||||||
@gen_parser
|
@gen_parser
|
||||||
(query.keyword.take().unwrap_or_default(), partition_key, query, None),
|
(query.keyword.take().unwrap_or_default(), partition_key, query, None),
|
||||||
key: [
|
key: [
|
||||||
|
POLL_RANGE => PollRange,
|
||||||
],
|
],
|
||||||
no_key: [
|
no_key: [
|
||||||
EMPTY => ReadBatch,
|
EMPTY => ReadBatch,
|
||||||
|
@ -142,6 +146,7 @@ impl Endpoint {
|
||||||
@gen_parser
|
@gen_parser
|
||||||
(query.keyword.take().unwrap_or_default(), partition_key, query, None),
|
(query.keyword.take().unwrap_or_default(), partition_key, query, None),
|
||||||
key: [
|
key: [
|
||||||
|
POLL_RANGE => PollRange,
|
||||||
],
|
],
|
||||||
no_key: [
|
no_key: [
|
||||||
EMPTY => InsertBatch,
|
EMPTY => InsertBatch,
|
||||||
|
@ -234,7 +239,8 @@ impl Endpoint {
|
||||||
generateQueryParameters! {
|
generateQueryParameters! {
|
||||||
keywords: [
|
keywords: [
|
||||||
"delete" => DELETE,
|
"delete" => DELETE,
|
||||||
"search" => SEARCH
|
"search" => SEARCH,
|
||||||
|
"poll_range" => POLL_RANGE
|
||||||
],
|
],
|
||||||
fields: [
|
fields: [
|
||||||
"prefix" => prefix,
|
"prefix" => prefix,
|
||||||
|
|
|
@ -5,4 +5,4 @@ pub mod item_table;
|
||||||
|
|
||||||
pub mod rpc;
|
pub mod rpc;
|
||||||
|
|
||||||
pub(crate) mod sub;
|
pub mod sub;
|
||||||
|
|
Loading…
Reference in a new issue