use std::sync::Arc;
use blake2b_simd::Params as Blake2bParams;
use serde_json::{Map, Number, Value};
use zakura_jsonl_trace::{JsonlEventEmitter, JsonlTraceEvent, JsonlTracer};
use super::{ZakuraPeerId, ZakuraRejectReason};
pub type ZakuraTraceTable = zakura_jsonl_trace::JsonlTraceTable;
pub const HANDSHAKE_TABLE: ZakuraTraceTable = ZakuraTraceTable::new("handshake", "handshake.jsonl");
pub const CONN_TABLE: ZakuraTraceTable = ZakuraTraceTable::new("conn", "conn.jsonl");
pub const STREAM_TABLE: ZakuraTraceTable = ZakuraTraceTable::new("stream", "stream.jsonl");
pub const DISCOVERY_TABLE: ZakuraTraceTable = ZakuraTraceTable::new("discovery", "discovery.jsonl");
pub const RATELIMIT_TABLE: ZakuraTraceTable = ZakuraTraceTable::new("ratelimit", "ratelimit.jsonl");
pub const HEADER_SYNC_TABLE: ZakuraTraceTable =
ZakuraTraceTable::new("header_sync", "header_sync.jsonl");
#[allow(dead_code)]
pub(crate) mod header_sync_trace {
pub(crate) const EVENT: &str = "event";
pub(crate) const PEER: &str = "peer";
pub(crate) const SESSION_ID: &str = "session_id";
pub(crate) const DIRECTION: &str = "direction";
pub(crate) const REASON: &str = "reason";
pub(crate) const BOUNDARY: &str = "boundary";
pub(crate) const DISPOSITION: &str = "disposition";
pub(crate) const STATE_VERSION: &str = "state_version";
pub(crate) const HEADER_GENERATION: &str = "header_generation";
pub(crate) const VERIFIED_GENERATION: &str = "verified_generation";
pub(crate) const BRANCH_ANCHOR: &str = "branch_anchor";
pub(crate) const BRANCH_TARGET: &str = "branch_target";
pub(crate) const REQUEST_ID: &str = "request_id";
pub(crate) const STREAM_VERSION: &str = "stream_version";
pub(crate) const TARGET_HASH: &str = "target_hash";
pub(crate) const COMMON_ANCESTOR_HEIGHT: &str = "common_ancestor_height";
pub(crate) const COMMON_ANCESTOR_HASH: &str = "common_ancestor_hash";
pub(crate) const LOCATOR_COUNT: &str = "locator_count";
pub(crate) const LOCATOR_HEAD: &str = "locator_head";
pub(crate) const HEADER_COUNT: &str = "header_count";
pub(crate) const COMPLETE: &str = "complete";
pub(crate) const TREE_AUX_SCHEMA: &str = "tree_aux_schema";
pub(crate) const OUTCOME: &str = "outcome";
pub(crate) const INBOUND_COUNT: &str = "inbound_count";
pub(crate) const OUTBOUND_COUNT: &str = "outbound_count";
pub(crate) const STAGE: &str = "stage";
pub(crate) const CATEGORY: &str = "category";
pub(crate) const ATTRIBUTION: &str = "attribution";
pub(crate) const CAUSE: &str = "cause";
pub(crate) const OPERATION: &str = "operation";
pub(crate) const WORK_ANCHOR_HEIGHT: &str = "work_anchor_height";
pub(crate) const WORK_ANCHOR_HASH: &str = "work_anchor_hash";
pub(crate) const SELECTED_TIP_HEIGHT: &str = "selected_tip_height";
pub(crate) const SELECTED_TIP_HASH: &str = "selected_tip_hash";
pub(crate) const MAX_HEADERS_PER_RESPONSE: &str = "max_headers_per_response";
pub(crate) const MAX_INFLIGHT_REQUESTS: &str = "max_inflight_requests";
pub(crate) const MAX_MESSAGE_BYTES: &str = "max_message_bytes";
pub(crate) const TREE_AUX_SCHEMA_MASK: &str = "tree_aux_schema_mask";
pub(crate) const OLD_SELECTED_HEIGHT: &str = "old_selected_height";
pub(crate) const OLD_SELECTED_HASH: &str = "old_selected_hash";
pub(crate) const NEW_SELECTED_HEIGHT: &str = "new_selected_height";
pub(crate) const NEW_SELECTED_HASH: &str = "new_selected_hash";
pub(crate) const HEIGHT: &str = "height";
pub(crate) const REPAIR_GENERATION: &str = "repair_generation";
pub(crate) const PHASE: &str = "phase";
pub(crate) const SUPPLIER_COUNT: &str = "supplier_count";
pub(crate) const PREDECESSOR_HEIGHT: &str = "predecessor_height";
pub(crate) const PEERS_CONSIDERED: &str = "peers_considered";
pub(crate) const REJECTED_HEIGHT: &str = "rejected_height";
pub(crate) const REJECTED_CAPACITY: &str = "rejected_capacity";
pub(crate) const REJECTED_SCHEMA: &str = "rejected_schema";
pub(crate) const REJECTED_BUSY: &str = "rejected_busy";
pub(crate) const REJECTED_TRIED: &str = "rejected_tried";
pub(crate) const BEST_PEER_HEIGHT: &str = "best_peer_height";
pub(crate) const BEST_PEER_HASH: &str = "best_peer_hash";
pub(crate) const HEADER_PEER_CONNECTED: &str = "header_peer_connected";
pub(crate) const HEADER_PEER_DISCONNECTED: &str = "header_peer_disconnected";
pub(crate) const HEADER_STATUS_SENT: &str = "header_status_sent";
pub(crate) const HEADER_STATUS_RECEIVED: &str = "header_status_received";
pub(crate) const HEADER_REQUEST_SENT: &str = "header_request_sent";
pub(crate) const HEADER_REQUEST_TERMINAL: &str = "header_request_terminal";
pub(crate) const HEADER_RESPONSE_RECEIVED: &str = "header_response_received";
pub(crate) const HEADER_RESPONSE_SERVED: &str = "header_response_served";
pub(crate) const HEADER_OUTCOME: &str = "header_outcome";
pub(crate) const HEADER_TARGET_ADMITTED: &str = "header_target_admitted";
pub(crate) const HEADER_TARGET_REJECTED: &str = "header_target_rejected";
pub(crate) const HEADER_PEER_VIOLATION: &str = "header_peer_violation";
pub(crate) const HEADER_SNAPSHOT_OBSERVED: &str = "header_snapshot_observed";
pub(crate) const HEADER_VCT_REPAIR_STATE: &str = "header_vct_repair_state";
}
pub const LEGACY_REQUEST_TABLE: ZakuraTraceTable =
ZakuraTraceTable::new("legacy_request", "legacy_request.jsonl");
pub const BLOCK_SYNC_TABLE: ZakuraTraceTable =
ZakuraTraceTable::new("block_sync", "block_sync.jsonl");
pub const COMMIT_STATE_TABLE: ZakuraTraceTable =
ZakuraTraceTable::new("commit_state", "commit_state.jsonl");
pub const QUEUE_SEND_TABLE: ZakuraTraceTable =
ZakuraTraceTable::new("queue_send", "queue_send.jsonl");
#[allow(dead_code)] pub mod queue_send_trace {
pub const EVENT: &str = "event";
pub const QUEUE_SEND_FAILED: &str = "queue_send_failed";
pub const SERVICE: &str = "service";
pub const MESSAGE: &str = "message";
pub const PEER: &str = "peer";
pub const ERROR: &str = "error";
pub const REASON: &str = "reason";
pub const QUEUE_CAPACITY: &str = "queue_capacity";
pub const QUEUE_MAX_CAPACITY: &str = "queue_max_capacity";
pub const RANGE_START: &str = "range_start";
pub const RANGE_COUNT: &str = "range_count";
pub const SESSION_ID: &str = "session_id";
pub const REQUEST_ID: &str = "request_id";
}
#[allow(dead_code)] pub mod block_sync_trace {
pub const EVENT: &str = "event";
pub const PEER: &str = "peer";
pub const KIND: &str = "kind";
pub const HEIGHT: &str = "height";
pub const HASH: &str = "hash";
pub const RANGE_START: &str = "range_start";
pub const RANGE_COUNT: &str = "range_count";
pub const EXPECTED_COUNT: &str = "expected_count";
pub const ESTIMATED_BYTES: &str = "estimated_bytes";
pub const SERIALIZED_BYTES: &str = "serialized_bytes";
pub const DECODED_ATTRIBUTED_MEMORY_SIZE_BYTES: &str = "decoded_attributed_memory_size_bytes";
pub const SEQUENCER_INPUT_DECODED_ATTRIBUTED_MEMORY_BYTES: &str =
"sequencer_input_decoded_attributed_memory_bytes";
pub const REORDER_DECODED_ATTRIBUTED_MEMORY_BYTES: &str =
"reorder_decoded_attributed_memory_bytes";
pub const APPLYING_DECODED_ATTRIBUTED_MEMORY_BYTES: &str =
"applying_decoded_attributed_memory_bytes";
pub const ACTIVE_PIPELINE_DECODED_ATTRIBUTED_MEMORY_BYTES: &str =
"active_pipeline_decoded_attributed_memory_bytes";
pub const ELAPSED_MS: &str = "elapsed_ms";
pub const PREPARE_ELAPSED_MS: &str = "prepare_elapsed_ms";
pub const SEND_ELAPSED_MS: &str = "send_elapsed_ms";
pub const RESULT: &str = "result";
pub const REASON: &str = "reason";
pub const ERROR: &str = "error";
pub const APPLY_TOKEN: &str = "apply_token";
pub const REQUEST_FLOOR: &str = "request_floor";
pub const BODY_DOWNLOAD_FLOOR: &str = "body_download_floor";
pub const FLOOR_GAP_HEIGHT: &str = "floor_gap_height";
pub const FLOOR_GAP_STATE: &str = "floor_gap_state";
pub const FLOOR_GAP_SERVABLE_PEERS: &str = "floor_gap_servable_peers";
pub const FLOOR_GAP_AVAILABLE_PEERS: &str = "floor_gap_available_peers";
pub const FLOOR_GAP_OUTSTANDING_PEERS: &str = "floor_gap_outstanding_peers";
pub const FLOOR_GAP_OLDEST_OUTSTANDING_MS: &str = "floor_gap_oldest_outstanding_ms";
pub const FLOOR_GAP_NEXT_DEADLINE_MS: &str = "floor_gap_next_deadline_ms";
pub const VERIFIED_BLOCK_TIP: &str = "verified_block_tip";
pub const BEST_HEADER_TIP: &str = "best_header_tip";
pub const BODY_LAG: &str = "body_lag";
pub const APPLYING: &str = "applying";
pub const SUBMITTED_APPLIES: &str = "submitted_applies";
pub const REORDER: &str = "reorder";
pub const OUTSTANDING: &str = "outstanding";
pub const BUDGET_AVAILABLE: &str = "budget_available";
pub const BUDGET_RESERVED: &str = "budget_reserved";
pub const BUDGET_RESERVED_AFTER: &str = "budget_reserved_after";
pub const RECEIVED_BYTES_PER_SEC: &str = "received_bytes_per_sec";
pub const RECEIVED_BLOCKS_PER_SEC: &str = "received_blocks_per_sec";
pub const COMMITTED_BYTES_PER_SEC: &str = "committed_bytes_per_sec";
pub const COMMITTED_BLOCKS_PER_SEC: &str = "committed_blocks_per_sec";
pub const DOWNLOAD_BLOCKED_ON_BUDGET: &str = "download_blocked_on_budget";
pub const PEERS_WANTING_SLOTS: &str = "peers_wanting_slots";
pub const PEERS: &str = "peers";
pub const ACTIVE_CONNECTIONS: &str = "active_connections";
pub const PEERS_WITH_STATUS: &str = "peers_with_status";
pub const NEEDED_MIN: &str = "needed_min";
pub const NEEDED_COUNT: &str = "needed_count";
pub const QUEUE_LEN: &str = "queue_len";
pub const QUEUE_BLOCKS: &str = "queue_blocks";
pub const QUEUE_MIN_START: &str = "queue_min_start";
pub const ASSIGNED_LEN: &str = "assigned_len";
pub const LOCAL_BODY_WORK: &str = "local_body_work";
pub const REFILL_LOW_WATER: &str = "refill_low_water";
pub const COVERED_MAX_END: &str = "covered_max_end";
pub const BLOCK_STATUS_RECEIVED: &str = "block_status_received";
pub const BLOCK_STATUS_SENT: &str = "block_status_sent";
pub const BLOCK_STATUS_SEND_FAILED: &str = "block_status_send_failed";
pub const BLOCK_PEER_CONNECTED: &str = "block_peer_connected";
pub const BLOCK_PEER_DISCONNECTED: &str = "block_peer_disconnected";
pub const BLOCK_GET_BLOCKS_SENT: &str = "block_get_blocks_sent";
pub const BLOCK_EVENT_RECEIVED: &str = "block_event_received";
pub const BLOCK_MESSAGE_RECEIVED: &str = "block_message_received";
pub const BLOCK_MESSAGE_SENT: &str = "block_message_sent";
pub const BLOCK_ACTION_DISPATCHED: &str = "block_action_dispatched";
pub const BLOCK_BODY_RECEIVED: &str = "block_body_received";
pub const BLOCK_BODY_SEQUENCER_SENT: &str = "block_body_sequencer_sent";
pub const BLOCK_BODY_DECODE_PERMIT: &str = "block_body_decode_permit";
pub const BLOCK_BODY_ACCEPTED: &str = "block_body_accepted";
pub const BLOCK_SEQUENCER_CONTROL_SENT: &str = "block_sequencer_control_sent";
pub const BLOCK_BODY_SUBMITTED: &str = "block_body_submitted";
pub const BLOCK_BODY_SUBMISSION_RETRY_SCHEDULED: &str = "block_body_submission_retry_scheduled";
pub const BLOCK_APPLY_FINISHED: &str = "block_apply_finished";
pub const BLOCK_RANGE_UNAVAILABLE: &str = "block_range_unavailable";
pub const BLOCK_RANGE_RESPONSE_SENT: &str = "block_range_response_sent";
pub const BLOCK_WORK_EXTENDED: &str = "block_work_extended";
pub const BLOCK_WORK_TAKEN: &str = "block_work_taken";
pub const BLOCK_WORK_RETURNED: &str = "block_work_returned";
pub const BLOCK_FLOOR_WATCHDOG_CANCELLED: &str = "block_floor_watchdog_cancelled";
pub const BLOCK_FILL_STOP: &str = "block_fill_stop";
pub const FILL_STOP_REASON: &str = "fill_stop_reason";
pub const FILL_SENT: &str = "fill_sent";
pub const BLOCK_FRONTIERS_CHANGED: &str = "block_frontiers_changed";
pub const BLOCK_CHAIN_TIP_RESET: &str = "block_chain_tip_reset";
pub const BLOCK_FRONTIER_RESET_CLASSIFIED: &str = "block_frontier_reset_classified";
pub const BLOCK_SYNC_STATE: &str = "block_sync_state";
pub const BLOCK_PEER_BBR: &str = "block_peer_bbr";
pub const BLOCK_PEER_PARKED: &str = "block_peer_parked";
}
#[allow(dead_code)] pub mod discovery_trace {
pub const EVENT: &str = "event";
pub const PEER: &str = "peer";
pub const RESULT: &str = "result";
pub const DISCOVERY_DIAL_RESULT: &str = "discovery_dial_result";
}
pub mod commit_state_trace {
pub const EVENT: &str = "event";
pub const SOURCE: &str = "source";
pub const HEIGHT: &str = "height";
pub const HASH: &str = "hash";
pub const RANGE_START: &str = "range_start";
pub const RANGE_COUNT: &str = "range_count";
pub const TREE_AUX_ROOTS_LEN: &str = "tree_aux_roots_len";
pub const RESULT: &str = "result";
pub const ERROR_VARIANT: &str = "error_variant";
pub const ERROR_DEBUG: &str = "error_debug";
pub const REASON: &str = "reason";
pub const APPLY_TOKEN: &str = "apply_token";
pub const APPLY_CLASS: &str = "apply_class";
pub const FINALIZED_HEIGHT: &str = "finalized_height";
pub const VERIFIED_BLOCK_TIP: &str = "verified_block_tip";
pub const VERIFIED_BLOCK_HASH: &str = "verified_block_hash";
pub const BEST_HEADER_TIP: &str = "best_header_tip";
pub const ELAPSED_MS: &str = "elapsed_ms";
pub const COMMIT_STALL_REASON: &str = "commit_stall_reason";
pub const COMMITTED_MARKER: &str = "committed_marker";
pub const FIRED_HIGH_WATER: &str = "fired_high_water";
pub const COMMITS_IN_FLIGHT: &str = "commits_in_flight";
pub const PEER: &str = "peer";
pub const QUEUE_LEN: &str = "queue_len";
pub const IN_FLIGHT_COUNT: &str = "in_flight_count";
pub const ACTION: &str = "action";
pub const ACTION_RECEIVED: &str = "action_received";
pub const STATE_READ_START: &str = "state_read_start";
pub const STATE_READ_SUCCESS: &str = "state_read_success";
pub const STATE_READ_ERROR: &str = "state_read_error";
pub const STATE_READ_TIMEOUT: &str = "state_read_timeout";
pub const BLOCK_SUBMIT_QUEUED: &str = "block_submit_queued";
pub const COMMIT_START: &str = "commit_start";
pub const COMMIT_STALLED: &str = "commit_stalled";
pub const COMMIT_STALL_CONTIGUOUS_HEAD: &str = "contiguous_head";
pub const COMMIT_STALL_BEHIND_PREFIX: &str = "behind_committed_prefix";
pub const COMMIT_FINISH: &str = "commit_finish";
pub const REACTOR_EVENT_SENT: &str = "reactor_event_sent";
}
#[derive(Clone, Debug)]
pub struct ZakuraTrace {
emitter: JsonlEventEmitter,
}
impl ZakuraTrace {
pub fn noop() -> Self {
Self::new(JsonlTracer::noop(), zakura_jsonl_trace::node_id())
}
pub fn new(tracer: JsonlTracer, node: impl Into<Arc<str>>) -> Self {
Self {
emitter: JsonlEventEmitter::new(tracer, node),
}
}
pub fn tracer(&self) -> &JsonlTracer {
self.emitter.tracer()
}
pub fn is_enabled(&self) -> bool {
self.emitter.is_enabled()
}
pub fn emit(&self, table: ZakuraTraceTable, event: ZakuraTraceEvent<'_>) {
self.emit_with(table, |row| event.insert_into(row));
}
pub fn emit_event<E>(&self, build: impl FnOnce() -> E)
where
E: JsonlTraceEvent,
{
self.emitter.emit_event(build);
}
pub fn emit_with(&self, table: ZakuraTraceTable, build: impl FnOnce(&mut Map<String, Value>)) {
self.emitter.emit_with(table, build);
}
}
impl Default for ZakuraTrace {
fn default() -> Self {
Self::noop()
}
}
#[derive(Clone, Debug)]
pub struct ZakuraTraceEvent<'a> {
event: &'static str,
conn: Option<u64>,
stream: Option<u64>,
payload_len: Option<u64>,
frame_len: Option<u64>,
max_frame_bytes: Option<u64>,
peer: Option<&'a str>,
role: Option<&'static str>,
phase: Option<&'static str>,
reason: Option<&'static str>,
selected_protocol: Option<u16>,
direction: Option<&'static str>,
stream_kind: Option<&'static str>,
network: Option<&'static str>,
}
impl<'a> ZakuraTraceEvent<'a> {
pub fn new(event: &'static str) -> Self {
Self {
event,
conn: None,
stream: None,
payload_len: None,
frame_len: None,
max_frame_bytes: None,
peer: None,
role: None,
phase: None,
reason: None,
selected_protocol: None,
direction: None,
stream_kind: None,
network: None,
}
}
pub fn conn(mut self, conn: u64) -> Self {
self.conn = Some(conn);
self
}
pub fn stream(mut self, stream: u64) -> Self {
self.stream = Some(stream);
self
}
pub fn payload_len(mut self, payload_len: u64) -> Self {
self.payload_len = Some(payload_len);
self
}
pub fn frame_len(mut self, frame_len: u64) -> Self {
self.frame_len = Some(frame_len);
self
}
pub fn max_frame_bytes(mut self, max_frame_bytes: u64) -> Self {
self.max_frame_bytes = Some(max_frame_bytes);
self
}
pub fn peer(mut self, peer: &'a str) -> Self {
self.peer = Some(peer);
self
}
pub fn maybe_peer(mut self, peer: Option<&'a str>) -> Self {
self.peer = peer;
self
}
pub fn role(mut self, role: &'static str) -> Self {
self.role = Some(role);
self
}
pub fn phase(mut self, phase: &'static str) -> Self {
self.phase = Some(phase);
self
}
pub fn reason(mut self, reason: &'static str) -> Self {
self.reason = Some(reason);
self
}
pub fn selected_protocol(mut self, selected_protocol: u16) -> Self {
self.selected_protocol = Some(selected_protocol);
self
}
pub fn direction(mut self, direction: &'static str) -> Self {
self.direction = Some(direction);
self
}
pub fn stream_kind(mut self, stream_kind: &'static str) -> Self {
self.stream_kind = Some(stream_kind);
self
}
pub fn network(mut self, network: &'static str) -> Self {
self.network = Some(network);
self
}
fn insert_into(self, row: &mut Map<String, Value>) {
row.insert("event".to_string(), Value::String(self.event.to_string()));
insert_optional_u64(row, "conn", self.conn);
insert_optional_u64(row, "stream", self.stream);
insert_optional_u64(row, "payload_len", self.payload_len);
insert_optional_u64(row, "frame_len", self.frame_len);
insert_optional_u64(row, "max_frame_bytes", self.max_frame_bytes);
insert_optional_str(row, "peer", self.peer);
insert_optional_str(row, "role", self.role);
insert_optional_str(row, "phase", self.phase);
insert_optional_str(row, "reason", self.reason);
insert_optional_u64(
row,
"selected_protocol",
self.selected_protocol.map(u64::from),
);
insert_optional_str(row, "direction", self.direction);
insert_optional_str(row, "stream_kind", self.stream_kind);
insert_optional_str(row, "network", self.network);
}
}
pub fn peer_label(peer_id: &ZakuraPeerId) -> String {
let hash = Blake2bParams::new()
.hash_length(8)
.personal(b"zakura-peer-lbl")
.hash(peer_id.as_bytes());
format!("peer:{}", hex::encode(hash.as_bytes()))
}
pub fn reject_reason_label(reason: ZakuraRejectReason) -> &'static str {
match reason {
ZakuraRejectReason::UnsupportedPreludeVersion => "unsupported_prelude_version",
ZakuraRejectReason::IncompatibleZakuraProtocol => "incompatible_zakura_protocol",
ZakuraRejectReason::WrongNetwork => "wrong_network",
ZakuraRejectReason::WrongChain => "wrong_chain",
ZakuraRejectReason::MissingRequiredCapability => "missing_required_capability",
ZakuraRejectReason::ResourceLimit => "resource_limit",
ZakuraRejectReason::AlreadyConnected => "already_connected",
ZakuraRejectReason::TemporaryUnavailable => "temporary_unavailable",
}
}
pub(crate) fn ordered_send_error_label(
error: &crate::zakura::transport::OrderedSendError,
) -> &'static str {
match error {
crate::zakura::transport::OrderedSendError::Full => "full",
crate::zakura::transport::OrderedSendError::Closed => "closed",
crate::zakura::transport::OrderedSendError::Encode(_) => "encode",
}
}
fn insert_optional_str(row: &mut Map<String, Value>, key: &'static str, value: Option<&str>) {
row.insert(
key.to_string(),
value.map_or(Value::Null, |value| Value::String(value.to_string())),
);
}
fn insert_optional_u64(row: &mut Map<String, Value>, key: &'static str, value: Option<u64>) {
row.insert(
key.to_string(),
value.map_or(Value::Null, |value| Value::Number(Number::from(value))),
);
}
#[cfg(test)]
mod tests {
use std::sync::{
atomic::{AtomicBool, Ordering},
Arc,
};
use tokio::sync::mpsc;
use super::*;
#[test]
fn noop_trace_does_not_build_rows() {
let trace = ZakuraTrace::noop();
let called = Arc::new(AtomicBool::new(false));
let called_in_emit = called.clone();
trace.emit_with(CONN_TABLE, |_| {
called_in_emit.store(true, Ordering::SeqCst);
});
assert!(!called.load(Ordering::SeqCst));
}
#[test]
fn full_queue_does_not_build_rows() {
let (tx, _rx) = mpsc::channel(1);
let tracer = JsonlTracer::new(tx);
let trace = ZakuraTrace::new(tracer, "node-full");
trace.emit(CONN_TABLE, ZakuraTraceEvent::new("conn.fill"));
let called = Arc::new(AtomicBool::new(false));
let called_in_emit = called.clone();
trace.emit_with(CONN_TABLE, |_| {
called_in_emit.store(true, Ordering::SeqCst);
});
assert!(
!called.load(Ordering::SeqCst),
"build closure must not run when the queue is full"
);
}
#[test]
fn closed_queue_does_not_build_rows() {
let (tx, rx) = mpsc::channel(1);
let tracer = JsonlTracer::new(tx);
let trace = ZakuraTrace::new(tracer, "node-closed");
drop(rx);
let called = Arc::new(AtomicBool::new(false));
let called_in_emit = called.clone();
trace.emit_with(CONN_TABLE, |_| {
called_in_emit.store(true, Ordering::SeqCst);
});
assert!(
!called.load(Ordering::SeqCst),
"build closure must not run when the queue is closed"
);
assert!(
!trace.is_enabled(),
"trace must report disabled once the receiver is dropped"
);
}
}