-
Notifications
You must be signed in to change notification settings - Fork 688
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* WIP: topic split * Add cutover support * Remove measurements that were moved to PR#3988 * Code review rework * Code review rework --------- Co-authored-by: Evan Gray <[email protected]>
- Loading branch information
1 parent
c2496cd
commit d3533aa
Showing
11 changed files
with
900 additions
and
333 deletions.
There are no files selected for viewing
This file contains 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
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
package p2p | ||
|
||
import ( | ||
"fmt" | ||
"strings" | ||
"sync/atomic" | ||
"time" | ||
|
||
"go.uber.org/zap" | ||
) | ||
|
||
// The format of this time is very picky. Please use the exact format specified by cutOverFmtStr! | ||
const mainnetCutOverTimeStr = "" | ||
const testnetCutOverTimeStr = "" | ||
const devnetCutOverTimeStr = "" | ||
const cutOverFmtStr = "2006-01-02T15:04:05-0700" | ||
|
||
// gossipCutoverCompleteFlag indicates if the cutover time has passed, meaning we should publish only on the new topics. | ||
var gossipCutoverCompleteFlag atomic.Bool | ||
|
||
// GossipCutoverComplete returns true if the cutover time has passed, meaning we should publish on the new topic. | ||
func GossipCutoverComplete() bool { | ||
return gossipCutoverCompleteFlag.Load() | ||
} | ||
|
||
// evaluateCutOver determines if the gossip cutover time has passed yet and sets the global flag accordingly. If the time has | ||
// not yet passed, it creates a go routine to wait for that time and then set the flag. | ||
func evaluateGossipCutOver(logger *zap.Logger, networkID string) error { | ||
cutOverTimeStr := getCutOverTimeStr(networkID) | ||
|
||
sco, delay, err := evaluateGossipCutOverImpl(logger, cutOverTimeStr, time.Now()) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
gossipCutoverCompleteFlag.Store(sco) | ||
logger.Info("evaluated cutover flag", zap.Bool("cutOverFlag", GossipCutoverComplete()), zap.String("cutOverTime", cutOverTimeStr), zap.String("component", "p2pco")) | ||
|
||
if delay != time.Duration(0) { | ||
// Wait for the cut over time and then update the flag. | ||
go func() { | ||
time.Sleep(delay) | ||
logger.Info("time to cut over to new gossip topics", zap.String("cutOverTime", cutOverTimeStr), zap.String("component", "p2pco")) | ||
gossipCutoverCompleteFlag.Store(true) | ||
}() | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// evaluateGossipCutOverImpl performs the actual cut over check. It is a separate function for testing purposes. | ||
func evaluateGossipCutOverImpl(logger *zap.Logger, cutOverTimeStr string, now time.Time) (bool, time.Duration, error) { | ||
if cutOverTimeStr == "" { | ||
return false, 0, nil | ||
} | ||
|
||
cutOverTime, err := time.Parse(cutOverFmtStr, cutOverTimeStr) | ||
if err != nil { | ||
return false, 0, fmt.Errorf(`failed to parse cut over time: %w`, err) | ||
} | ||
|
||
if cutOverTime.Before(now) { | ||
logger.Info("cut over time has passed, should use new gossip topics", zap.String("cutOverTime", cutOverTime.Format(cutOverFmtStr)), zap.String("now", now.Format(cutOverFmtStr)), zap.String("component", "p2pco")) | ||
return true, 0, nil | ||
} | ||
|
||
// If we get here, we need to wait for the cutover and then switch the global flag. | ||
delay := cutOverTime.Sub(now) | ||
logger.Info("still waiting for cut over time", | ||
zap.Stringer("cutOverTime", cutOverTime), | ||
zap.String("now", now.Format(cutOverFmtStr)), | ||
zap.Stringer("delay", delay), | ||
zap.String("component", "p2pco")) | ||
|
||
return false, delay, nil | ||
} | ||
|
||
// getCutOverTimeStr returns the cut over time string based on the network ID passed in. | ||
func getCutOverTimeStr(networkID string) string { //nolint:unparam | ||
if strings.Contains(networkID, "/mainnet/") { | ||
return mainnetCutOverTimeStr | ||
} | ||
if strings.Contains(networkID, "/testnet/") { | ||
return testnetCutOverTimeStr | ||
} | ||
return devnetCutOverTimeStr | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
package p2p | ||
|
||
import ( | ||
"testing" | ||
"time" | ||
|
||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
"go.uber.org/zap" | ||
) | ||
|
||
func TestVerifyCutOverTime(t *testing.T) { | ||
if mainnetCutOverTimeStr != "" { | ||
_, err := time.Parse(cutOverFmtStr, mainnetCutOverTimeStr) | ||
require.NoError(t, err) | ||
} | ||
if testnetCutOverTimeStr != "" { | ||
_, err := time.Parse(cutOverFmtStr, testnetCutOverTimeStr) | ||
require.NoError(t, err) | ||
} | ||
if devnetCutOverTimeStr != "" { | ||
_, err := time.Parse(cutOverFmtStr, devnetCutOverTimeStr) | ||
require.NoError(t, err) | ||
} | ||
} | ||
|
||
func TestGetCutOverTimeStr(t *testing.T) { | ||
assert.Equal(t, mainnetCutOverTimeStr, getCutOverTimeStr("blah/blah/mainnet/blah")) | ||
assert.Equal(t, testnetCutOverTimeStr, getCutOverTimeStr("blah/blah/testnet/blah")) | ||
assert.Equal(t, devnetCutOverTimeStr, getCutOverTimeStr("blah/blah/devnet/blah")) | ||
} | ||
|
||
func TestCutOverDisabled(t *testing.T) { | ||
logger := zap.NewNop() | ||
|
||
cutOverTimeStr := "" | ||
now, err := time.Parse(cutOverFmtStr, "2023-10-06T18:19:00-0000") | ||
require.NoError(t, err) | ||
|
||
cuttingOver, delay, err := evaluateGossipCutOverImpl(logger, cutOverTimeStr, now) | ||
require.NoError(t, err) | ||
assert.False(t, cuttingOver) | ||
assert.Equal(t, time.Duration(0), delay) | ||
} | ||
|
||
func TestCutOverInvalidTime(t *testing.T) { | ||
logger := zap.NewNop() | ||
|
||
cutOverTimeStr := "Hello World" | ||
now, err := time.Parse(cutOverFmtStr, "2023-10-06T18:19:00-0000") | ||
require.NoError(t, err) | ||
|
||
_, _, err = evaluateGossipCutOverImpl(logger, cutOverTimeStr, now) | ||
require.EqualError(t, err, `failed to parse cut over time: parsing time "Hello World" as "2006-01-02T15:04:05-0700": cannot parse "Hello World" as "2006"`) | ||
} | ||
|
||
func TestCutOverAlreadyHappened(t *testing.T) { | ||
logger := zap.NewNop() | ||
|
||
cutOverTimeStr := "2023-10-06T18:18:00-0000" | ||
now, err := time.Parse(cutOverFmtStr, "2023-10-06T18:19:00-0000") | ||
require.NoError(t, err) | ||
|
||
cuttingOver, delay, err := evaluateGossipCutOverImpl(logger, cutOverTimeStr, now) | ||
require.NoError(t, err) | ||
assert.True(t, cuttingOver) | ||
assert.Equal(t, time.Duration(0), delay) | ||
} | ||
|
||
func TestCutOverDelayRequired(t *testing.T) { | ||
logger := zap.NewNop() | ||
|
||
cutOverTimeStr := "2023-10-06T18:18:00-0000" | ||
now, err := time.Parse(cutOverFmtStr, "2023-10-06T17:18:00-0000") | ||
require.NoError(t, err) | ||
|
||
cuttingOver, delay, err := evaluateGossipCutOverImpl(logger, cutOverTimeStr, now) | ||
require.NoError(t, err) | ||
assert.False(t, cuttingOver) | ||
assert.Equal(t, time.Duration(60*time.Minute), delay) | ||
} |
Oops, something went wrong.