use crate::{
UniKey, UniResponse, app,
domain::{Aggregate, CommandEnum, EventEnum},
};
use opentelemetry::{
Context, SpanId, TraceFlags, TraceId,
trace::{SpanContext, Status, TraceContextExt},
};
use rkyv::{
Archive, Deserialize,
de::Pool,
rancor::{Error, Strategy},
};
use tokio::sync::{mpsc::error::SendError, oneshot};
use tracing::{error, info, info_span};
use tracing_opentelemetry::OpenTelemetrySpanExt;
use uuid::Uuid;
pub trait Sender<A, C, E>: Sized + 'static
where
A: Aggregate,
C: CommandEnum<A = A, E = E>,
<C as Archive>::Archived: Deserialize<C, Strategy<Pool, Error>>,
E: EventEnum<A = A>,
<E as Archive>::Archived: Deserialize<E, Strategy<Pool, Error>>,
{
#[doc(hidden)]
fn new(ctx: &'static app::Context) -> impl Future<Output = Result<Self, String>>;
fn topic(&self) -> &'static str;
fn send(&self, todo: Todo<A, C, E>) -> Result<(), SendError<Todo<A, C, E>>>;
fn apply(
&self,
UniKey {
agg_id,
com_id,
span_id,
trace_flags,
}: UniKey,
com: C,
) -> impl Future<Output = Result<Vec<u8>, UniResponse>> {
async move {
let (res_tx, res_rx) = oneshot::channel::<Result<Vec<u8>, UniResponse>>();
let span_context = SpanContext::new(
TraceId::from_bytes(com_id),
SpanId::from_bytes(span_id),
TraceFlags::new(trace_flags),
true,
Default::default(),
);
let cx = Context::new().with_remote_span_context(span_context);
let root_span = info_span!("handle_command");
let _ = root_span.set_parent(cx);
let cx = root_span.context();
let cx_clone = cx.clone();
if let Err(e) = self.send(Todo::Reply {
agg_id,
com_id,
cx,
com,
res_tx,
}) {
panic!("聚合命令响应处理器已停止工作:{e}");
}
let result = res_rx.await;
let sp = info_span!("respond");
let _ = sp.set_parent(cx_clone);
sp.in_scope(|| match result {
Ok(Ok(res)) => {
info!(%agg_id, "命令处理成功");
Ok(res)
}
Ok(Err(UniResponse::SendError)) => {
error!(%agg_id, error = ?UniResponse::SendError, "命令处理失败");
root_span.set_status(Status::error("发送命令失败"));
Err(UniResponse::SendError)
}
Ok(Err(UniResponse::ReadError)) => {
error!(%agg_id, error = ?UniResponse::ReadError, "命令处理失败");
root_span.set_status(Status::error("加载聚合事件流失败"));
Err(UniResponse::ReadError)
}
Ok(Err(UniResponse::WriteError)) => {
error!(%agg_id, error = ?UniResponse::WriteError, "命令处理失败");
root_span.set_status(Status::error("聚合事件持久化失败"));
Err(UniResponse::WriteError)
}
Ok(Err(e)) => {
error!(%agg_id, error = ?e, "命令处理失败");
Err(e)
}
Err(e) => {
error!(%agg_id, error = ?e, "命令结果反馈通道意外关闭");
Err(UniResponse::ResponseError)
}
})
}
}
}
pub enum Todo<A, C, E>
where
A: Aggregate,
C: CommandEnum<A = A, E = E>,
<C as Archive>::Archived: Deserialize<C, Strategy<Pool, Error>>,
E: EventEnum<A = A>,
<E as Archive>::Archived: Deserialize<E, Strategy<Pool, Error>>,
{
Reply {
agg_id: Uuid,
com_id: [u8; 16],
cx: Context,
com: C,
res_tx: oneshot::Sender<Result<Vec<u8>, UniResponse>>,
},
Response {
agg_id: Uuid,
com_id: [u8; 16],
res: Result<Vec<u8>, UniResponse>,
},
}