From 8177e64f8bfb5a962986954602c86104549888e9 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Fri, 24 Jan 2014 19:15:49 +0200 Subject: [PATCH] new histtory tests --- cmd/cgr-loader/cgr-loader.go | 6 +- engine/calldesc.go | 3 + engine/destinations.go | 5 ++ engine/history_test.go | 30 +++++---- engine/ratingplan.go | 5 ++ engine/ratingprofile.go | 5 ++ engine/storage_map.go | 11 ++- engine/storage_mongo.go | 11 ++- engine/storage_redis.go | 7 +- history/file_scribe.go | 126 +++++++++-------------------------- history/mock_scribe.go | 81 +++++----------------- history/proxy_scribe.go | 19 ++++-- history/scribe.go | 70 ++++++++++++++----- history/scribe_test.go | 36 +++++----- 14 files changed, 179 insertions(+), 236 deletions(-) diff --git a/cmd/cgr-loader/cgr-loader.go b/cmd/cgr-loader/cgr-loader.go index c835e9233..6060e552a 100644 --- a/cmd/cgr-loader/cgr-loader.go +++ b/cmd/cgr-loader/cgr-loader.go @@ -67,8 +67,8 @@ var ( stats = flag.Bool("stats", false, "Generates statsistics about given data.") fromStorDb = flag.Bool("from_stordb", false, "Load the tariff plan from storDb to dataDb") toStorDb = flag.Bool("to_stordb", false, "Import the tariff plan from files to storDb") - historyServer = flag.String("history_server", cgrConfig.HistoryServer, "The history server address:port, empty to disable automaticautomatic history archiving") - raterAddress = flag.String("rater_address", cgrConfig.MediatorRater, "Rater service to contact for cache reloads, empty to disable automatic cache reloads") + historyServer = flag.String("history_server", cgrConfig.RPCGOBListen, "The history server address:port, empty to disable automaticautomatic history archiving") + raterAddress = flag.String("rater_address", cgrConfig.RPCGOBListen, "Rater service to contact for cache reloads, empty to disable automatic cache reloads") runId = flag.String("runid", "", "Uniquely identify an import/load, postpended to some automatic fields") ) @@ -157,8 +157,8 @@ func main() { log.Fatalf("Could not connect to history server, error: %s. Make sure you have properly configured it via -history_server flag.", err.Error()) return } else { + gob.Register(engine.Destination{}) engine.SetHistoryScribe(scribeAgent) - gob.Register(&engine.Destination{}) defer scribeAgent.Client.Close() } } else { diff --git a/engine/calldesc.go b/engine/calldesc.go index 817b3ce86..16ce1fd64 100644 --- a/engine/calldesc.go +++ b/engine/calldesc.go @@ -96,6 +96,9 @@ func SetDebitPeriod(d time.Duration) { // Exported method to set the history scribe. func SetHistoryScribe(scribe history.Scribe) { + history.RegisterRecordFilename(&Destination{}) + history.RegisterRecordFilename(&RatingPlan{}) + history.RegisterRecordFilename(&RatingProfile{}) historyScribe = scribe } diff --git a/engine/destinations.go b/engine/destinations.go index e4e9162e1..819fbc2be 100644 --- a/engine/destinations.go +++ b/engine/destinations.go @@ -53,3 +53,8 @@ func (d *Destination) String() (result string) { func (d *Destination) AddPrefix(pfx string) { d.Prefixes = append(d.Prefixes, pfx) } + +// history record method +func (d *Destination) GetId() string { + return d.Id +} diff --git a/engine/history_test.go b/engine/history_test.go index a432493dd..19f8a2712 100644 --- a/engine/history_test.go +++ b/engine/history_test.go @@ -27,24 +27,26 @@ import ( func TestHistoryRatinPlans(t *testing.T) { scribe := historyScribe.(*history.MockScribe) - if !strings.Contains(scribe.RpBuf.String(), `{"Key":"*out:vdf:0:minu","Object":{"Id":"*out:vdf:0:minu","RatingPlanActivations":[{"ActivationTime":"2012-01-01T00:00:00Z","RatingPlanId":"EVENING","FallbackKeys":null}]}}`) { - t.Error("Error in destination history content:", scribe.RpBuf.String()) + buf := scribe.BufMap["ratingprofiles.json"] + if !strings.Contains(buf.String(), `{"Id":"*out:vdf:0:minu","RatingPlanActivations":[{"ActivationTime":"2012-01-01T00:00:00Z","RatingPlanId":"EVENING","FallbackKeys":null}]}`) { + t.Error("Error in destination history content:", buf.String()) } } func TestHistoryDestinations(t *testing.T) { scribe := historyScribe.(*history.MockScribe) - expected := `[{"Key":"ALL","Object":{"Id":"ALL","Prefixes":["49","41","43"]}} -{"Key":"GERMANY","Object":{"Id":"GERMANY","Prefixes":["49"]}} -{"Key":"GERMANY_O2","Object":{"Id":"GERMANY_O2","Prefixes":["41"]}} -{"Key":"GERMANY_PREMIUM","Object":{"Id":"GERMANY_PREMIUM","Prefixes":["43"]}} -{"Key":"NAT","Object":{"Id":"NAT","Prefixes":["0256","0257","0723"]}} -{"Key":"PSTN_70","Object":{"Id":"PSTN_70","Prefixes":["+4970"]}} -{"Key":"PSTN_71","Object":{"Id":"PSTN_71","Prefixes":["+4971"]}} -{"Key":"PSTN_72","Object":{"Id":"PSTN_72","Prefixes":["+4972"]}} -{"Key":"RET","Object":{"Id":"RET","Prefixes":["0723","0724"]}} -{"Key":"nat","Object":{"Id":"nat","Prefixes":["0257","0256","0723"]}}]` - if scribe.DestBuf.String() != expected { - t.Error("Error in destination history content:", scribe.DestBuf.String()) + buf := scribe.BufMap["destinations.json"] + expected := `[{"Id":"ALL","Prefixes":["49","41","43"]}, +{"Id":"GERMANY","Prefixes":["49"]}, +{"Id":"GERMANY_O2","Prefixes":["41"]}, +{"Id":"GERMANY_PREMIUM","Prefixes":["43"]}, +{"Id":"NAT","Prefixes":["0256","0257","0723"]}, +{"Id":"PSTN_70","Prefixes":["+4970"]}, +{"Id":"PSTN_71","Prefixes":["+4971"]}, +{"Id":"PSTN_72","Prefixes":["+4972"]}, +{"Id":"RET","Prefixes":["0723","0724"]}, +{"Id":"nat","Prefixes":["0257","0256","0723"]}]` + if buf.String() != expected { + t.Error("Error in destination history content:", buf.String()) } } diff --git a/engine/ratingplan.go b/engine/ratingplan.go index 5db3fc4b7..a33b9a6d5 100644 --- a/engine/ratingplan.go +++ b/engine/ratingplan.go @@ -97,3 +97,8 @@ func (rp *RatingPlan) AddRateInterval(dId string, ris ...*RateInterval) { func (rp *RatingPlan) Equal(o *RatingPlan) bool { return rp.Id == o.Id } + +// history record method +func (rp *RatingPlan) GetId() string { + return rp.Id +} diff --git a/engine/ratingprofile.go b/engine/ratingprofile.go index 869c55ed1..159e5ea2e 100644 --- a/engine/ratingprofile.go +++ b/engine/ratingprofile.go @@ -146,3 +146,8 @@ func (rp *RatingProfile) GetRatingPlansForPrefix(cd *CallDescriptor) (err error) return errors.New("not found") } + +// history record method +func (rpf *RatingProfile) GetId() string { + return rpf.Id +} diff --git a/engine/storage_map.go b/engine/storage_map.go index f4169251e..071966b36 100644 --- a/engine/storage_map.go +++ b/engine/storage_map.go @@ -22,11 +22,10 @@ import ( "errors" "fmt" - "github.com/cgrates/cgrates/cache2go" - "github.com/cgrates/cgrates/history" - "github.com/cgrates/cgrates/utils" "strings" "time" + "github.com/cgrates/cgrates/cache2go" + "github.com/cgrates/cgrates/utils" ) type MapStorage struct { @@ -123,7 +122,7 @@ func (ms *MapStorage) SetRatingPlan(rp *RatingPlan) (err error) { result, err := ms.ms.Marshal(rp) ms.dict[RATING_PLAN_PREFIX+rp.Id] = result response := 0 - go historyScribe.Record(&history.Record{RATING_PLAN_PREFIX + rp.Id, rp}, &response) + go historyScribe.Record(rp, &response) cache2go.Cache(RATING_PLAN_PREFIX+rp.Id, rp) return } @@ -151,7 +150,7 @@ func (ms *MapStorage) SetRatingProfile(rpf *RatingProfile) (err error) { result, err := ms.ms.Marshal(rpf) ms.dict[RATING_PROFILE_PREFIX+rpf.Id] = result response := 0 - go historyScribe.Record(&history.Record{RATING_PROFILE_PREFIX + rpf.Id, rpf}, &response) + go historyScribe.Record(rpf, &response) cache2go.Cache(RATING_PROFILE_PREFIX+rpf.Id, rpf) return } @@ -180,7 +179,7 @@ func (ms *MapStorage) SetDestination(dest *Destination) (err error) { result, err := ms.ms.Marshal(dest) ms.dict[DESTINATION_PREFIX+dest.Id] = result response := 0 - go historyScribe.Record(&history.Record{DESTINATION_PREFIX + dest.Id, dest}, &response) + go historyScribe.Record(dest, &response) cache2go.Cache(DESTINATION_PREFIX+dest.Id, dest) return } diff --git a/engine/storage_mongo.go b/engine/storage_mongo.go index da69f652e..6438ae9dd 100644 --- a/engine/storage_mongo.go +++ b/engine/storage_mongo.go @@ -20,11 +20,10 @@ package engine import ( "fmt" - "github.com/cgrates/cgrates/history" - "labix.org/v2/mgo" - "labix.org/v2/mgo/bson" "log" "time" + "labix.org/v2/mgo" + "labix.org/v2/mgo/bson" ) type MongoStorage struct { @@ -137,7 +136,7 @@ func (ms *MongoStorage) GetRatingPlan(key string) (rp *RatingPlan, err error) { func (ms *MongoStorage) SetRatingPlan(rp *RatingPlan) error { if historyScribe != nil { response := 0 - historyScribe.Record(&history.Record{RATING_PLAN_PREFIX + rp.Id, rp}, &response) + historyScribe.Record(rp, &response) } return ms.db.C("ratingplans").Insert(rp) } @@ -151,7 +150,7 @@ func (ms *MongoStorage) GetRatingProfile(key string) (rp *RatingProfile, err err func (ms *MongoStorage) SetRatingProfile(rp *RatingProfile) error { if historyScribe != nil { response := 0 - historyScribe.Record(&history.Record{RATING_PROFILE_PREFIX + rp.Id, rp}, &response) + historyScribe.Record(rp, &response) } return ms.db.C("ratingprofiles").Insert(rp) } @@ -168,7 +167,7 @@ func (ms *MongoStorage) GetDestination(key string) (result *Destination, err err func (ms *MongoStorage) SetDestination(dest *Destination) error { if historyScribe != nil { response := 0 - historyScribe.Record(&history.Record{DESTINATION_PREFIX + dest.Id, dest}, &response) + historyScribe.Record(dest, &response) } return ms.db.C("destinations").Insert(dest) } diff --git a/engine/storage_redis.go b/engine/storage_redis.go index cbf2fdde2..99b2adefb 100644 --- a/engine/storage_redis.go +++ b/engine/storage_redis.go @@ -25,7 +25,6 @@ import ( "fmt" "github.com/cgrates/cgrates/cache2go" - "github.com/cgrates/cgrates/history" "github.com/cgrates/cgrates/utils" "github.com/hoisie/redis" @@ -195,7 +194,7 @@ func (rs *RedisStorage) SetRatingPlan(rp *RatingPlan) (err error) { err = rs.db.Set(RATING_PLAN_PREFIX+rp.Id, b.Bytes()) if err == nil && historyScribe != nil { response := 0 - go historyScribe.Record(&history.Record{RATING_PLAN_PREFIX + rp.Id, rp}, &response) + go historyScribe.Record(rp, &response) } //cache2go.Cache(RATING_PLAN_PREFIX+rp.Id, rp) return @@ -223,7 +222,7 @@ func (rs *RedisStorage) SetRatingProfile(rpf *RatingProfile) (err error) { err = rs.db.Set(RATING_PROFILE_PREFIX+rpf.Id, result) if err == nil && historyScribe != nil { response := 0 - go historyScribe.Record(&history.Record{RATING_PROFILE_PREFIX + rpf.Id, rpf}, &response) + go historyScribe.Record(rpf, &response) } //cache2go.Cache(RATING_PROFILE_PREFIX+rpf.Id, rpf) return @@ -272,7 +271,7 @@ func (rs *RedisStorage) SetDestination(dest *Destination) (err error) { err = rs.db.Set(DESTINATION_PREFIX+dest.Id, b.Bytes()) if err == nil && historyScribe != nil { response := 0 - go historyScribe.Record(&history.Record{DESTINATION_PREFIX + dest.Id, dest}, &response) + go historyScribe.Record(dest, &response) } //cache2go.Cache(DESTINATION_PREFIX+dest.Id, dest) return diff --git a/history/file_scribe.go b/history/file_scribe.go index 0db5ec0f9..8dbe175b8 100644 --- a/history/file_scribe.go +++ b/history/file_scribe.go @@ -22,31 +22,22 @@ import ( "bufio" "encoding/json" "errors" + "fmt" "io" "os" "os/exec" "path/filepath" - "strings" "sync" "time" ) -const ( - DESTINATIONS_FILE = "destinations.json" - RATING_PLANS_FILE = "rating_plans.json" - RATING_PROFILES_FILE = "rating_profiles.json" -) - type FileScribe struct { - mu sync.Mutex - fileRoot string - gitCommand string - destinations records - ratingPlans records - ratingProfiles records - loopChecker chan int - waitingFile string - savePeriod time.Duration + mu sync.Mutex + fileRoot string + gitCommand string + loopChecker chan int + waitingFile string + savePeriod time.Duration } func NewFileScribe(fileRoot string, saveInterval time.Duration) (*FileScribe, error) { @@ -58,31 +49,20 @@ func NewFileScribe(fileRoot string, saveInterval time.Duration) (*FileScribe, er s := &FileScribe{fileRoot: fileRoot, gitCommand: gitCommand, savePeriod: saveInterval} s.loopChecker = make(chan int) s.gitInit() - if err := s.load(DESTINATIONS_FILE); err != nil { - return nil, err - } - if err := s.load(RATING_PLANS_FILE); err != nil { - return nil, err - } - if err := s.load(RATING_PROFILES_FILE); err != nil { - return nil, err + + for fn, _ := range recordsMap { + if err := s.load(fn); err != nil { + return nil, err + } } return s, nil } -func (s *FileScribe) Record(rec *Record, out *int) error { +func (s *FileScribe) Record(rec Record, out *int) error { s.mu.Lock() - var fileToSave string - switch { - case strings.HasPrefix(rec.Key, DESTINATION_PREFIX): - s.destinations = s.destinations.SetOrAdd(&Record{rec.Key[len(DESTINATION_PREFIX):], rec.Object}) - fileToSave = DESTINATIONS_FILE - case strings.HasPrefix(rec.Key, RATING_PLAN_PREFIX): - s.ratingPlans = s.ratingPlans.SetOrAdd(&Record{rec.Key[len(RATING_PLAN_PREFIX):], rec.Object}) - fileToSave = RATING_PLANS_FILE - case strings.HasPrefix(rec.Key, RATING_PROFILE_PREFIX): - s.ratingProfiles = s.ratingProfiles.SetOrAdd(&Record{rec.Key[len(RATING_PROFILE_PREFIX):], rec.Object}) - fileToSave = RATING_PROFILES_FILE + fileToSave := GetRFN(rec) + if records, ok := recordsMap[fileToSave]; ok { + records.SetOrAdd(rec) } // flood protection for save method (do not save on every loop iteration) @@ -126,21 +106,14 @@ func (s *FileScribe) gitInit() error { if out, err := cmd.Output(); err != nil { return errors.New(string(out) + " " + err.Error()) } - if f, err := os.Create(filepath.Join(s.fileRoot, DESTINATIONS_FILE)); err != nil { - return errors.New(" Error writing destinations file: " + err.Error()) - } else { - f.Close() - } - if f, err := os.Create(filepath.Join(s.fileRoot, RATING_PLANS_FILE)); err != nil { - return errors.New(" Error writing rating plans file: " + err.Error()) - } else { - f.Close() - } - if f, err := os.Create(filepath.Join(s.fileRoot, RATING_PROFILES_FILE)); err != nil { - return errors.New(" Error writing rating profiles file: " + err.Error()) - } else { - f.Close() + for fn, _ := range recordsMap { + if f, err := os.Create(filepath.Join(s.fileRoot, fn)); err != nil { + return fmt.Errorf(" Error writing %s file: %s", fn, err.Error()) + } else { + f.Close() + } } + cmd = exec.Command(s.gitCommand, "add", ".") cmd.Dir = s.fileRoot if out, err := cmd.Output(); err != nil { @@ -169,23 +142,11 @@ func (s *FileScribe) load(filename string) error { defer f.Close() d := json.NewDecoder(f) - switch filename { - case DESTINATIONS_FILE: - if err := d.Decode(&s.destinations); err != nil && err != io.EOF { - return errors.New(" Error loading destinations: " + err.Error()) - } - s.destinations.Sort() - case RATING_PLANS_FILE: - if err := d.Decode(&s.ratingPlans); err != nil && err != io.EOF { - return errors.New(" Error loading rating plans: " + err.Error()) - } - s.ratingPlans.Sort() - case RATING_PROFILES_FILE: - if err := d.Decode(&s.ratingProfiles); err != nil && err != io.EOF { - return errors.New(" Error loading rating profiles: " + err.Error()) - } - s.ratingProfiles.Sort() + records := recordsMap[filename] + if err := d.Decode(&records); err != nil && err != io.EOF { + return fmt.Errorf(" Error loading %s: %s", filename, err.Error()) } + records.Sort() return nil } @@ -198,38 +159,11 @@ func (s *FileScribe) save(filename string) error { } b := bufio.NewWriter(f) - switch filename { - case DESTINATIONS_FILE: - if err := s.format(b, s.destinations); err != nil { - return err - } - case RATING_PLANS_FILE: - if err := s.format(b, s.ratingPlans); err != nil { - return err - } - case RATING_PROFILES_FILE: - if err := s.format(b, s.ratingProfiles); err != nil { - return err - } + records := recordsMap[filename] + if err := format(b, records); err != nil { + return err } b.Flush() f.Close() return s.gitCommit() } - -func (s *FileScribe) format(b io.Writer, recs records) error { - recs.Sort() - b.Write([]byte("[")) - for i, r := range recs { - src, err := json.Marshal(r) - if err != nil { - return err - } - b.Write(src) - if i < len(recs)-1 { - b.Write([]byte(",\n")) - } - } - b.Write([]byte("]")) - return nil -} diff --git a/history/mock_scribe.go b/history/mock_scribe.go index 0f1caf649..3524340fd 100644 --- a/history/mock_scribe.go +++ b/history/mock_scribe.go @@ -21,85 +21,38 @@ package history import ( "bufio" "bytes" - "encoding/json" - "io" - "strings" "sync" ) type MockScribe struct { - mu sync.Mutex - destinations records - ratingPlans records - ratingProfiles records - DestBuf bytes.Buffer - RplBuf bytes.Buffer - RpBuf bytes.Buffer + mu sync.Mutex + BufMap map[string]*bytes.Buffer } func NewMockScribe() (*MockScribe, error) { - return &MockScribe{}, nil + return &MockScribe{BufMap: map[string]*bytes.Buffer{ + "destinations.json": bytes.NewBuffer(nil), + "ratingplans.json": bytes.NewBuffer(nil), + "ratingprofiles.json": bytes.NewBuffer(nil), + }}, nil } -func (s *MockScribe) Record(rec *Record, out *int) error { - switch { - case strings.HasPrefix(rec.Key, DESTINATION_PREFIX): - s.destinations = s.destinations.SetOrAdd(&Record{rec.Key[len(DESTINATION_PREFIX):], rec.Object}) - s.save(DESTINATIONS_FILE) - case strings.HasPrefix(rec.Key, RATING_PLAN_PREFIX): - s.ratingPlans = s.ratingPlans.SetOrAdd(&Record{rec.Key[len(RATING_PLAN_PREFIX):], rec.Object}) - s.save(RATING_PLANS_FILE) - case strings.HasPrefix(rec.Key, RATING_PROFILE_PREFIX): - s.ratingProfiles = s.ratingProfiles.SetOrAdd(&Record{rec.Key[len(RATING_PROFILE_PREFIX):], rec.Object}) - s.save(RATING_PROFILES_FILE) - } - *out = 0 +func (s *MockScribe) Record(rec Record, out *int) error { + fn := GetRFN(rec) + recordsMap[fn] = recordsMap[fn].SetOrAdd(rec) + s.save(fn) return nil } func (s *MockScribe) save(filename string) error { s.mu.Lock() defer s.mu.Unlock() - switch filename { - case DESTINATIONS_FILE: - s.DestBuf.Reset() - b := bufio.NewWriter(&s.DestBuf) - defer b.Flush() - if err := s.format(b, s.destinations); err != nil { - return err - } - case RATING_PLANS_FILE: - s.RplBuf.Reset() - b := bufio.NewWriter(&s.RplBuf) - defer b.Flush() - if err := s.format(b, s.ratingPlans); err != nil { - return err - } - case RATING_PROFILES_FILE: - s.RpBuf.Reset() - b := bufio.NewWriter(&s.RpBuf) - defer b.Flush() - if err := s.format(b, s.ratingProfiles); err != nil { - return err - } + records := recordsMap[filename] + s.BufMap[filename].Reset() + b := bufio.NewWriter(s.BufMap[filename]) + defer b.Flush() + if err := format(b, records); err != nil { + return err } - - return nil -} - -func (s *MockScribe) format(b io.Writer, recs records) error { - recs.Sort() - b.Write([]byte("[")) - for i, r := range recs { - src, err := json.Marshal(r) - if err != nil { - return err - } - b.Write(src) - if i < len(recs)-1 { - b.Write([]byte("\n")) - } - } - b.Write([]byte("]")) return nil } diff --git a/history/proxy_scribe.go b/history/proxy_scribe.go index 31fd3efb6..c7be783ea 100644 --- a/history/proxy_scribe.go +++ b/history/proxy_scribe.go @@ -18,11 +18,10 @@ along with this program. If not, see package history -import "net/rpc" - -const ( - JSON = "json" - GOB = "gob" +import ( + "encoding/gob" + "log" + "net/rpc" ) type ProxyScribe struct { @@ -38,6 +37,12 @@ func NewProxyScribe(addr string) (*ProxyScribe, error) { return &ProxyScribe{Client: client}, nil } -func (ps *ProxyScribe) Record(rec *Record, out *int) error { - return ps.Client.Call("Scribe.Record", rec, out) +func RRR(r interface{}) { + gob.Register(r) +} + +func (ps *ProxyScribe) Record(rec Record, out *int) error { + err := ps.Client.Call("Scribe.Record", &rec, out) + log.Printf("Result for %v: %v", rec, err) + return err } diff --git a/history/scribe.go b/history/scribe.go index 8391feb0e..97c8a29b1 100644 --- a/history/scribe.go +++ b/history/scribe.go @@ -19,25 +19,27 @@ along with this program. If not, see package history import ( + "encoding/json" + "io" + "reflect" "sort" -) - -const ( - RATING_PLAN_PREFIX = "rpl_" - RATING_PROFILE_PREFIX = "rpf_" - DESTINATION_PREFIX = "dst_" + "strings" ) type Scribe interface { - Record(record *Record, out *int) error + Record(Record, *int) error } -type Record struct { - Key string - Object interface{} +type Record interface { + GetId() string } -type records []*Record +type records []Record + +var ( + recordsMap = make(map[string]records) + filenameMap = make(map[reflect.Type]string) +) func (rs records) Len() int { return len(rs) @@ -48,19 +50,19 @@ func (rs records) Swap(i, j int) { } func (rs records) Less(i, j int) bool { - return rs[i].Key < rs[j].Key + return rs[i].GetId() < rs[j].GetId() } func (rs records) Sort() { sort.Sort(rs) } -func (rs records) SetOrAdd(rec *Record) records { +func (rs records) SetOrAdd(rec Record) records { //rs.Sort() n := len(rs) - i := sort.Search(n, func(i int) bool { return rs[i].Key >= rec.Key }) - if i < n && rs[i].Key == rec.Key { - rs[i].Object = rec.Object + i := sort.Search(n, func(i int) bool { return rs[i].GetId() >= rec.GetId() }) + if i < n && rs[i].GetId() == rec.GetId() { + rs[i] = rec } else { // i is the index where it would be inserted. rs = append(rs, nil) @@ -70,10 +72,10 @@ func (rs records) SetOrAdd(rec *Record) records { return rs } -func (rs records) SetOrAddOld(rec *Record) records { +/*func (rs records) SetOrAdd(rec Record) records { found := false for _, r := range rs { - if r.Key == rec.Key { + if r.GetId() == rec.GetId() { found = true r.Object = rec.Object return rs @@ -83,4 +85,36 @@ func (rs records) SetOrAddOld(rec *Record) records { rs = append(rs, rec) } return rs +}*/ + +func format(b io.Writer, recs records) error { + recs.Sort() + b.Write([]byte("[")) + for i, r := range recs { + src, err := json.Marshal(r) + if err != nil { + return err + } + b.Write(src) + if i < len(recs)-1 { + b.Write([]byte(",\n")) + } + } + b.Write([]byte("]")) + return nil } + +func GetRFN(rec Record) string { + if fn, ok := filenameMap[reflect.TypeOf(rec)]; ok { + return fn + } else { + typ := reflect.TypeOf(rec) + typeSegments := strings.Split(typ.String(), ".") + fn = strings.ToLower(typeSegments[len(typeSegments)-1]) + "s.json" + filenameMap[typ] = fn + recordsMap[fn] = make(records, 0) + return fn + } +} + +var RegisterRecordFilename = GetRFN // will create a key in filename and records map diff --git a/history/scribe_test.go b/history/scribe_test.go index 8f586dcf5..1fa264f34 100644 --- a/history/scribe_test.go +++ b/history/scribe_test.go @@ -23,18 +23,28 @@ import ( "testing" ) +type TestRecord struct { + Id string +} + +func (tr *TestRecord) GetId() string { + return tr.Id +} + func TestHistorySet(t *testing.T) { - rs := records{&Record{"first", "test"}} - rs.SetOrAdd(&Record{"first", "new value"}) - if len(rs) != 1 || rs[0].Object != "new value" { + rs := records{&TestRecord{"first"}} + second := &TestRecord{"first"} + rs.SetOrAdd(second) + if len(rs) != 1 || rs[0] != second { t.Error("error setting new value: ", rs[0]) } } func TestHistoryAdd(t *testing.T) { - rs := records{&Record{"first", "test"}} - rs = rs.SetOrAdd(&Record{"second", "new value"}) - if len(rs) != 2 || rs[1].Object != "new value" { + rs := records{&TestRecord{"first"}} + second := &TestRecord{"second"} + rs = rs.SetOrAdd(second) + if len(rs) != 2 || rs[1] != second { t.Error("error setting new value: ", rs) } } @@ -42,19 +52,9 @@ func TestHistoryAdd(t *testing.T) { func BenchmarkSetOrAdd(b *testing.B) { var rs records for i := 0; i < 1000; i++ { - rs = rs.SetOrAdd(&Record{strconv.Itoa(i), strconv.Itoa(i)}) + rs = rs.SetOrAdd(&TestRecord{strconv.Itoa(i)}) } for i := 0; i < b.N; i++ { - rs.SetOrAdd(&Record{"400", "test"}) - } -} - -func BenchmarkSetOrAddOld(b *testing.B) { - var rs records - for i := 0; i < 1000; i++ { - rs = rs.SetOrAddOld(&Record{strconv.Itoa(i), strconv.Itoa(i)}) - } - for i := 0; i < b.N; i++ { - rs.SetOrAddOld(&Record{"400", "test"}) + rs.SetOrAdd(&TestRecord{"400"}) } }