github.com/uber/kraken@v0.1.4/lib/torrent/scheduler/announcer/announcer.go (about) 1 // Copyright (c) 2016-2019 Uber Technologies, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 package announcer 15 16 import ( 17 "time" 18 19 "github.com/uber/kraken/core" 20 "github.com/uber/kraken/tracker/announceclient" 21 22 "github.com/andres-erbsen/clock" 23 "go.uber.org/atomic" 24 "go.uber.org/zap" 25 ) 26 27 // Config defines Announcer configuration. 28 type Config struct { 29 DefaultInterval time.Duration `yaml:"default_interval"` 30 MaxInterval time.Duration `yaml:"max_interval"` 31 } 32 33 func (c Config) applyDefaults() Config { 34 if c.DefaultInterval == 0 { 35 c.DefaultInterval = 5 * time.Second 36 } 37 if c.MaxInterval == 0 { 38 c.MaxInterval = time.Minute 39 } 40 return c 41 } 42 43 // Events defines Announcer events. 44 type Events interface { 45 AnnounceTick() 46 } 47 48 // Announcer is a thin wrapper around an announceclient.Client which handles 49 // changes to the announce interval. 50 type Announcer struct { 51 config Config 52 client announceclient.Client 53 events Events 54 interval *atomic.Int64 55 timer *clock.Timer 56 logger *zap.SugaredLogger 57 } 58 59 // New creates a new Announcer. 60 func New( 61 config Config, 62 client announceclient.Client, 63 events Events, 64 clk clock.Clock, 65 logger *zap.SugaredLogger) *Announcer { 66 config = config.applyDefaults() 67 return &Announcer{ 68 config: config, 69 client: client, 70 events: events, 71 interval: atomic.NewInt64(int64(config.DefaultInterval)), 72 timer: clk.Timer(config.DefaultInterval), 73 logger: logger, 74 } 75 } 76 77 // Default creates a default Announcer. 78 // TODO(evelynl94): make announce interval configurable. 79 func Default( 80 client announceclient.Client, 81 events Events, 82 clk clock.Clock, 83 logger *zap.SugaredLogger) *Announcer { 84 return New(Config{}, client, events, clk, logger) 85 } 86 87 // Announce announces through the underlying client and returns the resulting 88 // peer handout. Updates the announce interval if it has changed. 89 func (a *Announcer) Announce( 90 d core.Digest, h core.InfoHash, complete bool) ([]*core.PeerInfo, error) { 91 92 peers, interval, err := a.client.Announce(d, h, complete, announceclient.V1) 93 if err != nil { 94 return nil, err 95 } 96 if interval == 0 { 97 // Protect against unset intervals. 98 interval = a.config.DefaultInterval 99 } 100 if interval > a.config.MaxInterval { 101 // Since the timer is only reset on ticks, a wildly high interval can lock 102 // down future updates to interval. The max interval protects against a 103 // mistake in the central authority which will become impossible to correct. 104 interval = a.config.DefaultInterval 105 } 106 if a.interval.Swap(int64(interval)) != int64(interval) { 107 // Note: updated interval will take effect after next tick. 108 a.logger.Infof("Announce interval updated to %s", interval) 109 } 110 return peers, nil 111 } 112 113 // Ticker emits AnnounceTick events at the current announce interval, which may be 114 // updated by Announce. Ticker exits when done is closed. 115 func (a *Announcer) Ticker(done <-chan struct{}) { 116 for { 117 select { 118 case <-a.timer.C: 119 a.events.AnnounceTick() 120 a.timer.Reset(time.Duration(a.interval.Load())) 121 case <-done: 122 return 123 } 124 } 125 }