-
Notifications
You must be signed in to change notification settings - Fork 17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add vertex store size limit; cleanup vertex store events #854
base: develop
Are you sure you want to change the base?
Conversation
3ac1d06
to
b8f0707
Compare
b8f0707
to
501a3bf
Compare
Docker tags |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The vertex store size limit feature (i.e. break vertices_loop;
) looks very much ok 👍 (I mean, I was able to understand it, and it seems to work [outside of corner-cases where "really small stuff is added to vertex store without checking size"]).
I can't say I understood 100% of the rest (i.e. the refactor) :(
I left some minor "technical Java" comments.
It does look more structured than before, but I definitely lack the Consensus knowledge to spot any new subtle bugs.
If you have faith in our Consensus regression tests - feel free to merge with my 🟢 .
Otherwise - I recommend waiting for more reviewers.
@@ -233,7 +233,12 @@ public record Sync( | |||
Counter invalidEpochInitialQcSyncStates) {} | |||
|
|||
public record VertexStore( | |||
Gauge size, Counter forks, Counter rebuilds, Counter indirectParents) {} | |||
Gauge size, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
vertexCount
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sounds good 👍
note to self: update this on the dashboard later (if it's used)
vertexStoreConfig.maxSerializedSizeBytes() | ||
>= VertexStoreConfig.MIN_MAX_SERIALIZED_SIZE_BYTES, | ||
"Invalid configuration: bft.vertex_store.max_serialized_size_byte must be at least " | ||
+ VertexStoreConfig.MIN_MAX_SERIALIZED_SIZE_BYTES); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(nitpick) Preconditions
has a built-int ("formatting %s", ...args)
syntax
@@ -350,43 +431,15 @@ private void removeVertexAndPruneInternal(HashCode vertexId, HashCode skip) { | |||
var children = vertexChildren.remove(vertexId); | |||
if (children != null) { | |||
for (HashCode child : children) { | |||
if (!child.equals(skip)) { | |||
removeVertexAndPruneInternal(child, null); | |||
if (!skip.map(child::equals).orElse(false)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (!skip.map(child::equals).orElse(false)) { | |
if (!Optional.of(child).equals(skip)) { |
.flatMap( | ||
removedVertex -> | ||
Optional.ofNullable(vertexChildren.get(removedVertex.vertex().getParentVertexId()))) | ||
.ifPresent(siblings -> siblings.remove(vertexId)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
some lines could be a lot less awkward if this.vertexChildren
was really a Multimap<HashCode, HashCode>
(e.g. = HashMultimap.create()
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Splendid idea! Done.
Quality Gate passedIssues Measures |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generally really nice changes. I like the event fixing, the metrics tweaks, and the use of sealed interfaces for better error propagation.
The code in VertexStoreImpl isn't the prettiest, but then again, the whole class isn't great to start with... I wonder if we should change the size check to a simple check on current (cached) serialized size before we try inserting each vertex; rather than a check of the state-after-inserting-the-vertex? This would reduce the work done on the happy path, and mean we can be happier in not checking the size on the other code paths which just update the highQcs. Anyway, just an idea.
I don't think any of these notes are blockers, but ideally some of the more important ones could do with a small fix before we merge.
* An event emitted when vertex store updates its highQC, which possibly results in some vertices | ||
* being committed. | ||
*/ | ||
public record BFTHighQCUpdate( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We've lost the nice toString
here - perhaps we should override it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Otherwise we could end up with a massive log of the serialized vertex state in hex.
} | ||
} | ||
/** An event emitted after a vertex has been inserted into the vertex store. */ | ||
public record BFTInsertUpdate( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We've lost the nice toString
here - perhaps we should override it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Otherwise we could end up with a massive log of the serialized vertex state in hex.
} | ||
} | ||
/** An even emitted when the vertex store has been rebuilt. */ | ||
public record BFTRebuildUpdate( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We've lost the nice toString
here - perhaps we should override it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Otherwise we could end up with a massive log of the serialized vertex state in hex.
} | ||
|
||
public boolean insertTimeoutCertificate(TimeoutCertificate timeoutCertificate) { | ||
return vertexStore.insertTimeoutCertificate(timeoutCertificate); | ||
final var result = vertexStore.insertTimeoutCertificate(timeoutCertificate); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For code beauty, can we align the insertQc
below with this?
e.g. the code below should be insertQuorumCertificate
, it should match on result, and we can inline dispatchPostQcInsertionEvents
if (hasAnyChildren) { | ||
// TODO: Check to see if qc's match in case there's a fault | ||
return new VertexStore.InsertQcResult.Ignored(); | ||
} | ||
|
||
// proposed vertex doesn't have any children | ||
// Proposed vertex doesn't have any children |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
<sidenote>
Still really want to rename HighQC
=> HighCertificates
to make this less confusing :D</sidenote>
highQcUpdate | ||
.committedVertices() | ||
.ifPresent( | ||
committedVertices -> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just as a minor style thing - this is nested quite deep. Perhaps we should consider either pulling out some methods, or returning early in the not-present case to de-nest things.
@ProcessOnDispatch Set<EventProcessor<BFTInsertUpdate>> processors, | ||
Environment environment, | ||
Metrics metrics) { | ||
@ProcessOnDispatch Set<EventProcessor<BFTInsertUpdate>> processors, Environment environment) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice moving of metrics into the vertex store. I think this makes this simpler to comprehend.
}, | ||
BFTCommittedUpdate.class); | ||
BFTHighQCUpdate.class); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this code is now a duplicate so should be deleted?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yup, good catch
@@ -162,7 +161,8 @@ StateComputerPrepareResult prepare( | |||
List<RawNotarizedTransaction> proposedTransactions, | |||
RoundDetails roundDetails); | |||
|
|||
LedgerProofBundle commit(LedgerExtension ledgerExtension, VertexStoreState vertexStore); | |||
LedgerProofBundle commit( | |||
LedgerExtension ledgerExtension, Option<byte[]> serializedVertexStoreState); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Out of interest, why is this Option<byte[]>
and not Option<WrappedByteArray>
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no reason at all, I'll change to Option<WrappedByteArray>
* and the forced divergent vertex execution in `prepare` is reverted. | ||
*/ | ||
// spotless:on | ||
public final class DivergentExecutionLivenessBreakTest { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice test 👍
…re-overflow-mitigations
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## develop #854 +/- ##
===========================================
- Coverage 42.8% 42.8% -0.1%
+ Complexity 4299 4295 -4
===========================================
Files 1692 1692
Lines 51999 52035 +36
Branches 1494 1496 +2
===========================================
+ Hits 22298 22312 +14
- Misses 29231 29250 +19
- Partials 470 473 +3
Flags with carried forward coverage won't be shown. Click here to find out more.
|
Quality Gate passedIssues Measures |
e791b13
to
1ad56a4
Compare
final var proof = ledgerExtension.proof(); | ||
final var header = proof.ledgerHeader(); | ||
|
||
var commitRequest = | ||
new CommitRequest( | ||
ledgerExtension.transactions(), | ||
proof, | ||
serializedVertexStoreState, | ||
serializedVertexStoreState.map(WrappedByteArray::value), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should just always handle it as a WrappedByteArray
I think? It's just safer than passing around a big byte[]
.
If we need to, let's add an SborCodec for WrappedByteArray
:
public static void registerCodec(CodecMap codecMap) {
codecMap.register(
WrappedByteArray.class,
codecs ->
new CustomByteArrayCodec<>(
WrappedByteArray::value,
WrappedByteArray::new));
}
This PR includes:
bft.vertex_store.max_serialized_size_bytes
(min value is 10 Mb).record
s and there's no longer a separateBFTCommittedUpdate
event. Since a (consensus) commit can only happen as a result of QC insertion, this has been captured inBFTHighQCUpdate
, which allowed us to get rid of the weird logic indispatchPostQcInsertionEvents
in VertexStoreAdapter