middleware: added varinterval
This commit is contained in:
parent
35df7a29bc
commit
0607841b3b
6 changed files with 273 additions and 0 deletions
|
@ -23,6 +23,7 @@ import (
|
|||
|
||||
// Middleware
|
||||
_ "github.com/chihaya/chihaya/middleware/deniability"
|
||||
_ "github.com/chihaya/chihaya/middleware/varinterval"
|
||||
_ "github.com/chihaya/chihaya/server/store/middleware/client"
|
||||
_ "github.com/chihaya/chihaya/server/store/middleware/infohash"
|
||||
_ "github.com/chihaya/chihaya/server/store/middleware/ip"
|
||||
|
|
34
middleware/varinterval/README.md
Normal file
34
middleware/varinterval/README.md
Normal file
|
@ -0,0 +1,34 @@
|
|||
## Announce Interval Variation Middleware
|
||||
|
||||
This package provides the announce middleware `varinterval` which randomizes the announce interval.
|
||||
|
||||
### Functionality
|
||||
|
||||
This middleware will choose random announces and modify the `interval` and `min_interval` fields.
|
||||
A random number of seconds will be added to the `interval` field and, if desired, also to the `min_interval` field.
|
||||
|
||||
Note that if a response is picked for modification and `min_interval` should be changed as well, both `interval` and `min_interval` will be modified by the same amount.
|
||||
|
||||
### Use Case
|
||||
|
||||
Use this middleware to avoid recurring load spikes on the tracker.
|
||||
By randomizing the announce interval, load spikes will flatten out after a few cycles.
|
||||
|
||||
### Configuration
|
||||
|
||||
This middleware provides the following parameters for configuration:
|
||||
|
||||
- `modify_response_probability` (float, >0, <= 1) indicates the probability by which a response will be augmented with random peers.
|
||||
- `max_increase_delta` (int, >0) sets an upper boundary (inclusive) for the amount of seconds added.
|
||||
- `modify_min_interval` (boolean) whether to modify the `min_interval` field as well.
|
||||
|
||||
An example config might look like this:
|
||||
|
||||
chihaya:
|
||||
tracker:
|
||||
announce_middleware:
|
||||
- name: varinterval
|
||||
config:
|
||||
modify_response_probability: 0.2
|
||||
max_increase_delta: 60
|
||||
modify_min_interval: true
|
43
middleware/varinterval/config.go
Normal file
43
middleware/varinterval/config.go
Normal file
|
@ -0,0 +1,43 @@
|
|||
// Copyright 2016 The Chihaya Authors. All rights reserved.
|
||||
// Use of this source code is governed by the BSD 2-Clause license,
|
||||
// which can be found in the LICENSE file.
|
||||
|
||||
package varinterval
|
||||
|
||||
import (
|
||||
"gopkg.in/yaml.v2"
|
||||
|
||||
"github.com/chihaya/chihaya"
|
||||
)
|
||||
|
||||
// Config represents the configuration for the varinterval middleware.
|
||||
type Config struct {
|
||||
// ModifyResponseProbability is the probability by which a response will
|
||||
// be modified.
|
||||
ModifyResponseProbability float32 `yaml:"modify_response_probability"`
|
||||
|
||||
// MaxIncreaseDelta is the amount of seconds that will be added at most.
|
||||
MaxIncreaseDelta int `yaml:"max_increase_delta"`
|
||||
|
||||
// ModifyMinInterval specifies whether min_interval should be increased
|
||||
// as well.
|
||||
ModifyMinInterval bool `yaml:"modify_min_interval"`
|
||||
}
|
||||
|
||||
// newConfig parses the given MiddlewareConfig as a varinterval.Config.
|
||||
//
|
||||
// The contents of the config are not checked.
|
||||
func newConfig(mwcfg chihaya.MiddlewareConfig) (*Config, error) {
|
||||
bytes, err := yaml.Marshal(mwcfg.Config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var cfg Config
|
||||
err = yaml.Unmarshal(bytes, &cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &cfg, nil
|
||||
}
|
59
middleware/varinterval/config_test.go
Normal file
59
middleware/varinterval/config_test.go
Normal file
|
@ -0,0 +1,59 @@
|
|||
// Copyright 2016 The Chihaya Authors. All rights reserved.
|
||||
// Use of this source code is governed by the BSD 2-Clause license,
|
||||
// which can be found in the LICENSE file.
|
||||
|
||||
package varinterval
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/yaml.v2"
|
||||
|
||||
"github.com/chihaya/chihaya"
|
||||
)
|
||||
|
||||
type configTestData struct {
|
||||
modifyProbability string
|
||||
maxIncreaseDelta string
|
||||
modifyMinInterval string
|
||||
err bool
|
||||
expected Config
|
||||
}
|
||||
|
||||
var (
|
||||
configTemplate = `
|
||||
name: foo
|
||||
config:
|
||||
modify_response_probability: %s
|
||||
max_increase_delta: %s
|
||||
modify_min_interval: %s`
|
||||
|
||||
configData = []configTestData{
|
||||
{"1.0", "60", "false", false, Config{1.0, 60, false}},
|
||||
{"a", "60", "false", true, Config{}},
|
||||
}
|
||||
)
|
||||
|
||||
func TestNewConfig(t *testing.T) {
|
||||
var mwconfig chihaya.MiddlewareConfig
|
||||
|
||||
cfg, err := newConfig(mwconfig)
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, cfg)
|
||||
|
||||
for _, test := range configData {
|
||||
config := fmt.Sprintf(configTemplate, test.modifyProbability, test.maxIncreaseDelta, test.modifyMinInterval)
|
||||
err = yaml.Unmarshal([]byte(config), &mwconfig)
|
||||
assert.Nil(t, err)
|
||||
|
||||
cfg, err = newConfig(mwconfig)
|
||||
if test.err {
|
||||
assert.NotNil(t, err)
|
||||
continue
|
||||
}
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, test.expected, *cfg)
|
||||
}
|
||||
}
|
70
middleware/varinterval/varinterval.go
Normal file
70
middleware/varinterval/varinterval.go
Normal file
|
@ -0,0 +1,70 @@
|
|||
// Copyright 2016 The Chihaya Authors. All rights reserved.
|
||||
// Use of this source code is governed by the BSD 2-Clause license,
|
||||
// which can be found in the LICENSE file.
|
||||
|
||||
package varinterval
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"github.com/chihaya/chihaya"
|
||||
"github.com/chihaya/chihaya/tracker"
|
||||
)
|
||||
|
||||
func init() {
|
||||
tracker.RegisterAnnounceMiddlewareConstructor("varinterval", constructor)
|
||||
}
|
||||
|
||||
type varintervalMiddleware struct {
|
||||
cfg *Config
|
||||
r *rand.Rand
|
||||
}
|
||||
|
||||
// constructor provides a middleware constructor that returns a middleware to
|
||||
// insert a variation into announce intervals.
|
||||
//
|
||||
// It returns an error if the config provided is either syntactically or
|
||||
// semantically incorrect.
|
||||
func constructor(c chihaya.MiddlewareConfig) (tracker.AnnounceMiddleware, error) {
|
||||
cfg, err := newConfig(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if cfg.ModifyResponseProbability <= 0 || cfg.ModifyResponseProbability > 1 {
|
||||
return nil, errors.New("modify_response_probability must be in [0,1)")
|
||||
}
|
||||
|
||||
if cfg.MaxIncreaseDelta <= 0 {
|
||||
return nil, errors.New("max_increase_delta must be > 0")
|
||||
}
|
||||
|
||||
mw := varintervalMiddleware{
|
||||
cfg: cfg,
|
||||
r: rand.New(rand.NewSource(time.Now().UnixNano())),
|
||||
}
|
||||
|
||||
return mw.modifyResponse, nil
|
||||
}
|
||||
|
||||
func (mw *varintervalMiddleware) modifyResponse(next tracker.AnnounceHandler) tracker.AnnounceHandler {
|
||||
return func(cfg *chihaya.TrackerConfig, req *chihaya.AnnounceRequest, resp *chihaya.AnnounceResponse) error {
|
||||
err := next(cfg, req, resp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if mw.cfg.ModifyResponseProbability == 1 || mw.r.Float32() < mw.cfg.ModifyResponseProbability {
|
||||
addSeconds := time.Duration(mw.r.Intn(mw.cfg.MaxIncreaseDelta)+1) * time.Second
|
||||
resp.Interval += addSeconds
|
||||
|
||||
if mw.cfg.ModifyMinInterval {
|
||||
resp.MinInterval += addSeconds
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
66
middleware/varinterval/varinterval_test.go
Normal file
66
middleware/varinterval/varinterval_test.go
Normal file
|
@ -0,0 +1,66 @@
|
|||
// Copyright 2016 The Chihaya Authors. All rights reserved.
|
||||
// Use of this source code is governed by the BSD 2-Clause license,
|
||||
// which can be found in the LICENSE file.
|
||||
|
||||
package varinterval
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/chihaya/chihaya"
|
||||
"github.com/chihaya/chihaya/tracker"
|
||||
)
|
||||
|
||||
type constructorTestData struct {
|
||||
cfg Config
|
||||
error bool
|
||||
}
|
||||
|
||||
var constructorData = []constructorTestData{
|
||||
{Config{1.0, 10, false}, false},
|
||||
{Config{1.1, 10, false}, true},
|
||||
{Config{0, 10, true}, true},
|
||||
{Config{1.0, 0, false}, true},
|
||||
}
|
||||
|
||||
func TestConstructor(t *testing.T) {
|
||||
for _, tt := range constructorData {
|
||||
_, err := constructor(chihaya.MiddlewareConfig{
|
||||
Config: tt.cfg,
|
||||
})
|
||||
|
||||
if tt.error {
|
||||
assert.NotNil(t, err, fmt.Sprintf("error expected for %+v", tt.cfg))
|
||||
} else {
|
||||
assert.Nil(t, err, fmt.Sprintf("no error expected for %+v", tt.cfg))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestModifyResponse(t *testing.T) {
|
||||
var (
|
||||
achain tracker.AnnounceChain
|
||||
req chihaya.AnnounceRequest
|
||||
resp chihaya.AnnounceResponse
|
||||
)
|
||||
|
||||
mw, err := constructor(chihaya.MiddlewareConfig{
|
||||
Config: Config{
|
||||
ModifyResponseProbability: 1.0,
|
||||
MaxIncreaseDelta: 10,
|
||||
ModifyMinInterval: true,
|
||||
},
|
||||
})
|
||||
assert.Nil(t, err)
|
||||
|
||||
achain.Append(mw)
|
||||
handler := achain.Handler()
|
||||
|
||||
err = handler(nil, &req, &resp)
|
||||
assert.Nil(t, err)
|
||||
assert.True(t, resp.Interval > 0, "interval should have been increased")
|
||||
assert.True(t, resp.MinInterval > 0, "min_interval should have been increased")
|
||||
}
|
Loading…
Reference in a new issue