use std::fmt;
use std::sync::Arc;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum QwpWsProgress {
Background,
Manual,
}
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct QwpWsSenderError {
pub category: QwpWsErrorCategory,
pub applied_policy: QwpWsErrorPolicy,
pub status: Option<u8>,
pub message: Option<String>,
pub message_sequence: Option<u64>,
pub from_fsn: u64,
pub to_fsn: u64,
}
#[derive(Clone)]
pub struct QwpWsErrorHandler {
handler: Arc<dyn Fn(&QwpWsSenderError) + Send + Sync>,
}
impl QwpWsErrorHandler {
pub fn new<F>(handler: F) -> Self
where
F: Fn(&QwpWsSenderError) + Send + Sync + 'static,
{
Self {
handler: Arc::new(handler),
}
}
pub(crate) fn log_default() -> Self {
Self::new(default_qwp_ws_error_handler)
}
pub(crate) fn handle(&self, error: &QwpWsSenderError) {
(self.handler)(error);
}
}
impl fmt::Debug for QwpWsErrorHandler {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("QwpWsErrorHandler { .. }")
}
}
fn default_qwp_ws_error_handler(error: &QwpWsSenderError) {
let status = error
.status
.map(|status| format!("0x{status:02x}"))
.unwrap_or_else(|| "none".to_string());
let sequence = error
.message_sequence
.map(|sequence| sequence.to_string())
.unwrap_or_else(|| "none".to_string());
let message = error.message.as_deref().unwrap_or("");
if error.applied_policy == QwpWsErrorPolicy::Terminal {
log::error!(
target: "questdb::ingress",
"QWP/WebSocket server rejected batch [category={:?}, policy={:?}, status={}, fsn=[{},{}], seq={}, msg={}]",
error.category,
error.applied_policy,
status,
error.from_fsn,
error.to_fsn,
sequence,
message
);
} else {
log::warn!(
target: "questdb::ingress",
"QWP/WebSocket server rejected batch [category={:?}, policy={:?}, status={}, fsn=[{},{}], seq={}, msg={}]",
error.category,
error.applied_policy,
status,
error.from_fsn,
error.to_fsn,
sequence,
message
);
}
}
#[non_exhaustive]
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub struct QwpWsTotals {
pub frames_sent: u64,
pub frames_replayed: u64,
pub acks: u64,
pub reconnect_attempts: u64,
pub reconnects_succeeded: u64,
pub server_errors: u64,
}
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum QwpWsErrorCategory {
SchemaMismatch,
ParseError,
InternalError,
SecurityError,
WriteError,
NotWritable,
ProtocolViolation,
Unknown,
}
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum QwpWsErrorPolicy {
Retriable,
RetriableOther,
Terminal,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct QwpWsRoleReject {
pub(crate) role: String,
pub(crate) zone: Option<String>,
}
impl QwpWsRoleReject {
pub(crate) fn new(role: &str, zone: Option<&str>) -> Self {
Self {
role: role.to_string(),
zone: zone.map(str::to_string),
}
}
pub(crate) fn is_transient(&self) -> bool {
self.role.eq_ignore_ascii_case("PRIMARY_CATCHUP")
}
}