1use std::collections::{BTreeMap, VecDeque};
18use std::fmt;
19use std::future::Future;
20use std::num::NonZeroUsize;
21use std::pin::Pin;
22use std::sync::Arc;
23use std::task::{Context, Poll};
24use futures_core::Stream;
26use tokio::sync::{mpsc, oneshot, watch};
27use tracing::Instrument;
28use super::TrackedState;
30use super::entry::{At, Entry, Link, Position, PrincipalId};
31use super::envelope::{Envelope, Recorded};
32use super::transaction::dispatch::Dispatch;
33use super::transaction::{DecodeError, Inverting};
34use crate::Application;
35use crate::error::Error;
36use crate::supervision::executor::InContext;
37
38type Predicate<A> = Arc<dyn Fn(&<A as Application>::Transaction) -> bool + Send + Sync>;
45
46type Step<A, X> = Pin<Box<dyn Future<Output = (Parts<A>, Result<Vec<Entry<X>>, Error>)> + Send>>;
47
48pub struct Origin<S> {
51 pub value: S,
52 pub position: Position,
53}
54
55pub(crate) struct Reads<A: Application> {
56 sender: mpsc::Sender<ReadRequest<A>>,
57}
58
59pub struct Focus<A: Application> {
60 predicate: Option<Predicate<A>>,
61}
62
63#[derive(Clone, Copy, Debug, PartialEq, Eq)]
64pub struct Reversals {
65 pub undoable: bool,
66 pub redoable: bool,
67}
68
69pub(crate) struct Undoable<A: Application> {
70 pub(crate) principal: PrincipalId,
71 pub(crate) forward: Arc<A::Transaction>,
72 pub(crate) inverse: Option<Arc<A::Transaction>>,
73 pub(crate) undone: Option<Position>,
74}
75
76pub(crate) struct Undos<A: Application> {
77 pub(crate) entries: BTreeMap<Position, Undoable<A>>,
78}
79
80pub(crate) struct Tail<A: Application> {
81 pub(crate) entries: VecDeque<Arc<Entry<Recorded<A>>>>,
82 capacity: usize,
83 floor: Position,
84}
85
86pub(crate) struct SharedPage<A: Application> {
87 pub(crate) entries: Vec<Arc<Entry<Recorded<A>>>>,
88 pub(crate) reaches: Position,
89}
90
91struct Parts<A: Application> {
92 trace: crate::supervision::executor::SpanContext,
93 tracked: Arc<TrackedState<A>>,
94 frontier: Position,
95 doorbell: watch::Receiver<Position>,
96}
97
98struct Watching<A: Application, X> {
99 parts: Option<Parts<A>>,
100 step: Option<Step<A, X>>,
101 pending: VecDeque<Entry<X>>,
102 done: bool,
103}
104
105pub(crate) enum ReadRequest<A: Application> {
108 Entries {
109 trace: crate::supervision::executor::SpanContext,
110 after: Position,
111 through: Position,
112 reply: oneshot::Sender<Result<Vec<Entry<Recorded<A>>>, Error>>,
113 },
114 State {
115 trace: crate::supervision::executor::SpanContext,
116 origin: Origin<A::State>,
117 at: Position,
118 reply: oneshot::Sender<Result<At<A::State>, Error>>,
119 },
120}
121
122pub(crate) trait ReplayPayload<A: Application>: Sized {
125 fn prepared(&self) -> Option<crate::state::Prepared> {
126 None
127 }
128
129 fn forward(&self) -> Option<Arc<A::Transaction>>;
130 fn restore(
131 envelope: Envelope<Self>, catalog: Option<&Dispatch<A, Self>>, state: &mut A::State,
132 ) -> Result<Option<Recorded<A>>, DecodeError>;
133}
134
135impl<S> Origin<S> {
138 pub fn initial(value: S) -> Self {
139 Self { value, position: Position::ORIGIN }
140 }
141
142 pub fn at(value: S, position: Position) -> Self {
143 Self { value, position }
144 }
145}
146
147impl<S> From<S> for Origin<S> {
148 fn from(value: S) -> Self {
149 Self::initial(value)
150 }
151}
152
153impl<S> From<At<S>> for Origin<S> {
154 fn from(origin: At<S>) -> Self {
155 Self::at(origin.value, origin.position)
156 }
157}
158
159impl<A: Application> Reads<A> {
160 pub(crate) fn channel(capacity: usize) -> (Self, mpsc::Receiver<ReadRequest<A>>) {
161 let (sender, receiver) = mpsc::channel(capacity.max(1));
162
163 (Self { sender }, receiver)
164 }
165
166 #[tracing::instrument(target = "harmos::state", name = "state.entries", level = "debug", skip_all)]
167 pub(crate) async fn entries(&self, after: Position, through: Position) -> Result<Vec<Entry<Recorded<A>>>, Error> {
168 let (reply, answer) = oneshot::channel();
169 self.sender
170 .send(ReadRequest::Entries {
171 after,
172 through,
173 reply,
174 trace: crate::supervision::executor::SpanContext::new(tracing::Span::current(), tracing::Span::none()),
175 })
176 .await
177 .map_err(|_| Error::BeyondTail)?;
178
179 answer.await.map_err(|_| Error::Unavailable)?
180 }
181
182 #[tracing::instrument(target = "harmos::state", name = "state.state", level = "debug", skip_all)]
183 pub(crate) async fn state(&self, origin: Origin<A::State>, at: Position) -> Result<At<A::State>, Error> {
184 if origin.position > at {
185 return Err(Error::OriginAhead);
186 }
187 if origin.position == at {
188 return Ok(At { value: origin.value, position: at });
189 }
190
191 let (reply, answer) = oneshot::channel();
192 self.sender
193 .send(ReadRequest::State {
194 origin,
195 at,
196 reply,
197 trace: crate::supervision::executor::SpanContext::new(tracing::Span::current(), tracing::Span::none()),
198 })
199 .await
200 .map_err(|_| Error::BeyondTail)?;
201
202 answer.await.map_err(|_| Error::Unavailable)?
203 }
204}
205
206impl<A: Application> Clone for Reads<A> {
207 fn clone(&self) -> Self {
208 Self { sender: self.sender.clone() }
209 }
210}
211
212impl<A: Application> Focus<A> {
213 pub fn all() -> Self {
214 Self { predicate: None }
215 }
216
217 pub fn matching(predicate: impl Fn(&A::Transaction) -> bool + Send + Sync + 'static) -> Self {
218 Self { predicate: Some(Arc::new(predicate)) }
219 }
220
221 fn admits(&self, transaction: &A::Transaction) -> bool {
222 self.predicate.as_ref().is_none_or(|predicate| predicate(transaction))
223 }
224}
225
226impl<A: Application> Clone for Focus<A> {
227 fn clone(&self) -> Self {
228 Self { predicate: self.predicate.clone() }
229 }
230}
231
232impl<A: Application> fmt::Debug for Focus<A> {
233 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
234 formatter.write_str(match self.predicate {
235 Some(_) => "Focus::matching(..)",
236 None => "Focus::all()",
237 })
238 }
239}
240
241impl<A: Application> Undos<A> {
242 pub(crate) fn record(
243 &mut self, position: Position, principal: Option<&PrincipalId>, link: Option<Link>, forward: &Arc<A::Transaction>,
244 inverse: Option<A::Transaction>,
245 ) {
246 match link {
247 Some(Link::UndoOf(original)) => {
248 if let Some(entry) = self.entries.get_mut(&original) {
249 entry.undone = Some(position);
250 }
251 }
252 Some(Link::RedoOf(original)) => {
253 if let Some(entry) = self.entries.get_mut(&original) {
254 entry.undone = None;
255 entry.inverse = inverse.map(Arc::new);
259 }
260 }
261 None => {
262 let Some(principal) = principal else {
263 return;
264 };
265 self.entries.retain(|_, entry| entry.principal != *principal || entry.undone.is_none());
271 self.entries.insert(
272 position,
273 Undoable { principal: principal.clone(), forward: Arc::clone(forward), inverse: inverse.map(Arc::new), undone: None },
274 );
275 }
276 }
277 }
278
279 pub(crate) fn undoable(&self, principal: &PrincipalId, focus: &Focus<A>) -> Option<(Position, Arc<A::Transaction>)> {
280 for (position, entry) in self.entries.iter().rev() {
281 if entry.principal != *principal {
282 continue;
283 }
284 if entry.undone.is_some() || !focus.admits(&entry.forward) {
285 continue;
286 }
287 let inverse = entry.inverse.as_ref()?;
288 return Some((*position, Arc::clone(inverse)));
289 }
290 None
291 }
292
293 pub(crate) fn redoable(&self, principal: &PrincipalId, focus: &Focus<A>) -> Option<(Position, Arc<A::Transaction>)> {
294 self.entries
295 .iter()
296 .filter(|(_, entry)| entry.principal == *principal && focus.admits(&entry.forward))
297 .filter_map(|(position, entry)| entry.undone.map(|at| (at, *position, Arc::clone(&entry.forward))))
298 .max_by_key(|(at, _position, _forward)| *at)
299 .map(|(_at, position, forward)| (position, forward))
300 }
301
302 pub(crate) fn reversals(&self, principal: &PrincipalId, focus: &Focus<A>) -> Reversals {
303 Reversals { undoable: self.undoable(principal, focus).is_some(), redoable: self.redoable(principal, focus).is_some() }
304 }
305}
306
307impl<A: Application> Default for Undos<A> {
308 fn default() -> Self {
309 Self { entries: BTreeMap::new() }
310 }
311}
312
313impl<A: Application> Tail<A> {
314 pub(crate) fn new(origin: Position, capacity: usize) -> Self {
315 Self { entries: VecDeque::new(), capacity: capacity.max(1), floor: origin }
316 }
317
318 pub(crate) fn recovered(frontier: Position, capacity: usize, entries: Vec<Arc<Entry<Recorded<A>>>>) -> Self {
319 if entries.is_empty() {
320 return Self::new(frontier, capacity);
321 }
322 let floor = entries.first().map_or(frontier, |entry| Position(entry.position.0.saturating_sub(1)));
323
324 Self { entries: entries.into(), capacity: capacity.max(1), floor }
325 }
326
327 pub(crate) fn push_group(&mut self, entries: Vec<Entry<Recorded<A>>>) -> Vec<Arc<Entry<Recorded<A>>>> {
328 while self.entries.len() + entries.len() > self.capacity && !self.entries.is_empty() {
329 let evicted = self.entries.pop_front().expect("nonempty tail");
330 let frontier = evicted.metadata.commit.map_or(evicted.position, |(_, end)| end);
331 self.floor = frontier;
332 while self.entries.front().is_some_and(|entry| entry.position <= frontier) {
333 self.entries.pop_front();
334 }
335 }
336 let entries: Vec<_> = entries.into_iter().map(Arc::new).collect();
337 self.entries.extend(entries.iter().cloned());
338 entries
339 }
340
341 pub(crate) fn splits_commit(&self, position: Position) -> bool {
342 self.entries.iter().any(|entry| entry.metadata.commit.is_some_and(|(first, end)| first <= position && position < end))
343 }
344
345 pub(crate) fn since(&self, after: Position) -> Result<Vec<Arc<Entry<Recorded<A>>>>, Error> {
346 if after < self.floor {
347 return Err(Error::BeyondTail);
348 }
349
350 Ok(self.entries.iter().filter(|entry| entry.position > after).map(Arc::clone).collect())
351 }
352
353 pub(crate) fn scan<X: Clone + 'static>(&self, after: Position) -> Result<(Vec<Entry<X>>, Position), Error> {
354 self.scan_through(after, Position(u64::MAX))
355 }
356
357 pub(crate) fn scan_through<X: Clone + 'static>(&self, after: Position, through: Position) -> Result<(Vec<Entry<X>>, Position), Error> {
358 let mut frontier = after;
359 let mut found = Vec::new();
360 loop {
361 let page = self.shared_page(frontier, through, NonZeroUsize::new(1024).unwrap())?;
362 if page.entries.is_empty() {
363 break;
364 }
365 frontier = page.reaches;
366 for entry in page.entries {
367 if let Some(value) = entry.value.select::<X>() {
368 found.push(Entry { prepared: None, position: entry.position, metadata: entry.metadata.clone(), value });
369 }
370 }
371 }
372
373 Ok((found, frontier))
374 }
375
376 #[tracing::instrument(target = "harmos::state", name = "state.shared_page", level = "debug", skip_all)]
377 pub(crate) fn shared_page(&self, after: Position, through: Position, max_rows: NonZeroUsize) -> Result<SharedPage<A>, Error> {
378 if after < self.floor {
379 return Err(Error::BeyondTail);
380 }
381 let start = self.entries.partition_point(|entry| entry.position <= after);
382 let count = max_rows.get().min(self.entries.len() - start);
383 let mut entries = Vec::with_capacity(count);
384 let mut reaches = after;
385 for entry in self.entries.iter().skip(start).take(count) {
386 if entry.position > through {
387 break;
388 }
389 reaches = entry.position;
390 entries.push(Arc::clone(entry));
391 }
392 Ok(SharedPage { entries, reaches })
393 }
394}
395
396impl<A: Application, X> Unpin for Watching<A, X> {}
397
398impl<A, X> Stream for Watching<A, X>
399where
400 A: Application,
401 X: Clone + Send + 'static,
402{
403 type Item = Result<Entry<X>, Error>;
404
405 fn poll_next(self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<Option<Self::Item>> {
406 let watching = self.get_mut();
407
408 if let Some(entry) = watching.pending.pop_front() {
409 return Poll::Ready(Some(Ok(entry)));
410 }
411 if watching.done {
412 return Poll::Ready(None);
413 }
414
415 let mut step = match watching.step.take() {
416 Some(step) => step,
417 None => match watching.parts.take() {
418 Some(parts) => {
419 let dispatcher = parts.trace.dispatcher.clone();
420 let span = tracing::dispatcher::with_default(
421 &dispatcher,
422 || tracing::trace_span!(target: "harmos::state", parent: &parts.trace.span, "state.watch_batch", after = parts.frontier.0),
423 );
424 Box::pin(next(parts).instrument(span).in_context(dispatcher))
425 }
426 None => {
427 watching.done = true;
428 return Poll::Ready(None);
429 }
430 },
431 };
432
433 match step.as_mut().poll(context) {
434 Poll::Pending => {
435 watching.step = Some(step);
436 Poll::Pending
437 }
438 Poll::Ready((parts, Ok(found))) => {
439 watching.parts = Some(parts);
440 watching.pending.extend(found);
441 match watching.pending.pop_front() {
442 Some(entry) => Poll::Ready(Some(Ok(entry))),
443 None => {
444 watching.done = true;
445 Poll::Ready(None)
446 }
447 }
448 }
449 Poll::Ready((parts, Err(refusal))) => {
450 watching.parts = Some(parts);
451 watching.done = true;
452 Poll::Ready(Some(Err(refusal)))
453 }
454 }
455 }
456}
457
458#[tracing::instrument(target = "harmos::state", name = "state.replay", level = "debug", skip_all)]
461pub(crate) fn replay<A, W>(
462 catalog: Option<&Dispatch<A, W>>, recorded: Vec<Envelope<W>>, state: &mut At<A::State>, undos: &mut Undos<A>, inverses: Inverting<A>,
463 tail: usize,
464) -> Result<Vec<Arc<Entry<Recorded<A>>>>, DecodeError>
465where
466 A: Application,
467 W: ReplayPayload<A>,
468{
469 let mut redelivered = VecDeque::new();
470
471 for envelope in recorded {
472 let prepared = envelope.payload.prepared();
473 let position = envelope.position;
474 let metadata = envelope.metadata.clone();
475 let link = metadata.link;
476 let principal = metadata.principal.clone();
477 let forward = envelope.payload.forward();
478 let inverse = forward.as_ref().and_then(|change| inverses(change, &state.value));
479 let recovered = W::restore(envelope, catalog, &mut state.value)?;
480
481 if let Some(forward) = &forward {
482 undos.record(position, principal.as_ref(), link, forward, inverse);
483 }
484 if let Some(value) = recovered
485 && tail != 0
486 {
487 let first = metadata.commit.map_or(position, |(first, _)| first);
489 while redelivered.len() >= tail && redelivered.front().is_some_and(|entry: &Arc<Entry<Recorded<A>>>| entry.position < first) {
490 let end = redelivered.front().map(|entry| entry.metadata.commit.map_or(entry.position, |(_, end)| end)).expect("nonempty");
491 while redelivered.front().is_some_and(|entry| entry.position <= end) {
492 redelivered.pop_front();
493 }
494 }
495 redelivered.push_back(Arc::new(Entry { prepared, position, metadata, value }));
496 }
497 state.position = position;
498 }
499
500 Ok(redelivered.into())
501}
502
503pub(crate) fn watching<A, X>(tracked: Arc<TrackedState<A>>, after: Position) -> impl Stream<Item = Result<Entry<X>, Error>>
504where
505 A: Application,
506 X: Clone + Send + 'static,
507{
508 let doorbell = tracked.applied.subscribe();
509
510 Watching {
511 parts: Some(Parts {
512 tracked,
513 frontier: after,
514 doorbell,
515 trace: crate::supervision::executor::SpanContext::new(tracing::Span::current(), tracing::Span::none()),
516 }),
517 step: None,
518 pending: VecDeque::new(),
519 done: false,
520 }
521}
522
523async fn next<A, X>(mut parts: Parts<A>) -> (Parts<A>, Result<Vec<Entry<X>>, Error>)
526where
527 A: Application,
528 X: Clone + Send + 'static,
529{
530 loop {
531 if parts.tracked.poisoned() {
532 return (parts, Err(Error::Unavailable));
533 }
534
535 let scanned = {
536 let tail = parts.tracked.tail.read().await;
537 tail.scan::<X>(parts.frontier)
538 };
539
540 match scanned {
541 Err(refusal) => return (parts, Err(refusal)),
542 Ok((found, frontier)) => {
543 parts.frontier = frontier;
544 if !found.is_empty() {
545 return (parts, Ok(found));
546 }
547 }
548 }
549
550 if parts.doorbell.changed().await.is_err() {
551 return (parts, Err(Error::Unavailable));
552 }
553 }
554}
555
556