-
Notifications
You must be signed in to change notification settings - Fork 211
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
sync2: implement multi-peer synchronization
This adds multi-peer synchronization support. When the local set differs too much from the remote sets, "torrent-style" "split sync" is attempted which splits the set into subranges and syncs each sub-range against a separate peer. Otherwise, the full sync is done, syncing the whole set against each of the synchronization peers. Full sync is also done after each split sync run. The local set can be considered synchronized after the specified number of full syncs has happened. The approach is loosely based on [SREP: Out-Of-Band Sync of Transaction Pools for Large-Scale Blockchains](https://people.bu.edu/staro/2023-ICBC-Novak.pdf) paper by Novak Boškov, Sevval Simsek, Ari Trachtenberg, and David Starobinski.
- Loading branch information
Showing
22 changed files
with
3,877 additions
and
12 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
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,22 @@ | ||
package multipeer | ||
|
||
import ( | ||
"encoding/binary" | ||
|
||
"github.com/spacemeshos/go-spacemesh/sync2/rangesync" | ||
) | ||
|
||
func getDelimiters(numPeers, keyLen, maxDepth int) (h []rangesync.KeyBytes) { | ||
if numPeers < 2 { | ||
return nil | ||
} | ||
mask := uint64(0xffffffffffffffff) << (64 - maxDepth) | ||
inc := (uint64(0x80) << 56) / uint64(numPeers) | ||
h = make([]rangesync.KeyBytes, numPeers-1) | ||
for i, v := 0, uint64(0); i < numPeers-1; i++ { | ||
h[i] = make(rangesync.KeyBytes, keyLen) | ||
v += inc | ||
binary.BigEndian.PutUint64(h[i], (v<<1)&mask) | ||
} | ||
return h | ||
} |
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,105 @@ | ||
package multipeer_test | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/stretchr/testify/require" | ||
|
||
"github.com/spacemeshos/go-spacemesh/sync2/multipeer" | ||
) | ||
|
||
func TestGetDelimiters(t *testing.T) { | ||
for _, tc := range []struct { | ||
numPeers int | ||
keyLen int | ||
maxDepth int | ||
values []string | ||
}{ | ||
{ | ||
numPeers: 0, | ||
maxDepth: 64, | ||
keyLen: 32, | ||
values: nil, | ||
}, | ||
{ | ||
numPeers: 1, | ||
maxDepth: 64, | ||
keyLen: 32, | ||
values: nil, | ||
}, | ||
{ | ||
numPeers: 2, | ||
maxDepth: 64, | ||
keyLen: 32, | ||
values: []string{ | ||
"8000000000000000000000000000000000000000000000000000000000000000", | ||
}, | ||
}, | ||
{ | ||
numPeers: 2, | ||
maxDepth: 24, | ||
keyLen: 32, | ||
values: []string{ | ||
"8000000000000000000000000000000000000000000000000000000000000000", | ||
}, | ||
}, | ||
{ | ||
numPeers: 3, | ||
maxDepth: 64, | ||
keyLen: 32, | ||
values: []string{ | ||
"5555555555555554000000000000000000000000000000000000000000000000", | ||
"aaaaaaaaaaaaaaa8000000000000000000000000000000000000000000000000", | ||
}, | ||
}, | ||
{ | ||
numPeers: 3, | ||
maxDepth: 24, | ||
keyLen: 32, | ||
values: []string{ | ||
"5555550000000000000000000000000000000000000000000000000000000000", | ||
"aaaaaa0000000000000000000000000000000000000000000000000000000000", | ||
}, | ||
}, | ||
{ | ||
numPeers: 3, | ||
maxDepth: 4, | ||
keyLen: 32, | ||
values: []string{ | ||
"5000000000000000000000000000000000000000000000000000000000000000", | ||
"a000000000000000000000000000000000000000000000000000000000000000", | ||
}, | ||
}, | ||
{ | ||
numPeers: 4, | ||
maxDepth: 64, | ||
keyLen: 32, | ||
values: []string{ | ||
"4000000000000000000000000000000000000000000000000000000000000000", | ||
"8000000000000000000000000000000000000000000000000000000000000000", | ||
"c000000000000000000000000000000000000000000000000000000000000000", | ||
}, | ||
}, | ||
{ | ||
numPeers: 4, | ||
maxDepth: 24, | ||
keyLen: 32, | ||
values: []string{ | ||
"4000000000000000000000000000000000000000000000000000000000000000", | ||
"8000000000000000000000000000000000000000000000000000000000000000", | ||
"c000000000000000000000000000000000000000000000000000000000000000", | ||
}, | ||
}, | ||
} { | ||
ds := multipeer.GetDelimiters(tc.numPeers, tc.keyLen, tc.maxDepth) | ||
var hs []string | ||
for _, d := range ds { | ||
hs = append(hs, d.String()) | ||
} | ||
if len(tc.values) == 0 { | ||
require.Empty(t, hs, "%d delimiters", tc.numPeers) | ||
} else { | ||
require.Equal(t, tc.values, hs, "%d delimiters", tc.numPeers) | ||
} | ||
} | ||
} |
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,58 @@ | ||
package multipeer | ||
|
||
import ( | ||
"github.com/spacemeshos/go-spacemesh/sync2/rangesync" | ||
) | ||
|
||
// DumbSet is an unoptimized OrderedSet to be used for testing purposes. | ||
// It builds on rangesync.DumbSet. | ||
type DumbSet struct { | ||
*rangesync.DumbSet | ||
} | ||
|
||
var _ OrderedSet = &DumbSet{} | ||
|
||
// NewDumbHashSet creates an unoptimized OrderedSet to be used for testing purposes. | ||
// If disableReAdd is true, receiving the same item multiple times will fail. | ||
func NewDumbHashSet() *DumbSet { | ||
return &DumbSet{ | ||
DumbSet: &rangesync.DumbSet{}, | ||
} | ||
} | ||
|
||
// Advance implements OrderedSet. | ||
func (ds *DumbSet) EnsureLoaded() error { | ||
return nil | ||
} | ||
|
||
// Advance implements OrderedSet. | ||
func (ds *DumbSet) Advance() error { | ||
return nil | ||
} | ||
|
||
// Has implements OrderedSet. | ||
func (ds *DumbSet) Has(k rangesync.KeyBytes) (bool, error) { | ||
var first rangesync.KeyBytes | ||
sr := ds.Items() | ||
for cur := range sr.Seq { | ||
if first == nil { | ||
first = cur | ||
} else if first.Compare(cur) == 0 { | ||
return false, sr.Error() | ||
} | ||
if k.Compare(cur) == 0 { | ||
return true, sr.Error() | ||
} | ||
} | ||
return false, sr.Error() | ||
} | ||
|
||
// Copy implements OrderedSet. | ||
func (ds *DumbSet) Copy(syncScope bool) rangesync.OrderedSet { | ||
return &DumbSet{ds.DumbSet.Copy(syncScope).(*rangesync.DumbSet)} | ||
} | ||
|
||
// Release implements OrderedSet. | ||
func (ds *DumbSet) Release() error { | ||
return nil | ||
} |
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,25 @@ | ||
package multipeer | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/spacemeshos/go-spacemesh/p2p" | ||
) | ||
|
||
type ( | ||
SyncRunner = syncRunner | ||
SplitSync = splitSync | ||
) | ||
|
||
var ( | ||
WithSyncRunner = withSyncRunner | ||
WithClock = withClock | ||
GetDelimiters = getDelimiters | ||
NewSyncQueue = newSyncQueue | ||
NewSplitSync = newSplitSync | ||
NewSyncList = newSyncList | ||
) | ||
|
||
func (mpr *MultiPeerReconciler) FullSync(ctx context.Context, syncPeers []p2p.Peer) error { | ||
return mpr.fullSync(ctx, syncPeers) | ||
} |
Oops, something went wrong.