2018-02-08 19:33:52 +01:00
package cluster
import (
2018-03-01 22:12:53 +01:00
"io/ioutil"
baselog "log"
2018-05-29 23:19:40 +02:00
"sort"
2018-03-01 22:12:53 +01:00
2018-05-24 19:05:05 +02:00
"github.com/lbryio/lbry.go/crypto"
2018-02-22 19:48:46 +01:00
"github.com/lbryio/lbry.go/errors"
2018-05-29 23:19:40 +02:00
"github.com/lbryio/lbry.go/stopOnce"
2018-02-08 19:33:52 +01:00
"github.com/hashicorp/serf/serf"
log "github.com/sirupsen/logrus"
)
2018-05-29 23:19:40 +02:00
const (
2018-05-30 03:38:55 +02:00
// DefaultClusterPort is the default port used when starting up a Cluster.
2018-05-29 23:19:40 +02:00
DefaultClusterPort = 17946
)
2018-05-30 03:38:55 +02:00
// Cluster is a management type for Serf which is used to maintain cluster membership of lbry nodes.
2018-05-24 19:05:05 +02:00
type Cluster struct {
2018-05-29 23:19:40 +02:00
name string
port int
seedAddr string
2018-05-24 19:05:05 +02:00
s * serf . Serf
2018-05-29 23:19:40 +02:00
eventCh chan serf . Event
stop * stopOnce . Stopper
2018-05-24 19:05:05 +02:00
}
2018-05-30 03:38:55 +02:00
// New returns a new Cluster instance that is not connected.
2018-05-29 23:19:40 +02:00
func New ( port int , seedAddr string ) * Cluster {
return & Cluster {
name : crypto . RandString ( 12 ) ,
port : port ,
seedAddr : seedAddr ,
stop : stopOnce . New ( ) ,
}
}
2018-05-30 03:38:55 +02:00
// Connect Initializes the Cluster based on a configuration passed via the New function. It then stores the seed
// address, starts gossiping and listens for gossip.
2018-05-29 23:19:40 +02:00
func ( c * Cluster ) Connect ( ) error {
2018-05-24 19:05:05 +02:00
var err error
2018-05-29 23:19:40 +02:00
conf := serf . DefaultConfig ( )
conf . MemberlistConfig . BindPort = c . port
conf . MemberlistConfig . AdvertisePort = c . port
conf . NodeName = c . name
nullLogger := baselog . New ( ioutil . Discard , "" , 0 )
conf . Logger = nullLogger
c . eventCh = make ( chan serf . Event )
conf . EventCh = c . eventCh
c . s , err = serf . Create ( conf )
2018-05-24 19:05:05 +02:00
if err != nil {
2018-05-29 23:19:40 +02:00
return errors . Prefix ( "couldn't create cluster" , err )
2018-05-24 19:05:05 +02:00
}
2018-05-29 23:19:40 +02:00
if c . seedAddr != "" {
_ , err = c . s . Join ( [ ] string { c . seedAddr } , true )
if err != nil {
return err
}
}
2018-06-07 05:48:07 +02:00
c . stop . Add ( 1 )
go func ( ) {
defer c . stop . Done ( )
c . listen ( )
} ( )
2018-05-29 23:19:40 +02:00
return nil
}
2018-05-24 19:05:05 +02:00
2018-05-30 03:38:55 +02:00
// Shutdown safely shuts down the cluster.
2018-05-29 23:19:40 +02:00
func ( c * Cluster ) Shutdown ( ) {
c . stop . StopAndWait ( )
2018-05-30 03:38:55 +02:00
if err := c . s . Leave ( ) ; err != nil {
log . Error ( "error shutting down cluster - " , err )
}
2018-05-29 23:19:40 +02:00
}
func ( c * Cluster ) listen ( ) {
2018-06-07 05:48:07 +02:00
for {
select {
case <- c . stop . Ch ( ) :
return
case event := <- c . eventCh :
switch event . EventType ( ) {
case serf . EventMemberJoin , serf . EventMemberFailed , serf . EventMemberLeave :
memberEvent := event . ( serf . MemberEvent )
if event . EventType ( ) == serf . EventMemberJoin && len ( memberEvent . Members ) == 1 && memberEvent . Members [ 0 ] . Name == c . name {
// ignore event from my own joining of the cluster
continue
2018-05-24 19:05:05 +02:00
}
2018-06-07 05:48:07 +02:00
//spew.Dump(c.Members())
alive := getAliveMembers ( c . s . Members ( ) )
log . Printf ( "%s: my hash range is now %d of %d\n" , c . name , getHashRangeStart ( c . name , alive ) , len ( alive ) )
// figure out my new hash range based on the start and the number of alive members
// get hashes in that range that need announcing
// announce them
// if more than one node is announcing each hash, figure out how to deal with last_announced_at so both nodes dont announce the same thing at the same time
2018-05-24 19:05:05 +02:00
}
}
2018-06-07 05:48:07 +02:00
}
2018-05-24 19:05:05 +02:00
}
2018-05-29 23:19:40 +02:00
func getHashRangeStart ( myName string , members [ ] serf . Member ) int {
var names [ ] string
for _ , m := range members {
names = append ( names , m . Name )
2018-02-08 19:33:52 +01:00
}
2018-05-29 23:19:40 +02:00
sort . Strings ( names )
i := 1
for _ , n := range names {
if n == myName {
return i
}
i ++
2018-02-08 19:33:52 +01:00
}
2018-05-29 23:19:40 +02:00
return - 1
}
2018-02-08 19:33:52 +01:00
2018-05-29 23:19:40 +02:00
func getAliveMembers ( members [ ] serf . Member ) [ ] serf . Member {
var alive [ ] serf . Member
for _ , m := range members {
if m . Status == serf . StatusAlive {
alive = append ( alive , m )
}
}
return alive
2018-02-08 19:33:52 +01:00
}