1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
//! This crate provides a lock-free Pub/Sub event-bus based on the Disruptor pattern from LMAX. //! //! Both sync and async APIs are available. //! //! # Why? //! //! Event-buses ease the development burden of concurrent programs by enabling concurrent //! application subroutines to interact and affect other subroutines through events. Of course, //! a poor implementation can become a serious bottleneck depending on the application's needs. //! //! Eventador supports the Rust model of *Choose Your Guarantees ™* by presenting //! configuration options for how to handle event publishing when consumers are lagging. //! Providing this configurable interface is currently a work in progress. //! //! # Examples //! //! Please use the provided [examples](#) for a more thorough approach on how to use this crate. //! //! # Design Considerations //! //! ## Ring Buffer //! //! Like Eventador, most event-bus implementations use some form of ring buffer for the underlying //! data structure to store published events. As such, an Eventador instance cannot indefinitely //! grow to accommodate events, unlike a [`Vec`]. In the strictest model (and Eventador's default //! approach), new events must overwrite the oldest event that has already been read by all its //! subscribers. In other words, publishers cannot publish an event to the ring buffer until all //! subscribers for the next overwrite-able event have consumed it. This model favors the //! subscribers so that no event is lost or overwritten without first being handled by every //! concerned party. //! //! Other implementations, like [bus-queue](https://github.com/filipdulic/bus-queue), solve this //! problem by ignoring lagging subscribers, and treating publishers as first-class operators. This //! is the opposite extreme to Eventador's default. //! //! Ultimately, there should not have to be a compromise between what a user wants to prioritize. //! How an event-bus handles the lagging-consumer problem should be left to the user to decide //! through configuration. //! //! ## LMAX Disruptor //! //! The LMAX Disruptor serves as a basis for a lot of event-bus implementations, though the //! contemporary architecture of the Disruptor looks very different from the one presented in the //! outdated LMAX white-paper. Eventador draws from the principles of the current Disruptor //! architecture, but the similarities stop there. //! //! A sequencer atomically assigns an event to an index in the ring buffer on publishing of an //! event. //! //! Subscribers internally have their own sequencer to determine their last read event in the ring //! buffer. On receiving a subscribed message, the sequencer is atomically updated to reflect that //! it can now receive the next event. //! //! ## Lock-free //! //! Eventador has the potential to be a high-contention (aka bottlenecking) structure to a given //! concurrent program, so the implementation needs to handle contention as effectively as possible. //! Atomic CAS operations are generally faster than locking, and is the preferred approach to handle //! contention. //! //! ## TypeId //! This crate relies on the use of `TypeId` to determine what type an event is, and what types of //! events a subscriber subscribes to. //! mod eventador; mod event; mod futures; mod ring_buffer; mod sequence; mod subscriber; pub use crate::futures::{AsyncPublisher, AsyncSubscriber}; pub use eventador::Eventador; pub use event::EventRead; pub use subscriber::Subscriber; pub use ::futures::{SinkExt, StreamExt};