middleware: added varinterval

This commit is contained in:
Leo Balduf 2016-04-02 18:44:45 -04:00
parent 35df7a29bc
commit 0607841b3b
6 changed files with 273 additions and 0 deletions

View file

@ -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"

View 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

View 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
}

View 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)
}
}

View 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
}
}

View 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")
}