From 73c54cdcf2c81521b2f5f02bd99649c1d4e6017c Mon Sep 17 00:00:00 2001 From: ionutboangiu Date: Tue, 16 Apr 2024 18:35:33 +0300 Subject: [PATCH] Add test & benchmarks for msgpack library update Check test comment for complete steps. --- engine/msgpack_compat_test.go | 269 ++++++++++++++++++++++++++++++++++ go.mod | 1 + 2 files changed, 270 insertions(+) create mode 100644 engine/msgpack_compat_test.go diff --git a/engine/msgpack_compat_test.go b/engine/msgpack_compat_test.go new file mode 100644 index 000000000..2d1ddf9d9 --- /dev/null +++ b/engine/msgpack_compat_test.go @@ -0,0 +1,269 @@ +//go:build benchmark + +/* +Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments +Copyright (C) ITsysCOM GmbH + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ + +package engine + +import ( + "testing" + "time" + + "github.com/cgrates/cgrates/config" + "github.com/cgrates/cgrates/utils" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" +) + +// TestMsgPackBackwardsCompatibility verifies whether the msgpack library update is backwards-incompatible +// even with the TimeNotBuiltin legacy option set to true (see https://github.com/ugorji/go/issues/269). +// To use: +// - switch to a previous commit, that's using the original library +// - checkout this file +// - execute the test with the action variable set to 'SET' +// - return on the master branch and execute the test with action == 'GET'. +// +// If the 'GET' test passes, we can assume it's backwards-compatible. +// +// List of types that could be affected by the library update: +// +// - RatingProfile +// - Action +// - Account +// - utils.LoadInstance +// - ActionTrigger +// - ActionPlan +// - ResourceProfile +// - Resource +// - StatQueueProfile +// - StatQueue +// - ThresholdProfile +// - Threshold +// - Filter +// - RouteProfile +// - AttributeProfile +// - ChargerProfile +// - DispatcherProfile +func TestMsgPackBackwardsCompatibility(t *testing.T) { + t.SkipNow() + // Define reference dates. + currentDate := time.Now() + refTime := time.Date(currentDate.Year(), currentDate.Month(), 16, 14, 20, 33, 123456789, time.UTC) + refTimeNextYear := refTime.AddDate(1, 0, 0) + + // Create the profiles. Use them to set on 'SET' action or to validate what + // we receive on 'GET' action. + ratingPrf := RatingProfile{ + Id: "ratingPrf1", + RatingPlanActivations: RatingPlanActivations{ + { + ActivationTime: refTime, + RatingPlanId: "ratingPlan1", + }, + }, + } + actionsKey := "Actions1" + actions := Actions{ + { + Id: "action1", + ActionType: "*log", + ExtraParameters: "{\"Field\":\"Value\"}", + Filters: []string{"FLTR_1", "FLTR_2"}, + ExpirationString: "1month", + Weight: 10, + Balance: &BalanceFilter{ + ID: utils.StringPointer("balance_1"), + Type: utils.StringPointer("*monetary"), + Value: &utils.ValueFormula{ + Static: 15, + }, + ExpirationDate: &refTimeNextYear, + }, + }, + } + account := Account{ + ID: "1001", + BalanceMap: map[string]Balances{ + "*monetary": { + { + ID: "balance_1", + Value: 15, + ExpirationDate: refTimeNextYear, + }, + }, + }, + + // Might want to ignore this field as it's possible + // to get updated on its own. + UpdateTime: refTime, + } + fltr := Filter{ + Tenant: "cgrates.org", + ID: "FLTR_1", + Rules: []*FilterRule{ + { + Type: utils.MetaString, + Element: "~*req.Account", + Values: []string{"1001"}, + }, + }, + ActivationInterval: &utils.ActivationInterval{ + ActivationTime: refTime, + ExpiryTime: refTimeNextYear, + }, + } + + cfg := config.NewDefaultCGRConfig() // datadb opts has redis defaults + db, err := NewDataDBConn(cfg.DataDbCfg().Type, + cfg.DataDbCfg().Host, cfg.DataDbCfg().Port, + cfg.DataDbCfg().Name, cfg.DataDbCfg().User, + cfg.DataDbCfg().Password, cfg.GeneralCfg().DBDataEncoding, + cfg.DataDbCfg().Opts, cfg.DataDbCfg().Items) + if err != nil { + t.Fatalf("failed to establish connection to redis: %v", err) + } + + // Decide whether to set or get the profiles by manually + // changing the following variable (SET|GET). + action := "GET" + + switch action { + case "SET": + err = db.SetRatingProfileDrv(&ratingPrf) + if err != nil { + t.Errorf("SetRatingProfileDrv(%v) failed unexpectedly: %v", ratingPrf, err) + } + err = db.SetActionsDrv(actionsKey, actions) + if err != nil { + t.Errorf("SetActionsDrv(%q,%v) failed unexpectedly: %v", actionsKey, actions, err) + } + err = db.SetAccountDrv(&account) + if err != nil { + t.Errorf("SetAccountDrv(%v) failed unexpectedly: %v", account, err) + } + err = db.SetFilterDrv(&fltr) + if err != nil { + t.Errorf("SetFilterDrv(%v) failed unexpectedly: %v", fltr, err) + } + case "GET": + if got, err := db.GetRatingProfileDrv(ratingPrf.Id); err != nil { + t.Errorf("GetRatingProfileDrv(%q) failed unexpectedly: %v", ratingPrf.Id, err) + } else if diff := cmp.Diff(&ratingPrf, got); diff != "" { + t.Errorf("GetRatingProfileDrv(%q) returned unexpected profile (-want +got): \n%s", ratingPrf.Id, diff) + } + + if got, err := db.GetActionsDrv(actionsKey); err != nil { + t.Errorf("GetActionsDrv(%q) failed unexpectedly: %v", actionsKey, err) + } else if diff := cmp.Diff(actions, got, cmpopts.IgnoreUnexported(Action{})); diff != "" { + t.Errorf("GetActionsDrv(%q) returned unexpected profile (-want +got): \n%s", ratingPrf.Id, diff) + } + + if got, err := db.GetAccountDrv(account.ID); err != nil { + t.Errorf("GetAccountDrv(%q) failed unexpectedly: %v", account.ID, err) + } else if diff := cmp.Diff(&account, got, cmpopts.IgnoreUnexported(Account{})); diff != "" { + t.Errorf("GetAccountDrv(%q) returned unexpected profile (-want +got): \n%s", ratingPrf.Id, diff) + } + + if got, err := db.GetFilterDrv(fltr.Tenant, fltr.ID); err != nil { + t.Errorf("GetFilterDrv(%q,%q) failed unexpectedly: %v", fltr.Tenant, fltr.ID, err) + } else if diff := cmp.Diff(&fltr, got, cmpopts.IgnoreUnexported(FilterRule{})); diff != "" { + t.Errorf("GetFilterDrv(%q) returned unexpected profile (-want +got): \n%s", ratingPrf.Id, diff) + } + } +} + +func BenchmarkMsgpackTime(b *testing.B) { + type NestedStructWithTime struct { + ID string + TimeField time.Time + TimePointerField *time.Time + } + type StructWithTime struct { + ID string + TimeField time.Time + StructField NestedStructWithTime + } + refTime := time.Now() + obj := StructWithTime{ + ID: "structWithTime", + TimeField: time.Now(), + StructField: NestedStructWithTime{ + ID: "nestedStructWithTime", + TimeField: time.Now(), + TimePointerField: &refTime, + }, + } + ms, err := NewMarshaler(utils.MsgPack) + if err != nil { + b.Fatal(err) + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + byt, err := ms.Marshal(obj) + if err != nil { + b.Fatal(err) + } + var s StructWithTime + if err := ms.Unmarshal(byt, &s); err != nil { + b.Fatal(err) + } + } +} + +/* +old version (github.com/cgrates/ugocodec): + + go test -tags=benchmark -bench=BenchmarkMsgpack -run=^$ -benchtime=1s -count=5 -benchmem + goos: linux + goarch: amd64 + pkg: github.com/cgrates/cgrates/general_tests + cpu: 12th Gen Intel(R) Core(TM) i7-1265U + BenchmarkMsgpack-3 652118 1976 ns/op 2952 B/op 18 allocs/op + BenchmarkMsgpack-3 565254 1890 ns/op 2952 B/op 18 allocs/op + BenchmarkMsgpack-3 640384 1841 ns/op 2952 B/op 18 allocs/op + BenchmarkMsgpack-3 663924 1855 ns/op 2952 B/op 18 allocs/op + BenchmarkMsgpack-3 641320 1834 ns/op 2952 B/op 18 allocs/op + + +new version with legacy option TimeNotBuiltin set to true (current implementation): + + go test -tags=benchmark -bench=BenchmarkMsgpack -run=^$ -benchtime=1s -count=5 -benchmem + goos: linux + goarch: amd64 + pkg: github.com/cgrates/cgrates/general_tests + cpu: 12th Gen Intel(R) Core(TM) i7-1265U + BenchmarkMsgpack-3 914578 1356 ns/op 2120 B/op 12 allocs/op + BenchmarkMsgpack-3 901622 1340 ns/op 2120 B/op 12 allocs/op + BenchmarkMsgpack-3 899760 1339 ns/op 2120 B/op 12 allocs/op + BenchmarkMsgpack-3 899694 1335 ns/op 2120 B/op 12 allocs/op + BenchmarkMsgpack-3 891693 1344 ns/op 2120 B/op 12 allocs/op + +new version default: + + go test -tags=benchmark -bench=BenchmarkMsgpack -run=^$ -benchtime=1s -count=5 -benchmem + goos: linux + goarch: amd64 + pkg: github.com/cgrates/cgrates/general_tests + cpu: 12th Gen Intel(R) Core(TM) i7-1265U + BenchmarkMsgpack-3 980895 1180 ns/op 2072 B/op 9 allocs/op + BenchmarkMsgpack-3 978346 1181 ns/op 2072 B/op 9 allocs/op + BenchmarkMsgpack-3 1008806 1209 ns/op 2072 B/op 9 allocs/op + BenchmarkMsgpack-3 1013218 1192 ns/op 2072 B/op 9 allocs/op + BenchmarkMsgpack-3 995960 1282 ns/op 2072 B/op 9 allocs/op + +*/ diff --git a/go.mod b/go.mod index 3d7a1bd17..81a570481 100644 --- a/go.mod +++ b/go.mod @@ -34,6 +34,7 @@ require ( github.com/fiorix/go-diameter/v4 v4.0.4 github.com/fsnotify/fsnotify v1.7.0 github.com/go-sql-driver/mysql v1.7.1 + github.com/google/go-cmp v0.6.0 github.com/gorhill/cronexpr v0.0.0-20180427100037-88b0669f7d75 github.com/mediocregopher/radix/v3 v3.8.1 github.com/miekg/dns v1.1.56