2018-06-15 04:30:37 +02:00
package prism
import (
"context"
"strconv"
"sync"
"github.com/lbryio/reflector.go/cluster"
"github.com/lbryio/reflector.go/db"
"github.com/lbryio/reflector.go/dht"
"github.com/lbryio/reflector.go/dht/bits"
"github.com/lbryio/reflector.go/peer"
"github.com/lbryio/reflector.go/reflector"
"github.com/lbryio/reflector.go/store"
"github.com/lbryio/lbry.go/errors"
2018-06-25 22:49:40 +02:00
"github.com/lbryio/lbry.go/stop"
2018-06-15 04:30:37 +02:00
log "github.com/sirupsen/logrus"
)
type Config struct {
PeerPort int
ReflectorPort int
DhtAddress string
DhtSeedNodes [ ] string
ClusterPort int
ClusterSeedAddr string
2018-06-21 17:26:48 +02:00
// limit the range of hashes to announce. useful for testing
HashRange * bits . Range
2018-06-15 04:30:37 +02:00
DB * db . SQL
Blobs store . BlobStore
}
// DefaultConf returns a default config
func DefaultConf ( ) * Config {
return & Config {
2018-06-21 17:26:48 +02:00
ClusterPort : cluster . DefaultPort ,
2018-06-15 04:30:37 +02:00
}
}
// Prism is the root instance of the application and houses the DHT, Peer Server, Reflector Server, and Cluster.
type Prism struct {
conf * Config
db * db . SQL
dht * dht . DHT
peer * peer . Server
reflector * reflector . Server
cluster * cluster . Cluster
2018-06-25 22:49:40 +02:00
grp * stop . Group
2018-06-15 04:30:37 +02:00
}
// New returns an initialized Prism instance
func New ( conf * Config ) * Prism {
if conf == nil {
conf = DefaultConf ( )
}
dhtConf := dht . NewStandardConfig ( )
dhtConf . Address = conf . DhtAddress
2018-06-21 17:26:48 +02:00
dhtConf . PeerProtocolPort = conf . PeerPort
if len ( conf . DhtSeedNodes ) > 0 {
dhtConf . SeedNodes = conf . DhtSeedNodes
}
2018-06-15 04:30:37 +02:00
d := dht . New ( dhtConf )
c := cluster . New ( conf . ClusterPort , conf . ClusterSeedAddr )
p := & Prism {
conf : conf ,
db : conf . DB ,
dht : d ,
cluster : c ,
peer : peer . NewServer ( conf . Blobs ) ,
reflector : reflector . NewServer ( conf . Blobs ) ,
2018-06-25 22:49:40 +02:00
grp : stop . New ( ) ,
2018-06-15 04:30:37 +02:00
}
2018-06-19 19:47:13 +02:00
c . OnMembershipChange = func ( n , total int ) {
2018-06-25 22:49:40 +02:00
p . grp . Add ( 1 )
2018-06-15 04:30:37 +02:00
go func ( ) {
p . AnnounceRange ( n , total )
2018-06-25 22:49:40 +02:00
p . grp . Done ( )
2018-06-15 04:30:37 +02:00
} ( )
}
return p
}
// Start starts the components of the application.
func ( p * Prism ) Start ( ) error {
2018-06-21 17:26:48 +02:00
var err error
2018-06-15 04:30:37 +02:00
if p . conf . DB == nil {
return errors . Err ( "db required in conf" )
}
if p . conf . Blobs == nil {
return errors . Err ( "blobs required in conf" )
}
2018-06-21 17:26:48 +02:00
err = p . peer . Start ( ":" + strconv . Itoa ( p . conf . PeerPort ) )
2018-06-15 04:30:37 +02:00
if err != nil {
return err
}
2018-06-21 17:26:48 +02:00
err = p . reflector . Start ( ":" + strconv . Itoa ( p . conf . ReflectorPort ) )
2018-06-15 04:30:37 +02:00
if err != nil {
return err
}
2018-06-21 17:26:48 +02:00
err = p . dht . Start ( )
2018-06-15 04:30:37 +02:00
if err != nil {
return err
}
2018-06-21 17:26:48 +02:00
err = p . cluster . Connect ( )
2018-06-15 04:30:37 +02:00
if err != nil {
return err
}
return nil
}
// Shutdown gracefully shuts down the different prism components before exiting.
func ( p * Prism ) Shutdown ( ) {
2018-06-25 22:49:40 +02:00
p . grp . StopAndWait ( )
2018-06-15 04:30:37 +02:00
p . cluster . Shutdown ( )
p . dht . Shutdown ( )
2018-06-21 17:26:48 +02:00
p . reflector . Shutdown ( )
p . peer . Shutdown ( )
2018-06-15 04:30:37 +02:00
}
// AnnounceRange announces the `n`th interval of hashes, out of a total of `total` intervals
func ( p * Prism ) AnnounceRange ( n , total int ) {
// TODO: 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
// num and total are 1-indexed
if n < 1 {
log . Errorf ( "%s: n must be >= 1" , p . dht . ID ( ) . HexShort ( ) )
return
}
2018-06-21 17:26:48 +02:00
var r bits . Range
if p . conf . HashRange != nil {
r = * p . conf . HashRange
} else {
//r := bits.MaxRange().IntervalP(n, total)
// TODO: this is temporary. it lets me test with a small number of hashes. use the full range in production
min , max , err := p . db . GetHashRange ( )
if err != nil {
log . Errorf ( "%s: error getting hash range: %s" , p . dht . ID ( ) . HexShort ( ) , err . Error ( ) )
return
}
r = ( bits . Range { Start : bits . FromHexP ( min ) , End : bits . FromHexP ( max ) } ) . IntervalP ( n , total )
2018-06-15 04:30:37 +02:00
}
2018-06-19 19:47:13 +02:00
log . Infof ( "%s: hash range is now %s to %s" , p . dht . ID ( ) . HexShort ( ) , r . Start , r . End )
2018-06-15 04:30:37 +02:00
ctx , cancel := context . WithCancel ( context . Background ( ) )
2018-06-22 15:30:16 +02:00
hashCh , errCh := p . db . GetStoredHashesInRange ( ctx , r . Start , r . End )
2018-06-15 04:30:37 +02:00
var wg sync . WaitGroup
wg . Add ( 1 )
go func ( ) {
defer wg . Done ( )
select {
2018-06-25 22:49:40 +02:00
case <- p . grp . Ch ( ) :
2018-06-15 04:30:37 +02:00
return
case err , more := <- errCh :
if more && err != nil {
log . Error ( err )
}
}
} ( )
wg . Add ( 1 )
go func ( ) {
defer wg . Done ( )
for {
select {
2018-06-25 22:49:40 +02:00
case <- p . grp . Ch ( ) :
2018-06-15 04:30:37 +02:00
cancel ( )
return
case hash , more := <- hashCh :
if ! more {
return
}
2018-06-19 19:47:13 +02:00
//log.Infof("%s: announcing %s", p.dht.ID().HexShort(), hash.Hex())
2018-06-15 04:30:37 +02:00
p . dht . Add ( hash )
}
}
} ( )
wg . Wait ( )
}