From 749972de713f435b2c64112653db66c51d12351e Mon Sep 17 00:00:00 2001 From: Tim Wu Date: Mon, 12 Oct 2015 11:34:12 -0700 Subject: [PATCH 01/17] Handle nil values on indexing operations Hitting a nil value previously would cause a crash in the experiment run. This happens in both the case of the value being in a map, and in the case where the value is part of a struct. This fix causes the code to instead return a nil. Unfortunately this is not quite ideal in the case of maps to numerical values. In a normal go map, maps with numerical values apparently return a 0 when the requested key is not present. There doesn't seem to be any simple way to maintain parity with that behavior (though it does seem kind of odd, looking at it from Java colored glasses), so a concession is made to just return a nil in that case. --- index_op_test.go | 47 ++++++++++++++++++++++++++++++--- operators.go | 6 ++++- test/map_index_test.json | 12 +++++++++ test/struct_with_nil_field.json | 12 +++++++++ 4 files changed, 72 insertions(+), 5 deletions(-) create mode 100644 test/struct_with_nil_field.json diff --git a/index_op_test.go b/index_op_test.go index 5f752da..589c2e4 100644 --- a/index_op_test.go +++ b/index_op_test.go @@ -95,7 +95,7 @@ func TestArrayInStruct(t *testing.T) { } type StructWithMap struct { - Map map[string]string + Map map[string]int64 } func TestMapField(t *testing.T) { @@ -110,8 +110,8 @@ func TestMapField(t *testing.T) { } inputs := make(map[string]interface{}) - mapField := make(map[string]string) - mapField["key"] = "value" + mapField := make(map[string]int64) + mapField["key"] = 42 inputs["s"] = &StructWithMap{ Map: mapField, } @@ -128,7 +128,46 @@ func TestMapField(t *testing.T) { t.Fatal("Experiment run failed") } - if elem := exp.Outputs["element"]; elem != "value" { + 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) { + data, err := ioutil.ReadFile("test/struct_with_nil_field.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["struct"] = &StructWithNilField{} + + exp := &Interpreter{ + Name: "struct with nil field", + Salt: "safasdf", + Inputs: inputs, + Outputs: make(map[string]interface{}), + Code: js, + } + + if _, ok := exp.Run(); !ok { + t.Fatal("Experiment run failed") + } + + if exp.Outputs["nil"] != nil { t.Fail() } } \ No newline at end of file diff --git a/operators.go b/operators.go index 0cc3d74..375b58e 100644 --- a/operators.go +++ b/operators.go @@ -213,7 +213,11 @@ func unwrapValue(value reflect.Value) interface{} { case reflect.Bool: return value.Bool() default: - return value.Interface() + if value.IsValid() { + return value.Interface() + } else { + return nil + } } } diff --git a/test/map_index_test.json b/test/map_index_test.json index 7ce7cd1..9481e0c 100644 --- a/test/map_index_test.json +++ b/test/map_index_test.json @@ -32,6 +32,18 @@ }, "index": "key" } + }, + { + "op": "set", + "var": "empty", + "value": { + "op": "index", + "base": { + "op": "get", + "var": "map" + }, + "index": "not there" + } } ] } \ No newline at end of file diff --git a/test/struct_with_nil_field.json b/test/struct_with_nil_field.json new file mode 100644 index 0000000..ad8e14b --- /dev/null +++ b/test/struct_with_nil_field.json @@ -0,0 +1,12 @@ +{ + "op": "set", + "var": "nil", + "value": { + "op": "index", + "base": { + "op": "get", + "var": "struct" + }, + "index": "None" + } +} \ No newline at end of file From 83ab4eba45246c75cd9d0a8089fbe74f64407a21 Mon Sep 17 00:00:00 2001 From: Tim Wu Date: Mon, 12 Oct 2015 11:54:19 -0700 Subject: [PATCH 02/17] Factor out some test commonality --- index_op_test.go | 96 +++++++++++++++--------------------------------- 1 file changed, 30 insertions(+), 66 deletions(-) diff --git a/index_op_test.go b/index_op_test.go index 589c2e4..9f491f8 100644 --- a/index_op_test.go +++ b/index_op_test.go @@ -6,6 +6,27 @@ import ( "encoding/json" ) +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 } @@ -19,18 +40,12 @@ 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", @@ -38,15 +53,6 @@ func TestNestedIndex(t *testing.T) { }, } - 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") } @@ -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") } @@ -99,31 +91,17 @@ type StructWithMap struct { } 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]int64) mapField["key"] = 42 - inputs["s"] = &StructWithMap{ + 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") } @@ -142,26 +120,12 @@ type StructWithNilField struct { } func TestStructWithNilField(t *testing.T) { - data, err := ioutil.ReadFile("test/struct_with_nil_field.json") + exp, err := getInterpreter("test/struct_with_nil_field.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["struct"] = &StructWithNilField{} - exp := &Interpreter{ - Name: "struct with nil field", - Salt: "safasdf", - Inputs: inputs, - Outputs: make(map[string]interface{}), - Code: js, - } + exp.Inputs["struct"] = &StructWithNilField{} if _, ok := exp.Run(); !ok { t.Fatal("Experiment run failed") From 34a712531c8f403d6f99e242ecacceb485db4563 Mon Sep 17 00:00:00 2001 From: Tim Wu Date: Mon, 12 Oct 2015 17:45:50 -0700 Subject: [PATCH 03/17] Formatting --- index_op_test.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/index_op_test.go b/index_op_test.go index 9f491f8..cb99a54 100644 --- a/index_op_test.go +++ b/index_op_test.go @@ -1,9 +1,9 @@ package planout import ( - "testing" - "io/ioutil" "encoding/json" + "io/ioutil" + "testing" ) func getInterpreter(filename string) (*Interpreter, error) { @@ -13,17 +13,17 @@ func getInterpreter(filename string) (*Interpreter, error) { } var js map[string]interface{} err = json.Unmarshal(data, &js) - if (err != nil) { + if err != nil { return nil, err } return &Interpreter{ - Name: "the name", - Salt: "the salt", + Name: "the name", + Salt: "the salt", Evaluated: false, - Inputs: make(map[string]interface{}), - Outputs: make(map[string]interface{}), - Code: js, + Inputs: make(map[string]interface{}), + Outputs: make(map[string]interface{}), + Code: js, }, nil } @@ -57,7 +57,7 @@ func TestNestedIndex(t *testing.T) { t.Fatal("Failed to run experiment") } - if (exp.Outputs["out"] != "foo") { + if exp.Outputs["out"] != "foo" { t.Fail() } } @@ -134,4 +134,4 @@ func TestStructWithNilField(t *testing.T) { if exp.Outputs["nil"] != nil { t.Fail() } -} \ No newline at end of file +} From e70ed1b5492946465c177f122ffbcc0b84c8d034 Mon Sep 17 00:00:00 2001 From: andy pan Date: Wed, 18 Jul 2018 15:39:48 +0800 Subject: [PATCH 04/17] clear segmentAllocations after removing an experiment --- namespace.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/namespace.go b/namespace.go index 8ff2b48..cd63425 100644 --- a/namespace.go +++ b/namespace.go @@ -94,6 +94,10 @@ func (n *SimpleNamespace) RemoveExperiment(name string) error { } } + for _, val := range segmentsToFree { + delete(n.segmentAllocations, uint64(val)) + } + for i := range segmentsToFree { n.availableSegments = append(n.availableSegments, segmentsToFree[i]) } From 61e7d99d385389e342ee53b759a1a6b498fd21b4 Mon Sep 17 00:00:00 2001 From: Andy Pan Date: Sun, 22 Jul 2018 12:42:48 +0800 Subject: [PATCH 05/17] optimization for removing experiments --- namespace.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/namespace.go b/namespace.go index cd63425..597f8d4 100644 --- a/namespace.go +++ b/namespace.go @@ -91,13 +91,10 @@ func (n *SimpleNamespace) RemoveExperiment(name string) error { for i := range n.segmentAllocations { if n.segmentAllocations[i] == name { segmentsToFree = append(segmentsToFree, int(i)) + delete(n.segmentAllocations, i) } } - for _, val := range segmentsToFree { - delete(n.segmentAllocations, uint64(val)) - } - for i := range segmentsToFree { n.availableSegments = append(n.availableSegments, segmentsToFree[i]) } From 26636857e4c54bd8ddc48d82f2bdba7417c4c066 Mon Sep 17 00:00:00 2001 From: andy pan Date: Mon, 23 Jul 2018 16:47:29 +0800 Subject: [PATCH 06/17] fixed issue after removing experiments --- namespace_test.go | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/namespace_test.go b/namespace_test.go index 7c784be..c4886a9 100644 --- a/namespace_test.go +++ b/namespace_test.go @@ -80,4 +80,33 @@ func TestSimpleNamespace(t *testing.T) { if len(n.availableSegments) != 100 { t.Errorf("Expected all segments to be available. Actual %d\n", len(n.availableSegments)) } + + + testUID := generateString() + inputs["userid"] = testUID + n = NewSimpleNamespace("test_removing_namespace", 20, "userid", inputs) + n.AddExperiment("random ops", e2, 10) + n.AddExperiment("simple", e3, 10) + n.AddDefaultExperiment(e1) + interpreter = n.Run() + for _, v := range n.segmentAllocations { + if v == "simple" { + t.Log("added [simple] exp\n") + } + } + val, ok := interpreter.Get("output") + if !ok || val!= "test" { + t.Errorf("Namespace run was not successful out:[%+v]\n", interpreter) + } + n.RemoveExperiment("simple") + interpreter = n.Run() + for _, v := range n.segmentAllocations { + if v == "simple" { + t.Error("didn't remove [simple] exp\n") + } + } + val, ok = interpreter.Get("output") + if ok { + t.Errorf("Namespace run was not successful out:[%+v]\n", interpreter) + } } From 0c8573e3f8e53561a405177decf0438c873d1e0c Mon Sep 17 00:00:00 2001 From: andy pan Date: Mon, 23 Jul 2018 16:56:17 +0800 Subject: [PATCH 07/17] fixed --- namespace_test.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/namespace_test.go b/namespace_test.go index c4886a9..ab02b96 100644 --- a/namespace_test.go +++ b/namespace_test.go @@ -82,12 +82,11 @@ func TestSimpleNamespace(t *testing.T) { } - testUID := generateString() - inputs["userid"] = testUID - n = NewSimpleNamespace("test_removing_namespace", 20, "userid", inputs) + inputs["userid"] = "test-id" + n = NewSimpleNamespace("test_removing_namespace", 100, "userid", inputs) + n.AddExperiment("simple ops", e1, 10) n.AddExperiment("random ops", e2, 10) - n.AddExperiment("simple", e3, 10) - n.AddDefaultExperiment(e1) + n.AddExperiment("simple", e3, 80) interpreter = n.Run() for _, v := range n.segmentAllocations { if v == "simple" { From 50cfdc55675a52d832aa191789ebed85e1243730 Mon Sep 17 00:00:00 2001 From: andy pan Date: Wed, 25 Jul 2018 15:27:47 +0800 Subject: [PATCH 08/17] fixed reflect err when userid was uint --- namespace_test.go | 1 + utils.go | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/namespace_test.go b/namespace_test.go index ab02b96..a0119d3 100644 --- a/namespace_test.go +++ b/namespace_test.go @@ -108,4 +108,5 @@ func TestSimpleNamespace(t *testing.T) { if ok { t.Errorf("Namespace run was not successful out:[%+v]\n", interpreter) } + } diff --git a/utils.go b/utils.go index 9d84acd..1ec5b06 100644 --- a/utils.go +++ b/utils.go @@ -213,9 +213,12 @@ func toNumber(value interface{}) (float64, bool) { x = float64(value.(float32)) } return x, true - case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64: + case int, int8, int16, int32, int64: i := reflect.ValueOf(value) return float64(i.Int()), true + case uint, uint8, uint16, uint32, uint64: + i := reflect.ValueOf(value) + return float64(i.Uint()), true case bool: if value { return 1, true From 98c9466b34d87ac06143fbfdb6b578238a80b923 Mon Sep 17 00:00:00 2001 From: andy pan Date: Thu, 26 Jul 2018 10:01:30 +0800 Subject: [PATCH 09/17] update --- namespace.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/namespace.go b/namespace.go index 597f8d4..4acadc7 100644 --- a/namespace.go +++ b/namespace.go @@ -48,6 +48,13 @@ func NewSimpleNamespace(name string, numSegments int, primaryUnit string, inputs } } +func (n *SimpleNamespace) SetInputs(inputs map[string]interface{}) { + for _, exp := range n.currentExperiments { + exp.Inputs = inputs + } + n.Inputs = inputs +} + func (n *SimpleNamespace) Run() *Interpreter { interpreter := n.defaultExperiment From 8f6bfd55888d57906fc5265f11d10d74ba86e829 Mon Sep 17 00:00:00 2001 From: andy pan Date: Thu, 26 Jul 2018 11:03:51 +0800 Subject: [PATCH 10/17] update --- namespace.go | 1 + 1 file changed, 1 insertion(+) diff --git a/namespace.go b/namespace.go index 4acadc7..167a68d 100644 --- a/namespace.go +++ b/namespace.go @@ -52,6 +52,7 @@ func (n *SimpleNamespace) SetInputs(inputs map[string]interface{}) { for _, exp := range n.currentExperiments { exp.Inputs = inputs } + n.defaultExperiment.Inputs = inputs n.Inputs = inputs } From 4c3b6501cdd2c44850ac5e33efa2029b73aa5cf6 Mon Sep 17 00:00:00 2001 From: andy pan Date: Thu, 26 Jul 2018 14:15:33 +0800 Subject: [PATCH 11/17] update --- namespace.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/namespace.go b/namespace.go index 167a68d..b5f49d3 100644 --- a/namespace.go +++ b/namespace.go @@ -61,8 +61,8 @@ func (n *SimpleNamespace) Run() *Interpreter { 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.Name = n.Name + "-" + interpreter.Name + //interpreter.Salt = n.Name + "." + interpreter.Name } interpreter.Run() From e29b3b5729d9d3a8131536161c117d2fda30c692 Mon Sep 17 00:00:00 2001 From: andy pan Date: Thu, 26 Jul 2018 14:22:02 +0800 Subject: [PATCH 12/17] optimization --- random.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/random.go b/random.go index 20aac8f..7d95dcb 100644 --- a/random.go +++ b/random.go @@ -18,7 +18,7 @@ package planout import ( "crypto/sha1" - "fmt" + "encoding/hex" "strconv" ) @@ -28,7 +28,8 @@ func hash(in string) uint64 { var x [20]byte = sha1.Sum([]byte(in)) // Get the first 15 characters of the hexdigest. - var y string = fmt.Sprintf("%x", x[0:8]) + //var y string = fmt.Sprintf("%x", x[0:8]) + y := hex.EncodeToString(x[:8]) y = y[0 : len(y)-1] // Convert hex string into uint64 From 7f880e36e5aceeb185e6b761e0ea21224fad2f52 Mon Sep 17 00:00:00 2001 From: andy pan Date: Fri, 3 Aug 2018 11:36:51 +0800 Subject: [PATCH 13/17] rename fields to exported for gob encoding --- interpreter.go | 2 +- namespace.go | 68 +++++++++++++++++++++++------------------------ namespace_test.go | 12 ++++----- operators.go | 2 +- random.go | 2 +- 5 files changed, 43 insertions(+), 43 deletions(-) diff --git a/interpreter.go b/interpreter.go index b6a6f5d..e07abbc 100644 --- a/interpreter.go +++ b/interpreter.go @@ -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) { diff --git a/namespace.go b/namespace.go index b5f49d3..b572ff9 100644 --- a/namespace.go +++ b/namespace.go @@ -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 { @@ -40,27 +40,27 @@ 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, } } func (n *SimpleNamespace) SetInputs(inputs map[string]interface{}) { - for _, exp := range n.currentExperiments { + for _, exp := range n.CurrentExperiments { exp.Inputs = inputs } - n.defaultExperiment.Inputs = inputs + n.DefaultExperiment.Inputs = inputs n.Inputs = inputs } func (n *SimpleNamespace) Run() *Interpreter { - interpreter := n.defaultExperiment + interpreter := n.DefaultExperiment - if name, ok := n.segmentAllocations[n.getSegment()]; ok { - interpreter = n.currentExperiments[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 } @@ -70,46 +70,46 @@ func (n *SimpleNamespace) Run() *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) + 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 } @@ -124,8 +124,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 } @@ -141,15 +141,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 @@ -169,8 +169,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 { diff --git a/namespace_test.go b/namespace_test.go index a0119d3..61ac2d3 100644 --- a/namespace_test.go +++ b/namespace_test.go @@ -46,7 +46,7 @@ func TestSimpleNamespace(t *testing.T) { n.AddExperiment("random ops", e2, 10) n.AddExperiment("simple", e3, 80) - x := n.availableSegments + x := n.AvailableSegments seg := n.getSegment() if seg != 92 { @@ -62,7 +62,7 @@ func TestSimpleNamespace(t *testing.T) { n.RemoveExperiment("random ops") n.AddExperiment("random ops", e2, 10) - y := n.availableSegments + y := n.AvailableSegments if reflect.DeepEqual(x, y) == false { t.Errorf("Removing and re-adding experiment to a namespace resulted in mismatched allocations. X: %v, Y: %v\n", x, y) @@ -77,8 +77,8 @@ func TestSimpleNamespace(t *testing.T) { n.RemoveExperiment("random ops") n.RemoveExperiment("simple ops") n.RemoveExperiment("simple") - if len(n.availableSegments) != 100 { - t.Errorf("Expected all segments to be available. Actual %d\n", len(n.availableSegments)) + if len(n.AvailableSegments) != 100 { + t.Errorf("Expected all segments to be available. Actual %d\n", len(n.AvailableSegments)) } @@ -88,7 +88,7 @@ func TestSimpleNamespace(t *testing.T) { n.AddExperiment("random ops", e2, 10) n.AddExperiment("simple", e3, 80) interpreter = n.Run() - for _, v := range n.segmentAllocations { + for _, v := range n.SegmentAllocations { if v == "simple" { t.Log("added [simple] exp\n") } @@ -99,7 +99,7 @@ func TestSimpleNamespace(t *testing.T) { } n.RemoveExperiment("simple") interpreter = n.Run() - for _, v := range n.segmentAllocations { + for _, v := range n.SegmentAllocations { if v == "simple" { t.Error("didn't remove [simple] exp\n") } diff --git a/operators.go b/operators.go index 375b58e..322a599 100644 --- a/operators.go +++ b/operators.go @@ -102,7 +102,7 @@ type set struct{} func (s *set) execute(m map[string]interface{}, interpreter *Interpreter) interface{} { existOrPanic(m, []string{"var", "value"}, "Set") lhs := m["var"].(string) - interpreter.parameterSalt = lhs + interpreter.ParameterSalt = lhs value := interpreter.evaluate(m["value"]) interpreter.Outputs[lhs] = value return true diff --git a/random.go b/random.go index 7d95dcb..86851e5 100644 --- a/random.go +++ b/random.go @@ -74,7 +74,7 @@ func getUnit(args map[string]interface{}, interpreter *Interpreter) string { func getHash(args map[string]interface{}, interpreter *Interpreter, appended_units ...string) uint64 { unitstr := getUnit(args, interpreter) - salt := getSalt(args, interpreter.Salt, interpreter.parameterSalt) + salt := getSalt(args, interpreter.Salt, interpreter.ParameterSalt) name := generateNameToHash(unitstr, salt) if len(appended_units) > 0 { From efbe672305fb0627fb280f26bdfb7b68bef670d4 Mon Sep 17 00:00:00 2001 From: andy pan Date: Mon, 6 Aug 2018 17:20:46 +0800 Subject: [PATCH 14/17] update - --- interpreter.go | 5 +++++ namespace.go | 11 ++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/interpreter.go b/interpreter.go index e07abbc..f63caf2 100644 --- a/interpreter.go +++ b/interpreter.go @@ -54,6 +54,11 @@ func (interpreter *Interpreter) Run(force ...bool) (map[string]interface{}, bool return interpreter.Outputs, true } +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 { diff --git a/namespace.go b/namespace.go index b572ff9..d258ca8 100644 --- a/namespace.go +++ b/namespace.go @@ -48,7 +48,7 @@ func NewSimpleNamespace(name string, numSegments int, primaryUnit string, inputs } } -func (n *SimpleNamespace) SetInputs(inputs map[string]interface{}) { +func (n *SimpleNamespace) SetInputs(inputs map[string]interface{}) { for _, exp := range n.CurrentExperiments { exp.Inputs = inputs } @@ -56,6 +56,15 @@ func (n *SimpleNamespace) SetInputs(inputs map[string]interface{}) { n.Inputs = inputs } +func (n *SimpleNamespace) ReSet() { + for _, exp := range n.CurrentExperiments { + exp.ReSet() + } + n.DefaultExperiment.ReSet() + n.Inputs = make(map[string]interface{}) + n.SelectedExperiment = uint64(numSegments + 1) +} + func (n *SimpleNamespace) Run() *Interpreter { interpreter := n.DefaultExperiment From c9817c2332cd425aedb8a7d25b6ba26accbef61b Mon Sep 17 00:00:00 2001 From: andy pan Date: Mon, 6 Aug 2018 17:35:59 +0800 Subject: [PATCH 15/17] update --- namespace.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namespace.go b/namespace.go index d258ca8..1cb3c17 100644 --- a/namespace.go +++ b/namespace.go @@ -62,7 +62,7 @@ func (n *SimpleNamespace) ReSet() { } n.DefaultExperiment.ReSet() n.Inputs = make(map[string]interface{}) - n.SelectedExperiment = uint64(numSegments + 1) + n.SelectedExperiment = uint64(n.NumSegments + 1) } func (n *SimpleNamespace) Run() *Interpreter { From 2f29e5703559b1a2bf803793a85f0ea1053efdf0 Mon Sep 17 00:00:00 2001 From: andy pan Date: Mon, 6 Aug 2018 17:55:58 +0800 Subject: [PATCH 16/17] update --- namespace.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/namespace.go b/namespace.go index 1cb3c17..a5492f5 100644 --- a/namespace.go +++ b/namespace.go @@ -60,7 +60,9 @@ func (n *SimpleNamespace) ReSet() { 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) } From 6c68e95192a87dd0a06928f6966fe394dc0ce2f7 Mon Sep 17 00:00:00 2001 From: Andy Pan Date: Wed, 24 Oct 2018 11:05:17 +0800 Subject: [PATCH 17/17] update --- interpreter.go | 3 +++ namespace.go | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/interpreter.go b/interpreter.go index f63caf2..48916ce 100644 --- a/interpreter.go +++ b/interpreter.go @@ -54,7 +54,10 @@ 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 } diff --git a/namespace.go b/namespace.go index a5492f5..cab8efa 100644 --- a/namespace.go +++ b/namespace.go @@ -48,6 +48,7 @@ func NewSimpleNamespace(name string, numSegments int, primaryUnit string, inputs } } +// SetInputs方法可以手动设置namespace的输入值 func (n *SimpleNamespace) SetInputs(inputs map[string]interface{}) { for _, exp := range n.CurrentExperiments { exp.Inputs = inputs @@ -55,8 +56,9 @@ func (n *SimpleNamespace) SetInputs(inputs map[string]interface{}) { n.DefaultExperiment.Inputs = inputs n.Inputs = inputs } - +// ReSet方法重置该namespace中的实验信息 func (n *SimpleNamespace) ReSet() { + // 遍历namespace下所有experiments,进行重置 for _, exp := range n.CurrentExperiments { exp.ReSet() }