Skip to content
This repository has been archived by the owner on Sep 26, 2019. It is now read-only.

Limit size of Ibft future message buffer #873

Merged
merged 26 commits into from Feb 20, 2019

Conversation

jframe
Copy link
Contributor

@jframe jframe commented Feb 15, 2019

PR description

This add a limit to the number of ibft messages that are stored in the future buffer.

There is a total number of messages that are allowed. And also a max distance from the current height that messages are allowed for.

If the total message limit is reached then all the messages in the highest chain height are removed.

Additionally if there is only one height then we will remove the oldest message to make room instead.

Fixed Issue(s)

@@ -307,7 +309,9 @@ private static ControllerAndState createControllerAndFinalState(
messageValidatorFactory),
messageValidatorFactory),
gossiper,
DUPLICATE_MESSAGE_LIMIT);
DUPLICATE_MESSAGE_LIMIT,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would probably prefer to see the futureMessageBuffer get created here and passed in (rather than the raw values) - why? Can't answer that.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 ^^^
It's cleaner, in terms of IoC and reduces number of constructors on the controller.
I'd suggest instead of passing in values, construct & pass in the objects:
final MessageTracker duplicateMessageTracker, final FutureMessageBuffer futureMessageBuffer

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is cleaner and also reduces the need the seperate constructor in the IbftController.
Done.

return;
}

if (totalMessages >= futureMessagesLimit) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had assumed evictMessages() MAY need to loop (i.e. what if you need to remove more than 1 height?) - should this conditional become a loop in evict?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think there is any need to remove any more than one height at a time. Each height must contain at least one message so if we remove a height we will have room for the message to be added.

}

if (!buffer.containsKey(msgChainHeight)) {
buffer.put(msgChainHeight, new ArrayList<>());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what if this msgChainHeight is higher than the height removed in evictMessage? Eg. MsgChainHeight is 5, but we just removed chain-height 4 ...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assuming that msgChainHeight is still within the max distance we would add a message for chain height 5. Which is perhaps is a bit odd. Maybe it would be better not to add in that case, essentially evicting itself as the highest chain height? Need to think about this a bit more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think I'd rather see it evict itself. MAYBE you could do something like add the new message, then run the eviction process.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done. yeah I think it better for it to be evicted. changed the code to run the evict after after adding, avoids needing to special case this add case.

if (buffer.size() > 1) {
buffer.remove(buffer.lastKey());
} else {
List<Message> messages = buffer.firstEntry().getValue();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible that buffer size is 0? I guess not.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am making an assumption that it isn't 0 at this point but that only holds if futureMessagesLimit isn't 0. Be better if the evict messages handled that case, so i'll update evictMessages to handle that properly.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done. added handling of zero message limit.

@jframe jframe added the work in progress Work on this pull request is ongoing label Feb 18, 2019
@jframe jframe removed the work in progress Work on this pull request is ongoing label Feb 18, 2019
}

@VisibleForTesting
FutureMessageBuffer(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this constructor really of use? ...looks like when it's called in the tests a new TreeMap<> is passed in

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is, I'm using that so I can check the state of the map is correct in the tests without exposing the map to everyone. A new TreeMap<> is passed in, but I'm checking in my tests messages are added to the map correctly.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a tight coupling between the test and the specific implementation of the logic, I'm not convinced that's the best approach.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done. updated so don't need special constructor

}

private void addMessageToBuffer(final long msgChainHeight, final Message rawMsg) {
if (!buffer.containsKey(msgChainHeight)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: could use computeIfAbsent/putIfAbsent

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

this.futureMessagesLimit = futureMessagesLimit;
}

public void addMessage(final long chainHeight, final long msgChainHeight, final Message rawMsg) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't LOVE msgChainHeight coming in on this API .... rather this get reset in the Poll somehow.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.changed so it is using been passed in as a arg.

private long chainHeight;

public FutureMessageBuffer(
final long futureMessagesMaxDistance, final long futureMessagesLimit, final int chainHeight) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Drop the chainHeight and initialise it to zero, as the only place it's getting called in code is the IbftController that is passing in a hardcoded zero.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice catch. that should not of been initialised to zero. fixed now.

this.chainHeight = chainHeight;
}

public void updateChainHeight(final long chainHeight) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry to be difficult, are we worried that both this and poll take "chainHeight" - but they are different numbers?
I.e. this is the number of the most recent block on the chain - whereas 'poll' is really the "next" block ...
Wondering if renaming these functions to (something like):

  • discardMessagesForHeightsBelow(x)
  • extractMessagesWithHeight(y)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The height in the poll is really the message chain height, whereas the updateChainHeight is the current chain height we are working so they are different. I think renaming the arg in poll to msgChainHeight might that clearer.

}

public List<Message> retrieveMessagesForHeight(final long height) {
this.chainHeight = height;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: no need for the 'this'

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@jframe jframe merged commit 7efd040 into PegaSysEng:master Feb 20, 2019
@jframe jframe deleted the futureMessageBuffer branch February 20, 2019 22:42
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
3 participants