make GossipData immutable

The same piece of GossipData can be shared and subject to different
Merge operations. We either need to copy the data when we share it, or
create copies on modification, i.e. copy-on-write.

We choose the latter, since it is more efficient and there is just one
mutating operation.

Fixes #1697.
This commit is contained in:
Matthias Radestock committed 2015-11-17 20:13:46 +00:00
1 parent de802f9d76
commit 3cd199e852
6 files changed
+32 -21

No files matched your search

+2 -2
View File
@@ -478,8 +478,8 @@ type ipamGossipData struct {
alloc *Allocator alloc *Allocator
} }
func (d *ipamGossipData) Merge(other mesh.GossipData) { func (d *ipamGossipData) Merge(other mesh.GossipData) mesh.GossipData {
// no-op return d // no-op
} }
func (d *ipamGossipData) Encode() [][]byte { func (d *ipamGossipData) Encode() [][]byte {
+2 -3
View File
@@ -6,7 +6,7 @@ import (
type GossipData interface { type GossipData interface {
Encode() [][]byte Encode() [][]byte
Merge(GossipData) Merge(GossipData) GossipData
} }
type Gossip interface { type Gossip interface {
@@ -79,8 +79,7 @@ func (sender *GossipSender) Send(data GossipData) {
// NB: this must not be invoked concurrently // NB: this must not be invoked concurrently
select { select {
case pending := <-sender.cell: case pending := <-sender.cell:
pending.Merge(data) sender.cell <- pending.Merge(data)
sender.cell <- pending
default: default:
sender.cell <- data sender.cell <- data
} }
+10 -8
View File
@@ -127,17 +127,19 @@ func (router *Router) BroadcastTopologyUpdate(update []*Peer) {
for _, p := range update { for _, p := range update {
names[p.Name] = void names[p.Name] = void
} }
router.TopologyGossip.GossipBroadcast(
router.TopologyGossip.GossipBroadcast(&TopologyGossipData{ &TopologyGossipData{peers: router.Peers, update: names})
peers: router.Peers,
update: names,
})
} }
func (d *TopologyGossipData) Merge(other GossipData) { func (d *TopologyGossipData) Merge(other GossipData) GossipData {
for name := range other.(*TopologyGossipData).update { names := make(PeerNameSet)
d.update[name] = void for name := range d.update {
names[name] = void
} }
for name := range other.(*TopologyGossipData).update {
names[name] = void
}
return &TopologyGossipData{peers: d.peers, update: names}
} }
func (d *TopologyGossipData) Encode() [][]byte { func (d *TopologyGossipData) Encode() [][]byte {
+6 -2
View File
@@ -12,8 +12,12 @@ func (d *SurrogateGossipData) Encode() [][]byte {
return d.messages return d.messages
} }
func (d *SurrogateGossipData) Merge(other GossipData) { func (d *SurrogateGossipData) Merge(other GossipData) GossipData {
d.messages = append(d.messages, other.(*SurrogateGossipData).messages...) o := other.(*SurrogateGossipData)
messages := make([][]byte, 0, len(d.messages)+len(o.messages))
messages = append(messages, d.messages...)
messages = append(messages, o.messages...)
return &SurrogateGossipData{messages: messages}
} }
// SurrogateGossiper ignores unicasts and relays broadcasts and gossips. // SurrogateGossiper ignores unicasts and relays broadcasts and gossips.
+10 -4
View File
@@ -245,12 +245,18 @@ type GossipData struct {
Entries Entries
} }
func (g *GossipData) Merge(o mesh.GossipData) { func (g *GossipData) Merge(o mesh.GossipData) mesh.GossipData {
other := o.(*GossipData) other := o.(*GossipData)
g.Entries.merge(other.Entries) gossip := &GossipData{
if g.Timestamp < other.Timestamp { Entries: make(Entries, len(g.Entries)),
g.Timestamp = other.Timestamp Timestamp: g.Timestamp,
} }
copy(gossip.Entries, g.Entries)
gossip.Entries.merge(other.Entries)
if gossip.Timestamp < other.Timestamp {
gossip.Timestamp = other.Timestamp
}
return gossip
} }
func (g *GossipData) Decode(msg []byte) error { func (g *GossipData) Decode(msg []byte) error {
+2 -2
View File
@@ -117,7 +117,7 @@ func TestGossipDataMerge(t *testing.T) {
g1 := GossipData{Entries: makeEntries("AcDf")} g1 := GossipData{Entries: makeEntries("AcDf")}
g2 := GossipData{Entries: makeEntries("BEf")} g2 := GossipData{Entries: makeEntries("BEf")}
g1.Merge(&g2) g3 := g1.Merge(&g2).(*GossipData)
require.Equal(t, GossipData{Entries: makeEntries("ABcDEf")}, g1) require.Equal(t, GossipData{Entries: makeEntries("ABcDEf")}, *g3)
} }