37 void reserve_events(std::size_t count) {
38 if (!events_.empty()) {
39 detail::fail_fast(
"EventStream::reserve_events requires an empty stream");
42 events_.reserve(count);
45 [[nodiscard]]
auto publish(std::uint64_t tick,
const T& value) ->
bool {
46 if (events_.size() >= max_events_) {
48 account_publish(
false);
51 events_.push_back(event_type{tick, next_sequence_, value});
53 account_publish(
true);
57 [[nodiscard]]
auto publish(std::uint64_t tick, T&& value) ->
bool {
58 if (events_.size() >= max_events_) {
60 account_publish(
false);
63 events_.push_back(event_type{tick, next_sequence_, std::move(value)});
65 account_publish(
true);
69 [[nodiscard]]
auto events()
const noexcept -> std::span<const event_type> {
70 return {events_.data(), events_.size()};
73 [[nodiscard]]
auto size()
const noexcept -> std::size_t {
74 return events_.size();
77 [[nodiscard]]
bool empty()
const noexcept {
return events_.empty(); }
79 [[nodiscard]]
auto rejected_events()
const noexcept -> std::uint64_t {
80 return rejected_events_;
90 void clear() noexcept { retire_batch(
false); }
106 if (!events_.empty()) {
108 "EventStream::set_flow_accounting requires an empty stream");
110 accounting_ = accounting;
120 if (accounting_ ==
nullptr) {
123 accounting_->observe_tick(tick);
124 const auto now = accounting_->last_observed_tick;
125 accounting_->counters.oldest_outstanding_age_ticks =
126 events_.empty() ? 0 : now - oldest_published_tick_;
137 : events_{other.events_},
138 max_events_{other.max_events_},
139 next_sequence_{other.next_sequence_},
140 rejected_events_{other.rejected_events_} {}
141 auto operator=(
const EventStream& other) -> EventStream& {
142 if (
this != &other) {
143 if (accounting_ !=
nullptr && !events_.empty()) {
145 "EventStream copy assignment would orphan outstanding "
148 events_ = other.events_;
149 max_events_ = other.max_events_;
150 next_sequence_ = other.next_sequence_;
151 rejected_events_ = other.rejected_events_;
152 oldest_published_tick_ = 0;
153 published_tick_total_ = 0;
154 accounting_ =
nullptr;
158 EventStream(EventStream&& other) noexcept
159 : events_{std::move(other.events_)},
160 max_events_{other.max_events_},
161 next_sequence_{other.next_sequence_},
162 rejected_events_{other.rejected_events_},
163 oldest_published_tick_{other.oldest_published_tick_},
164 published_tick_total_{other.published_tick_total_},
165 accounting_{other.accounting_} {
166 other.accounting_ =
nullptr;
167 other.oldest_published_tick_ = 0;
168 other.published_tick_total_ = 0;
170 auto operator=(EventStream&& other)
noexcept -> EventStream& {
171 if (
this != &other) {
172 if (accounting_ !=
nullptr && !events_.empty()) {
174 "EventStream move assignment would orphan outstanding "
177 events_ = std::move(other.events_);
178 max_events_ = other.max_events_;
179 next_sequence_ = other.next_sequence_;
180 rejected_events_ = other.rejected_events_;
181 oldest_published_tick_ = other.oldest_published_tick_;
182 published_tick_total_ = other.published_tick_total_;
183 accounting_ = other.accounting_;
184 other.accounting_ =
nullptr;
185 other.oldest_published_tick_ = 0;
186 other.published_tick_total_ = 0;
190 ~EventStream() =
default;
193 void retire_batch(
bool consumed)
noexcept {
194 if (accounting_ !=
nullptr) {
195 auto& counters = accounting_->counters;
196 const auto count =
static_cast<std::uint64_t
>(events_.size());
197 (consumed ? counters.completed : counters.dropped_after_admission) +=
204 counters.outstanding_current -=
205 std::min(counters.outstanding_current, count);
206 counters.residence_ticks_accumulated +=
207 accounting_->last_observed_tick * count - published_tick_total_;
208 published_tick_total_ = 0;
209 counters.oldest_outstanding_age_ticks = 0;
214 void account_publish(
bool admitted)
noexcept {
215 if (accounting_ ==
nullptr) {
218 ++accounting_->counters.offered;
220 ++accounting_->counters.rejected;
223 accounting_->record_admitted();
224 if (events_.size() == 1) {
225 oldest_published_tick_ = accounting_->last_observed_tick;
227 published_tick_total_ += accounting_->last_observed_tick;
230 std::vector<event_type> events_;
231 std::size_t max_events_ = 0;
232 std::uint64_t next_sequence_ = 0;
233 std::uint64_t rejected_events_ = 0;
234 std::uint64_t oldest_published_tick_ = 0;
235 std::uint64_t published_tick_total_ = 0;
236 diagnostics::FlowAccounting* accounting_ =
nullptr;