Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

namespace didn't remove experiments from segmentAllocations after invoking RemoveExperiment() #4

Open
wants to merge 19 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 59 additions & 56 deletions index_op_test.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,32 @@
package planout

import (
"testing"
"io/ioutil"
"encoding/json"
"io/ioutil"
"testing"
)

func getInterpreter(filename string) (*Interpreter, error) {
data, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
var js map[string]interface{}
err = json.Unmarshal(data, &js)
if err != nil {
return nil, err
}

return &Interpreter{
Name: "the name",
Salt: "the salt",
Evaluated: false,
Inputs: make(map[string]interface{}),
Outputs: make(map[string]interface{}),
Code: js,
}, nil
}

type Inner struct {
Value string
}
Expand All @@ -19,39 +40,24 @@ type NestedStruct struct {
}

func TestNestedIndex(t *testing.T) {
data, err := ioutil.ReadFile("test/nested_index.json")
exp, err := getInterpreter("test/nested_index.json")
if err != nil {
t.Fatal(err)
}
var js map[string]interface{}
err = json.Unmarshal(data, &js)
if (err != nil) {
t.Fatal(err)
}

inputs := make(map[string]interface{})
inputs["s"] = &NestedStruct{
exp.Inputs["s"] = &NestedStruct{
Outer: &Outer{
Inner: &Inner{
Value: "foo",
},
},
}

exp := &Interpreter{
Name: "nested_test",
Salt: "salt123",
Evaluated: false,
Inputs: inputs,
Outputs: make(map[string]interface{}),
Code: js,
}

if _, ok := exp.Run(); !ok {
t.Fatal("Failed to run experiment")
}

if (exp.Outputs["out"] != "foo") {
if exp.Outputs["out"] != "foo" {
t.Fail()
}
}
Expand All @@ -61,30 +67,16 @@ type StructWithArray struct {
}

func TestArrayInStruct(t *testing.T) {
data, err := ioutil.ReadFile("test/array_field_test.json")
exp, err := getInterpreter("test/array_field_test.json")
if err != nil {
t.Fatal(err)
}
var js map[string]interface{}
err = json.Unmarshal(data, &js)
if (err != nil) {
t.Fatal(err)
}

inputs := make(map[string]interface{})
i := 123
inputs["s"] = &StructWithArray{
i := int(123)
exp.Inputs["s"] = &StructWithArray{
Array: []*int{&i},
}

exp := &Interpreter{
Name: "test_array_field",
Salt: "blasdfalks",
Inputs: inputs,
Outputs: make(map[string]interface{}),
Code: js,
}

if _, ok := exp.Run(); !ok {
t.Fatal("Experiment run failed")
}
Expand All @@ -95,40 +87,51 @@ func TestArrayInStruct(t *testing.T) {
}

type StructWithMap struct {
Map map[string]string
Map map[string]int64
}

func TestMapField(t *testing.T) {
data, err := ioutil.ReadFile("test/map_index_test.json")
exp, err := getInterpreter("test/map_index_test.json")
if err != nil {
t.Fatal(err)
}
var js map[string]interface{}
err = json.Unmarshal(data, &js)
if (err != nil) {
t.Fatal(err)
}

inputs := make(map[string]interface{})
mapField := make(map[string]string)
mapField["key"] = "value"
inputs["s"] = &StructWithMap{
mapField := make(map[string]int64)
mapField["key"] = 42
exp.Inputs["s"] = &StructWithMap{
Map: mapField,
}

exp := &Interpreter{
Name: "test_map_index",
Salt: "asdfkhjaslkdfjh",
Inputs: inputs,
Outputs: make(map[string]interface{}),
Code: js,
if _, ok := exp.Run(); !ok {
t.Fatal("Experiment run failed")
}

if elem := exp.Outputs["element"]; elem != int64(42) {
t.Fail()
}

if exp.Outputs["empty"] != nil {
t.Fail()
}
}

type StructWithNilField struct {
None interface{}
}

func TestStructWithNilField(t *testing.T) {
exp, err := getInterpreter("test/struct_with_nil_field.json")
if err != nil {
t.Fatal(err)
}

exp.Inputs["struct"] = &StructWithNilField{}

if _, ok := exp.Run(); !ok {
t.Fatal("Experiment run failed")
}

if elem := exp.Outputs["element"]; elem != "value" {
if exp.Outputs["nil"] != nil {
t.Fail()
}
}
}
10 changes: 9 additions & 1 deletion interpreter.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ type Interpreter struct {
Inputs, Outputs, Overrides map[string]interface{}
Code interface{}
Evaluated, InExperiment bool
parameterSalt string
ParameterSalt string
}

func (interpreter *Interpreter) Run(force ...bool) (map[string]interface{}, bool) {
Expand All @@ -54,6 +54,14 @@ func (interpreter *Interpreter) Run(force ...bool) (map[string]interface{}, bool
return interpreter.Outputs, true
}

// ReSet方法重置experiment的三个参数Inputs、Overrides、Outputs,保留namespace的分区信息,就可以复用该namespace,
// 而不是每次都重新生成一个namespace,耗费性能
func (interpreter *Interpreter) ReSet() {
// 置空参数
interpreter.Inputs, interpreter.Overrides, interpreter.Outputs = make(map[string]interface{}), make(map[string]interface{}), make(map[string]interface{})
interpreter.Evaluated, interpreter.InExperiment = false, false
}

func (interpreter *Interpreter) Get(name string) (interface{}, bool) {
value, ok := interpreter.Overrides[name]
if ok {
Expand Down
88 changes: 55 additions & 33 deletions namespace.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ type SimpleNamespace struct {
PrimaryUnit string
NumSegments int
Inputs map[string]interface{}
segmentAllocations map[uint64]string
availableSegments []int
currentExperiments map[string]*Interpreter
defaultExperiment *Interpreter
selectedExperiment uint64
SegmentAllocations map[uint64]string
AvailableSegments []int
CurrentExperiments map[string]*Interpreter
DefaultExperiment *Interpreter
SelectedExperiment uint64
}

func NewSimpleNamespace(name string, numSegments int, primaryUnit string, inputs map[string]interface{}) SimpleNamespace {
Expand All @@ -40,67 +40,89 @@ func NewSimpleNamespace(name string, numSegments int, primaryUnit string, inputs
PrimaryUnit: primaryUnit,
NumSegments: numSegments,
Inputs: inputs,
segmentAllocations: make(map[uint64]string),
availableSegments: avail,
currentExperiments: make(map[string]*Interpreter),
selectedExperiment: uint64(numSegments + 1),
defaultExperiment: noop,
SegmentAllocations: make(map[uint64]string),
AvailableSegments: avail,
CurrentExperiments: make(map[string]*Interpreter),
SelectedExperiment: uint64(numSegments + 1),
DefaultExperiment: noop,
}
}

// SetInputs方法可以手动设置namespace的输入值
func (n *SimpleNamespace) SetInputs(inputs map[string]interface{}) {
for _, exp := range n.CurrentExperiments {
exp.Inputs = inputs
}
n.DefaultExperiment.Inputs = inputs
n.Inputs = inputs
}
// ReSet方法重置该namespace中的实验信息
func (n *SimpleNamespace) ReSet() {
// 遍历namespace下所有experiments,进行重置
for _, exp := range n.CurrentExperiments {
exp.ReSet()
}
defaultConfig := n.DefaultExperiment.Outputs
n.DefaultExperiment.ReSet()
n.DefaultExperiment.Outputs = defaultConfig
n.Inputs = make(map[string]interface{})
n.SelectedExperiment = uint64(n.NumSegments + 1)
}

func (n *SimpleNamespace) Run() *Interpreter {
interpreter := n.defaultExperiment
interpreter := n.DefaultExperiment

if name, ok := n.segmentAllocations[n.getSegment()]; ok {
interpreter = n.currentExperiments[name]
interpreter.Name = n.Name + "-" + interpreter.Name
interpreter.Salt = n.Name + "." + interpreter.Name
if name, ok := n.SegmentAllocations[n.getSegment()]; ok {
interpreter = n.CurrentExperiments[name]
//interpreter.Name = n.Name + "-" + interpreter.Name
//interpreter.Salt = n.Name + "." + interpreter.Name
}

interpreter.Run()
return interpreter
}

func (n *SimpleNamespace) AddDefaultExperiment(defaultExperiment *Interpreter) {
n.defaultExperiment = defaultExperiment
n.DefaultExperiment = defaultExperiment
}

func (n *SimpleNamespace) AddExperiment(name string, interpreter *Interpreter, segments int) error {
avail := len(n.availableSegments)
avail := len(n.AvailableSegments)
if avail < segments {
return fmt.Errorf("Not enough segments available %v to add the new experiment %v\n", avail, name)
}

if _, ok := n.currentExperiments[name]; ok {
if _, ok := n.CurrentExperiments[name]; ok {
return fmt.Errorf("There is already and experiment called %s\n", name)
}

n.allocateExperiment(name, segments)

n.currentExperiments[name] = interpreter
n.CurrentExperiments[name] = interpreter
return nil
}

func (n *SimpleNamespace) RemoveExperiment(name string) error {
_, exists := n.currentExperiments[name]
_, exists := n.CurrentExperiments[name]
if !exists {
return fmt.Errorf("Experiment %v does not exists in the namespace\n", name)
}

segmentsToFree := make([]int, 0, n.NumSegments)
for i := range n.segmentAllocations {
if n.segmentAllocations[i] == name {
for i := range n.SegmentAllocations {
if n.SegmentAllocations[i] == name {
segmentsToFree = append(segmentsToFree, int(i))
delete(n.SegmentAllocations, i)
}
}

for i := range segmentsToFree {
n.availableSegments = append(n.availableSegments, segmentsToFree[i])
n.AvailableSegments = append(n.AvailableSegments, segmentsToFree[i])
}

sort.Ints(n.availableSegments)
sort.Ints(n.AvailableSegments)

delete(n.currentExperiments, name)
delete(n.CurrentExperiments, name)
return nil
}

Expand All @@ -115,8 +137,8 @@ func (n *SimpleNamespace) allocateExperiment(name string, segments int) {
}

// Compile Sample operator
var availableSegmentsAsInterface []interface{} = make([]interface{}, len(n.availableSegments))
for i, d := range n.availableSegments {
var availableSegmentsAsInterface = make([]interface{}, len(n.AvailableSegments))
for i, d := range n.AvailableSegments {
availableSegmentsAsInterface[i] = d
}

Expand All @@ -132,15 +154,15 @@ func (n *SimpleNamespace) allocateExperiment(name string, segments int) {
// Remove segment from available_segments
for i := range shuffle {
j := shuffle[i].(int)
n.segmentAllocations[uint64(j)] = name
n.availableSegments = deallocateSegments(n.availableSegments, j)
n.SegmentAllocations[uint64(j)] = name
n.AvailableSegments = deallocateSegments(n.AvailableSegments, j)
}
}

func (n *SimpleNamespace) getSegment() uint64 {

if n.selectedExperiment != uint64(n.NumSegments+1) {
return n.selectedExperiment
if n.SelectedExperiment != uint64(n.NumSegments+1) {
return n.SelectedExperiment
}

// generate random integer min=0, max=num_segments, unit=primary_unit
Expand All @@ -160,8 +182,8 @@ func (n *SimpleNamespace) getSegment() uint64 {
args["max"] = n.NumSegments - 1
args["unit"] = n.Inputs[n.PrimaryUnit]
s := &randomInteger{}
n.selectedExperiment = s.execute(args, expt).(uint64)
return n.selectedExperiment
n.SelectedExperiment = s.execute(args, expt).(uint64)
return n.SelectedExperiment
}

func deallocateSegments(allocated []int, segmentToRemove int) []int {
Expand Down
Loading