commit
3e26c31d13
@ -0,0 +1,138 @@ |
||||
package consensus |
||||
|
||||
import ( |
||||
"sync" |
||||
"time" |
||||
|
||||
msg_pb "github.com/harmony-one/harmony/api/proto/message" |
||||
"github.com/harmony-one/harmony/internal/utils" |
||||
"github.com/harmony-one/harmony/p2p" |
||||
) |
||||
|
||||
const ( |
||||
// RetryIntervalInSec is the interval for message retry
|
||||
RetryIntervalInSec = 10 |
||||
) |
||||
|
||||
// MessageSender is the wrapper object that controls how a consensus message is sent
|
||||
type MessageSender struct { |
||||
blockNum uint64 // The current block number at consensus
|
||||
blockNumMutex sync.Mutex |
||||
messagesToRetry sync.Map |
||||
// The p2p host used to send/receive p2p messages
|
||||
host p2p.Host |
||||
// RetryTimes is number of retry attempts
|
||||
retryTimes int |
||||
} |
||||
|
||||
// MessageRetry controls the message that can be retried
|
||||
type MessageRetry struct { |
||||
blockNum uint64 // The block number this message is for
|
||||
groups []p2p.GroupID |
||||
p2pMsg []byte |
||||
msgType msg_pb.MessageType |
||||
retryCount int |
||||
isActive bool |
||||
isActiveMutex sync.Mutex |
||||
} |
||||
|
||||
// NewMessageSender initializes the consensus message sender.
|
||||
func NewMessageSender(host p2p.Host) *MessageSender { |
||||
return &MessageSender{host: host, retryTimes: int(phaseDuration.Seconds()) / RetryIntervalInSec} |
||||
} |
||||
|
||||
// Reset resets the sender's state for new block
|
||||
func (sender *MessageSender) Reset(blockNum uint64) { |
||||
sender.blockNumMutex.Lock() |
||||
sender.blockNum = blockNum |
||||
sender.blockNumMutex.Unlock() |
||||
sender.StopAllRetriesExceptCommitted() |
||||
sender.messagesToRetry.Range(func(key interface{}, value interface{}) bool { |
||||
if msgRetry, ok := value.(*MessageRetry); ok { |
||||
if msgRetry.msgType != msg_pb.MessageType_COMMITTED { |
||||
sender.messagesToRetry.Delete(key) |
||||
} |
||||
} |
||||
return true |
||||
}) |
||||
} |
||||
|
||||
// SendWithRetry sends message with retry logic.
|
||||
func (sender *MessageSender) SendWithRetry(blockNum uint64, msgType msg_pb.MessageType, groups []p2p.GroupID, p2pMsg []byte) error { |
||||
willRetry := sender.retryTimes != 0 |
||||
msgRetry := MessageRetry{blockNum: blockNum, groups: groups, p2pMsg: p2pMsg, msgType: msgType, retryCount: 0, isActive: willRetry} |
||||
if willRetry { |
||||
sender.messagesToRetry.Store(msgType, &msgRetry) |
||||
go func() { |
||||
sender.Retry(&msgRetry) |
||||
}() |
||||
} |
||||
return sender.host.SendMessageToGroups(groups, p2pMsg) |
||||
} |
||||
|
||||
// SendWithoutRetry sends message without retry logic.
|
||||
func (sender *MessageSender) SendWithoutRetry(groups []p2p.GroupID, p2pMsg []byte) error { |
||||
return sender.host.SendMessageToGroups(groups, p2pMsg) |
||||
} |
||||
|
||||
// Retry will retry the consensus message for <RetryTimes> times.
|
||||
func (sender *MessageSender) Retry(msgRetry *MessageRetry) { |
||||
for { |
||||
time.Sleep(RetryIntervalInSec * time.Second) |
||||
|
||||
if msgRetry.retryCount >= sender.retryTimes { |
||||
// Retried enough times
|
||||
return |
||||
} |
||||
|
||||
msgRetry.isActiveMutex.Lock() |
||||
if !msgRetry.isActive { |
||||
msgRetry.isActiveMutex.Unlock() |
||||
// Retry is stopped
|
||||
return |
||||
} |
||||
msgRetry.isActiveMutex.Unlock() |
||||
|
||||
if msgRetry.msgType != msg_pb.MessageType_COMMITTED { |
||||
sender.blockNumMutex.Lock() |
||||
if msgRetry.blockNum < sender.blockNum { |
||||
sender.blockNumMutex.Unlock() |
||||
// Block already moved ahead, no need to retry old block's messages
|
||||
return |
||||
} |
||||
sender.blockNumMutex.Unlock() |
||||
} |
||||
|
||||
msgRetry.retryCount++ |
||||
if err := sender.host.SendMessageToGroups(msgRetry.groups, msgRetry.p2pMsg); err != nil { |
||||
utils.GetLogInstance().Warn("[Retry] Failed re-sending consensus message", "groupID", msgRetry.groups, "blockNum", msgRetry.blockNum, "MsgType", msgRetry.msgType, "RetryCount", msgRetry.retryCount) |
||||
} else { |
||||
utils.GetLogInstance().Info("[Retry] Successfully resent consensus message", "groupID", msgRetry.groups, "blockNum", msgRetry.blockNum, "MsgType", msgRetry.msgType, "RetryCount", msgRetry.retryCount) |
||||
} |
||||
} |
||||
} |
||||
|
||||
// StopRetry stops the retry.
|
||||
func (sender *MessageSender) StopRetry(msgType msg_pb.MessageType) { |
||||
data, ok := sender.messagesToRetry.Load(msgType) |
||||
if ok { |
||||
msgRetry := data.(*MessageRetry) |
||||
msgRetry.isActiveMutex.Lock() |
||||
msgRetry.isActive = false |
||||
msgRetry.isActiveMutex.Unlock() |
||||
} |
||||
} |
||||
|
||||
// StopAllRetriesExceptCommitted stops all the existing retries except committed message (which lives across consensus).
|
||||
func (sender *MessageSender) StopAllRetriesExceptCommitted() { |
||||
sender.messagesToRetry.Range(func(k, v interface{}) bool { |
||||
if msgRetry, ok := v.(*MessageRetry); ok { |
||||
if msgRetry.msgType != msg_pb.MessageType_COMMITTED { |
||||
msgRetry.isActiveMutex.Lock() |
||||
msgRetry.isActive = false |
||||
msgRetry.isActiveMutex.Unlock() |
||||
} |
||||
} |
||||
return true |
||||
}) |
||||
} |
Loading…
Reference in new issue