use std::sync::{Arc, Mutex, MutexGuard};
use futures::task::AtomicWaker;
use ruststream::testing::Coordinator;
use super::Bus;
use super::log::Plan;
use crate::error::KinesisError;
use crate::message::KinesisPosition;
pub(crate) const IN_PROCESS_SHARD: &str = "shardId-000000000000";
const SEQUENCE_PREFIX: &str = "49";
pub(crate) fn sequence_of(index: usize) -> String {
format!("{SEQUENCE_PREFIX}{index:054}")
}
pub(crate) fn index_of(sequence: &str) -> Option<usize> {
let digits = sequence.strip_prefix(SEQUENCE_PREFIX)?;
if digits.len() != 54 {
return None;
}
digits.parse().ok()
}
#[derive(Default)]
pub(crate) struct SeekControl {
pending: Mutex<Option<Plan>>,
pub(crate) waker: AtomicWaker,
}
impl SeekControl {
fn lock(&self) -> MutexGuard<'_, Option<Plan>> {
self.pending
.lock()
.expect("kinesis in-process seek mutex poisoned")
}
pub(crate) fn take_pending(&self) -> Option<Plan> {
self.lock().take()
}
fn install_seek(&self, plan: Plan, coordinator: Option<&Coordinator>) {
count(coordinator, plan.count);
let displaced = self.lock().replace(plan);
release(
coordinator,
displaced.map_or(0, |displaced| displaced.count),
);
self.waker.wake();
}
}
fn count(coordinator: Option<&Coordinator>, deliveries: usize) {
if let Some(coordinator) = coordinator {
for _ in 0..deliveries {
coordinator.enqueued();
}
}
}
fn release(coordinator: Option<&Coordinator>, deliveries: usize) {
if let Some(coordinator) = coordinator {
for _ in 0..deliveries {
coordinator.consumed();
}
}
}
impl std::fmt::Debug for SeekControl {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("SeekControl").finish_non_exhaustive()
}
}
#[derive(Clone)]
pub(crate) struct LogSeeker {
bus: Arc<Bus>,
stream: Arc<str>,
control: Arc<SeekControl>,
}
impl std::fmt::Debug for LogSeeker {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("LogSeeker")
.field("stream", &self.stream)
.finish_non_exhaustive()
}
}
impl LogSeeker {
pub(crate) const fn new(bus: Arc<Bus>, stream: Arc<str>, control: Arc<SeekControl>) -> Self {
Self {
bus,
stream,
control,
}
}
pub(crate) fn control(&self) -> &SeekControl {
&self.control
}
pub(crate) fn seek(&self, to: &KinesisPosition) -> Result<(), KinesisError> {
self.bus.ensure_open()?;
let plan = self.bus.log().plan(&self.stream, to)?;
self.control.install_seek(plan, self.bus.coordinator());
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_sequence_number_round_trips_to_its_index() {
let sequence = sequence_of(42);
assert_eq!(sequence.len(), 56);
assert_eq!(index_of(&sequence), Some(42));
assert_eq!(index_of("42"), None);
assert_eq!(index_of("not-a-sequence-number"), None);
}
#[test]
fn sequence_numbers_sort_in_log_order() {
assert!(sequence_of(9) < sequence_of(10));
}
}