ereports: add declare_ereporter! macro#2427
Merged
Conversation
This reverts commit 6717b05. I forgot that we actaully do need this.
mkeeter
reviewed
Mar 11, 2026
mkeeter
reviewed
Mar 11, 2026
mkeeter
reviewed
Mar 11, 2026
mkeeter
reviewed
Mar 11, 2026
| #[macro_export] | ||
| macro_rules! declare_ereporter { | ||
| ($v:vis struct $Ereporter:ident<$Trait:ident> { | ||
| $($ClassName:ident($EreportTy:ty $(,)?)),+ $(,)? |
Collaborator
There was a problem hiding this comment.
boy I sure do love Rust macros
mkeeter
reviewed
Mar 11, 2026
mkeeter
reviewed
Mar 11, 2026
Co-authored-by: Matt Keeter <matt.j.keeter@gmail.com>
mkeeter
approved these changes
Mar 11, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Depends on #2399.
While working on #2399, I noticed that there was an emerging boilerplate pattern for tasks which report ereports, where I had declared a struct that bundles together a buffer of a size calculated using
microcbor::max_cbor_len_for!with thePackratAPI client and some methods for reporting ereports. For example, in the Cosmo sequencer, I wrote this:hubris/drv/cosmo-seq-server/src/main.rs
Lines 1237 to 1263 in 9e18d0b
And then an identical copy of the same code in the Gimlet sequencer:
hubris/drv/gimlet-seq-server/src/main.rs
Lines 1620 to 1647 in 9e18d0b
I started to wonder whether it would be possible to factor out this boilerplate. While we could easily just have a
and some methods in the
ereportscrate, and let the user provide the buffer size from a separate invocation of themicrocbormacro, it occurred to me that there was a way to ... feed ... two birds with one...loaf of bread?1 and also solve some of the long running issues with the present ereport-recording APIs.In particular, a long-standing thorn in my side is the fact that, while we presently have a way to calculate the required buffer length for a list of ereports, we don't currently have anything that actually stops you from attempting to to actually use that buffer to encode an ereport that wasn't involved in the length calculation. I had hoped that there would be a generic way to do this using const generics, but unfortunately, Rust's const generics remain only sort of half finished, as I discussed in detail in #2246 (comment). So, this means that it's possible to add a new ereport type, forget to add it to the list of ereports used for the length calculation, and end up with an ereport that never fits in the buffer without really noticing. So that's not great.
Another less important but similarly bothersome thing is that I have generally tried to add ringbuf entries to record when an ereport is submitted, and more importantly, when it isn't, either because the ereport was too big for the encoding buffer due to issues like the one I described above, or due to the
packratereport aggregation buffer being full. Ideally, these ringbuf entries/counters would also record which ereport class was or was not reported, but doing this requires even more boilerplate which must be specific to the individual task that records ereports, as the list of ereports depends on the task.So, I've come up with a way to feed all of the aforementioned birds, by introducing a new
declare_ereporter!macro in theereportscrate. This macro is invoked with the name of a struct, the name of a trait, and a list of ereports, and generates an ereporter struct, a trait implemented by all of the ereports in the provided list, and a method on the ereporter struct to record an ereport where the ereport type implements the generated trait. This way, we can have a way to ensure that only ereports whose lengths were used in the encoding buffer size calculation are reported using that buffer. The macro also generates ringbuf entries/counters for each ereport that's recorded using the generated ereporter type. For more details on how the new thing is used, see the RustDoc I added for the macro.Footnotes
Attempting to use non-violent language here... :) ↩