tidaldb/tidal/src/testing/cluster_transport.rs
jx12n 3bcfb3c576 feat: Bazel build, crate docs/ai-lookup, docker images, and engine hardening
- Add BUILD.bazel across tidal, tidal-net, tidal-server, tidalctl for bzlmod build
- Add tidal/ crate docs (README, CHANGELOG, CONTRIBUTING, AGENTS, CLAUDE, API, ARCHITECTURE) and ai-lookup reference
- Add docker standalone/cluster/deploy images, compose, and prometheus config
- Harden WAL (batch format, writer, dedup, diagnostics), text syncer/collectors, and vector registry
- Expand tidalctl CLI and tests; restructure WAL/visibility integration test suites
- Refine tidal-net transport/client/server and tidal-server cluster/scatter-gather
2026-06-07 18:29:38 -06:00

108 lines
3.9 KiB
Rust

//! Internal transport types used by [`super::cluster::SimulatedCluster`].
use crate::{
db::TidalDb,
replication::{
WalSegmentId,
shard::{RegionId, ShardId},
transport::{Transport, TransportError, WalSegmentPayload},
},
};
// ── ChannelTransport: bidirectional crossbeam transport ─────────────────
/// Bidirectional crossbeam-channel transport for in-process cluster simulation.
///
/// Wraps both sender and receiver so `SimulatedCluster` can call
/// `send_segment` to ship WAL payloads and `spawn_receiver` can call
/// `recv_segment` to apply them.
pub(super) struct ChannelTransport {
pub(super) local_shard: ShardId,
pub(super) tx: crossbeam::channel::Sender<WalSegmentPayload>,
pub(super) rx: crossbeam::channel::Receiver<WalSegmentPayload>,
}
impl Transport for ChannelTransport {
fn send_segment(&self, _to: ShardId, payload: WalSegmentPayload) -> Result<(), TransportError> {
self.tx.send(payload).map_err(|_| TransportError::Closed)
}
fn recv_segment(&self) -> Option<WalSegmentPayload> {
self.rx.recv().ok()
}
fn local_shard(&self) -> ShardId {
self.local_shard
}
}
/// Receive-only crossbeam transport handed to the receiver thread.
///
/// Crucially this holds **only** the receiver end. The matching `Sender` lives
/// in [`super::cluster::SimulatedCluster`]; dropping the cluster drops every
/// sender, which closes the channel and makes [`recv_segment`] return `None`,
/// letting the receiver thread exit so its `SegmentReceiverHandle` can be
/// joined. (A bidirectional [`ChannelTransport`] cannot be joined on shutdown:
/// the receiver thread's own `Arc` keeps a `Sender` alive, so `recv` would
/// block forever.)
///
/// [`recv_segment`]: Transport::recv_segment
pub(super) struct RecvOnlyChannelTransport {
pub(super) local_shard: ShardId,
pub(super) rx: crossbeam::channel::Receiver<WalSegmentPayload>,
}
impl Transport for RecvOnlyChannelTransport {
fn send_segment(
&self,
_to: ShardId,
_payload: WalSegmentPayload,
) -> Result<(), TransportError> {
// The receiver thread never ships; only the cluster's send side does.
Err(TransportError::Closed)
}
fn recv_segment(&self) -> Option<WalSegmentPayload> {
self.rx.recv().ok()
}
fn local_shard(&self) -> ShardId {
self.local_shard
}
}
// ── Batch log entry ──────────────────────────────────────────────────────
pub(super) struct BatchEntry {
/// Which leader shard produced this batch.
pub(super) source_shard: ShardId,
/// 1-indexed sequence number scoped to `source_shard`.
pub(super) seqno: u64,
/// Encoded WAL batch bytes (from [`crate::wal::format::batch::encode_batch`]).
pub(super) bytes: Vec<u8>,
}
// ── Re-delivery helper ────────────────────────────────────────────────────
/// Re-deliver all batch-log entries not yet applied to `db`, sending through
/// the given transport.
///
/// Called by `SimulatedCluster::heal_region` for immediate recovery after a
/// partition is healed, and by `await_convergence` during the polling loop.
pub(super) fn redeliver_missed(transport: &dyn Transport, db: &TidalDb, log: &[BatchEntry]) {
for entry in log {
let applied = db
.replication_state()
.applied_seqno(entry.source_shard)
.unwrap_or(0);
if entry.seqno > applied {
let payload = WalSegmentPayload {
id: WalSegmentId::new(RegionId::SINGLE, entry.source_shard, entry.seqno),
bytes: entry.bytes.clone(),
event_count: 1,
};
let _ = transport.send_segment(entry.source_shard, payload);
}
}
}