Use ring buffer to store breadcrumbs #1286
Merged
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.
Goal
Breadcrumbs are stored in a
ConcurrentLinkedQueue
and then trimmed as they are added. This was identified as an area where performance could potentially be improved as over an application's lifecycle thousands of breadcrumbs might be added to Bugsnag.Changeset
BreadcrumbState
has been altered to use a ring buffer instead of aConcurrentLinkedQueue
. Every time a breadcrumb is added an index is incremented by one, and the breadcrumb is stored in a backing array. If the number of breadcrumbs logged is greater thanconfig.maxBreadcrumbs
then the implementation wraps around to zero and overwrites the oldest breadcrumb added.When an error is captured breadcrumbs must be added to an
Event
in the order in which they were captured. Previously this was a case of transforming aQueue
into aList
but using a ring buffer means that the breadcrumbs can be out of order. The solution chosen here is to create two sublists based on the position of the most recent breadcrumb and then combine them together.Performance improvements
Adding 1000 breadcrumbs to the store took ~9ms with these changes, and ~56ms without, a difference of ~45ms.
The
BreadcrumbState#copy()
implementation has added on ~0.5ms to theBugsnag.notify()
call.Tradeoffs of this changeset
There are a couple of tradeoffs made with this changeset:
BreadcrumbState#copy()
does not sort elements or use synchronisation, so in rare cases this could lead to some breadcrumbs being ordered incorrectly. Sorting the collection was profiled as being expensive, but this is something that could potentially be added in if needed.Bugsnag.notify()
, by the equivalent of adding ~11 breadcrumbs. This feels like a reasonable trade off as breadcrumbs should typically be added much more frequently thanBugsnag.notify()
is called.Testing
Updated existing test coverage to match new method signatures, and added additional unit test cases for the ring buffer wrapping round. Additionally added a benchmark test for copying breadcrumbs (which happens whenever an event is generated).