#[cfg(feature = "bench")]
use std::time::Instant;
use serde::{Deserialize, Serialize};
use tokio::sync::oneshot;
use crate::protocols::*;
use dynamo_tokens::SequenceHash;
use rustc_hash::FxHashMap;
pub trait MaybeError {
fn from_err(err: impl std::error::Error + 'static) -> Self;
fn err(&self) -> Option<Box<dyn std::error::Error + Send + Sync>>;
}
#[derive(Debug, thiserror::Error)]
pub enum KvRouterError {
#[error("Block not found")]
BlockNotFound,
#[error("Indexer is offline")]
IndexerOffline,
#[error("Indexer dropped the request")]
IndexerDroppedRequest,
#[error("Prune operation failed: {0}")]
PruneFailed(String),
#[error("Unsupported operation: {0}")]
Unsupported(String),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub struct AnchorRef {
pub anchor_id: ExternalSequenceBlockHash,
pub anchor_local_hash: LocalBlockHash,
pub anchor_depth: usize,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub struct AnchorTask {
pub anchor_id: ExternalSequenceBlockHash,
pub anchor_local_hash: LocalBlockHash,
pub anchor_depth: usize,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct WorkerKvQueryRequest {
pub worker_id: WorkerId,
pub dp_rank: DpRank,
pub start_event_id: Option<u64>,
pub end_event_id: Option<u64>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum WorkerKvQueryResponse {
Events {
events: Vec<RouterEvent>,
last_event_id: u64,
},
TreeDump {
events: Vec<RouterEvent>,
last_event_id: u64,
},
TooNew {
requested_start: Option<u64>,
requested_end: Option<u64>,
newest_available: u64,
},
InvalidRange { start_id: u64, end_id: u64 },
Error(String),
}
impl MaybeError for WorkerKvQueryResponse {
fn from_err(err: impl std::error::Error + 'static) -> Self {
WorkerKvQueryResponse::Error(err.to_string())
}
fn err(&self) -> Option<Box<dyn std::error::Error + Send + Sync>> {
match self {
WorkerKvQueryResponse::Error(msg) => Some(Box::new(std::io::Error::other(msg.clone()))),
_ => None,
}
}
}
#[cfg(feature = "runtime-protocols")]
impl dynamo_runtime::protocols::maybe_error::MaybeError for WorkerKvQueryResponse {
fn from_err(err: impl std::error::Error + 'static) -> Self {
WorkerKvQueryResponse::Error(err.to_string())
}
fn err(&self) -> Option<dynamo_runtime::error::DynamoError> {
match self {
WorkerKvQueryResponse::Error(msg) => {
Some(dynamo_runtime::error::DynamoError::msg(msg.clone()))
}
_ => None,
}
}
}
pub const KV_INDEXER_QUERY_ENDPOINT: &str = "kv_indexer_query";
pub const KV_INDEXER_RECORD_ROUTING_DECISION_ENDPOINT: &str = "kv_indexer_record_routing_decision";
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct IndexerQueryRequest {
pub model_name: String,
pub block_hashes: Vec<LocalBlockHash>,
#[serde(default)]
pub device_only: bool,
}
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
pub struct WireOverlapScores {
pub scores: Vec<(WorkerWithDpRank, u32)>,
pub frequencies: Vec<usize>,
}
impl From<OverlapScores> for WireOverlapScores {
fn from(s: OverlapScores) -> Self {
Self {
scores: s.scores.into_iter().collect(),
frequencies: s.frequencies,
}
}
}
impl From<WireOverlapScores> for OverlapScores {
fn from(w: WireOverlapScores) -> Self {
Self {
scores: w.scores.into_iter().collect(),
frequencies: w.frequencies,
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
pub struct WireLowerTierMatchDetails {
pub hits: Vec<(WorkerWithDpRank, usize)>,
}
impl From<&super::lower_tier::LowerTierMatchDetails> for WireLowerTierMatchDetails {
fn from(d: &super::lower_tier::LowerTierMatchDetails) -> Self {
Self {
hits: d.hits.iter().map(|(w, h)| (*w, *h)).collect(),
}
}
}
impl From<WireLowerTierMatchDetails> for super::lower_tier::LowerTierMatchDetails {
fn from(w: WireLowerTierMatchDetails) -> Self {
Self {
hits: w.hits.into_iter().collect(),
next_continuations: Default::default(),
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
pub struct WireTieredMatchDetails {
pub device: WireOverlapScores,
pub lower_tier: Vec<(StorageTier, WireLowerTierMatchDetails)>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum IndexerQueryResponse {
TieredScores(WireTieredMatchDetails),
Error(String),
}
impl MaybeError for IndexerQueryResponse {
fn from_err(err: impl std::error::Error + 'static) -> Self {
IndexerQueryResponse::Error(err.to_string())
}
fn err(&self) -> Option<Box<dyn std::error::Error + Send + Sync>> {
match self {
IndexerQueryResponse::Error(msg) => Some(Box::new(std::io::Error::other(msg.clone()))),
_ => None,
}
}
}
#[cfg(feature = "runtime-protocols")]
impl dynamo_runtime::protocols::maybe_error::MaybeError for IndexerQueryResponse {
fn from_err(err: impl std::error::Error + 'static) -> Self {
IndexerQueryResponse::Error(err.to_string())
}
fn err(&self) -> Option<dynamo_runtime::error::DynamoError> {
match self {
IndexerQueryResponse::Error(msg) => {
Some(dynamo_runtime::error::DynamoError::msg(msg.clone()))
}
_ => None,
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct IndexerRecordRoutingDecisionRequest {
pub model_name: String,
pub worker: WorkerWithDpRank,
pub local_hashes: Vec<LocalBlockHash>,
pub sequence_hashes: Vec<SequenceHash>,
}
#[derive(Debug, Clone)]
pub struct RoutingDecisionHashes {
pub local_hashes: Vec<LocalBlockHash>,
pub sequence_hashes: Vec<SequenceHash>,
}
impl RoutingDecisionHashes {
pub fn from_local_hashes(local_hashes: Vec<LocalBlockHash>) -> Self {
let sequence_hashes = compute_seq_hash_for_block(&local_hashes);
Self {
local_hashes,
sequence_hashes,
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum IndexerRecordRoutingDecisionResponse {
Recorded,
Error(String),
}
impl MaybeError for IndexerRecordRoutingDecisionResponse {
fn from_err(err: impl std::error::Error + 'static) -> Self {
IndexerRecordRoutingDecisionResponse::Error(err.to_string())
}
fn err(&self) -> Option<Box<dyn std::error::Error + Send + Sync>> {
match self {
IndexerRecordRoutingDecisionResponse::Error(msg) => {
Some(Box::new(std::io::Error::other(msg.clone())))
}
_ => None,
}
}
}
#[cfg(feature = "runtime-protocols")]
impl dynamo_runtime::protocols::maybe_error::MaybeError for IndexerRecordRoutingDecisionResponse {
fn from_err(err: impl std::error::Error + 'static) -> Self {
IndexerRecordRoutingDecisionResponse::Error(err.to_string())
}
fn err(&self) -> Option<dynamo_runtime::error::DynamoError> {
match self {
IndexerRecordRoutingDecisionResponse::Error(msg) => {
Some(dynamo_runtime::error::DynamoError::msg(msg.clone()))
}
_ => None,
}
}
}
#[derive(Debug, Clone, Default)]
pub struct MatchDetails {
pub overlap_scores: OverlapScores,
pub last_matched_hashes: FxHashMap<WorkerWithDpRank, ExternalSequenceBlockHash>,
}
impl MatchDetails {
pub fn new() -> Self {
Self::default()
}
}
pub struct MatchRequest {
pub sequence: Vec<LocalBlockHash>,
pub early_exit: bool,
pub resp: oneshot::Sender<OverlapScores>,
#[cfg(feature = "bench")]
pub created_at: Instant,
}
impl MatchRequest {
pub(super) fn new(
sequence: Vec<LocalBlockHash>,
early_exit: bool,
resp: oneshot::Sender<OverlapScores>,
) -> Self {
Self {
sequence,
early_exit,
resp,
#[cfg(feature = "bench")]
created_at: Instant::now(),
}
}
}
pub struct MatchDetailsRequest {
pub sequence: Vec<LocalBlockHash>,
pub early_exit: bool,
pub resp: oneshot::Sender<MatchDetails>,
}
impl MatchDetailsRequest {
pub(super) fn new(
sequence: Vec<LocalBlockHash>,
early_exit: bool,
resp: oneshot::Sender<MatchDetails>,
) -> Self {
Self {
sequence,
early_exit,
resp,
}
}
}
pub struct DumpRequest {
pub resp: oneshot::Sender<Vec<RouterEvent>>,
}
pub struct FlushRequest {
pub resp: oneshot::Sender<()>,
}
pub struct GetWorkersRequest {
pub resp: oneshot::Sender<Vec<WorkerId>>,
}
#[derive(Debug, Default)]
pub struct WorkerLookupStats {
pub worker_blocks: Vec<(WorkerWithDpRank, usize)>,
}
impl WorkerLookupStats {
pub fn from_worker_block_counts(
counts: impl IntoIterator<Item = (WorkerWithDpRank, usize)>,
) -> Self {
Self {
worker_blocks: counts
.into_iter()
.filter(|(_, block_count)| *block_count > 0)
.collect(),
}
}
pub fn worker_count(&self) -> usize {
self.worker_blocks.len()
}
pub fn block_count(&self) -> usize {
self.worker_blocks
.iter()
.map(|(_, block_count)| *block_count)
.sum()
}
pub fn block_count_for_worker(&self, worker: WorkerWithDpRank) -> Option<usize> {
self.worker_blocks
.iter()
.find_map(|(candidate, block_count)| (*candidate == worker).then_some(*block_count))
}
}
pub enum WorkerTask {
Event(RouterEvent),
EventWithAck {
event: RouterEvent,
resp: oneshot::Sender<bool>,
},
Anchor {
worker: WorkerWithDpRank,
anchor: AnchorTask,
},
RemoveWorker(WorkerId),
RemoveWorkerDpRank(WorkerId, DpRank),
CleanupStaleChildren,
DumpEvents(oneshot::Sender<anyhow::Result<Vec<RouterEvent>>>),
Stats(oneshot::Sender<WorkerLookupStats>),
Flush(oneshot::Sender<()>),
Terminate,
}
pub(super) struct RoutingDecisionRequest {
pub(super) worker: WorkerWithDpRank,
pub(super) local_hashes: Vec<LocalBlockHash>,
pub(super) sequence_hashes: Vec<SequenceHash>,
}