Skip to content

Commit

Permalink
performance monitor package (#2046)
Browse files Browse the repository at this point in the history
* init performance monitor package

* handle new rmb call

* resolve comments:
- introduce Task interface
- remove testing code
- use redis connection from pool
- unexpose some methods and fields
- update running zos node docs & add example for using perf

* resolve comments:
- use redis pool correctly
- add GetAll method
- better naming for tasks with prefix `perf.`
- use unix date

* update go.mod file

* - add new handler for getAll method
- reuse Get code

* run tasks if it has no results stored, fix getAll method

* run the tasks in separate routines and use the redis pool pointer

* removing the wg and log the errors in the routine
  • Loading branch information
Omarabdul3ziz authored Sep 26, 2023
1 parent aa6efe6 commit 4944c63
Show file tree
Hide file tree
Showing 11 changed files with 317 additions and 4 deletions.
25 changes: 25 additions & 0 deletions cmds/modules/noded/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package noded
import (
"context"
"crypto/ed25519"
"encoding/json"
"fmt"
"os/exec"
"strings"
Expand All @@ -19,6 +20,7 @@ import (
"github.com/threefoldtech/zos/pkg/environment"
"github.com/threefoldtech/zos/pkg/events"
"github.com/threefoldtech/zos/pkg/monitord"
"github.com/threefoldtech/zos/pkg/perf"
"github.com/threefoldtech/zos/pkg/registrar"
"github.com/threefoldtech/zos/pkg/stubs"
"github.com/threefoldtech/zos/pkg/utils"
Expand Down Expand Up @@ -193,6 +195,29 @@ func action(cli *cli.Context) error {
return hypervisor, nil
})

log.Info().Msg("start perf scheduler")

perfMon, err := perf.NewPerformanceMonitor(msgBrokerCon)
if err != nil {
return errors.Wrap(err, "failed to create a new perfMon")
}

if err = perfMon.Run(ctx); err != nil {
return errors.Wrap(err, "failed to run the scheduler")
}
bus.WithHandler("zos.perf.get", func(ctx context.Context, payload []byte) (interface{}, error) {
var taskName string
err := json.Unmarshal(payload, &taskName)
if err != nil {
return nil, errors.Wrapf(err, "failed to unmarshal payload: %v", payload)
}

return perfMon.Get(taskName)
})
bus.WithHandler("zos.perf.get_all", func(ctx context.Context, payload []byte) (interface{}, error) {
return perfMon.GetAll()
})

// answer calls for dmi
go func() {
if err := bus.Run(ctx); err != nil {
Expand Down
3 changes: 2 additions & 1 deletion docs/development/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
- [Starting a local zos node](#starting-a-local-zos-node)
- [Accessing node](#accessing-node)
- [Development](#development)
- [Qemu docs](../../qemu/README.md)

## Starting a local zos node

Expand All @@ -19,7 +20,7 @@ Then, inside zos repository
make -C cmds
cd qemu
mv <downloaded image path> ./zos.efi
sudo ./vm.sh -n myzos-01 -c "farmer_id=<your farm id here> printk.devmsg=on runmode=dev"
sudo ./vm.sh -n node-01 -c "farmer_id=<your farm id here> printk.devmsg=on runmode=dev"
```

You should see the qemu console and boot logs, wait for awhile and you can [browse farms](https://dashboard.dev.grid.tf/explorer/farms) to see your node is added/detected automatically.
Expand Down
5 changes: 4 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ require (
github.com/diskfs/go-diskfs v1.2.0
github.com/fsnotify/fsnotify v1.6.0
github.com/g0rbe/go-chattr v0.0.0-20190906133247-aa435a6a0a37
github.com/garyburd/redigo v1.6.2
github.com/gizak/termui/v3 v3.1.0
github.com/go-co-op/gocron v1.33.1
github.com/go-redis/redis v6.15.9+incompatible
github.com/gomodule/redigo v1.8.9
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
Expand Down Expand Up @@ -74,7 +76,6 @@ require (
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect
github.com/ethereum/go-ethereum v1.11.6 // indirect
github.com/garyburd/redigo v1.6.2 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-stack/stack v1.8.1 // indirect
github.com/goccy/go-json v0.4.8 // indirect
Expand Down Expand Up @@ -121,6 +122,7 @@ require (
github.com/pkg/xattr v0.4.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/robfig/cron/v3 v3.0.1 // indirect
github.com/rs/cors v1.9.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/safchain/ethtool v0.0.0-20201023143004-874930cb3ce0 // indirect
Expand All @@ -136,6 +138,7 @@ require (
github.com/xxtea/xxtea-go v0.0.0-20170828040851-35c4b17eecf6 // indirect
github.com/yusufpapurcu/wmi v1.2.2 // indirect
go.opencensus.io v0.23.0 // indirect
go.uber.org/atomic v1.9.0 // indirect
golang.org/x/net v0.14.0 // indirect
golang.org/x/sync v0.3.0 // indirect
golang.org/x/text v0.12.0 // indirect
Expand Down
11 changes: 11 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:ma
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ=
github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s=
Expand Down Expand Up @@ -156,6 +157,8 @@ github.com/garyburd/redigo v1.6.2/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gizak/termui/v3 v3.1.0 h1:ZZmVDgwHl7gR7elfKf1xc4IudXZ5qqfDh4wExk4Iajc=
github.com/gizak/termui/v3 v3.1.0/go.mod h1:bXQEBkJpzxUAKf0+xq9MSWAvWZlE7c+aidmyFlkYTrY=
github.com/go-co-op/gocron v1.33.1 h1:wjX+Dg6Ae29a/f9BSQjY1Rl+jflTpW9aDyMqseCj78c=
github.com/go-co-op/gocron v1.33.1/go.mod h1:NLi+bkm4rRSy1F8U7iacZOz0xPseMoIOnvabGoSe/no=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
Expand Down Expand Up @@ -292,6 +295,7 @@ github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFB
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
Expand Down Expand Up @@ -413,6 +417,7 @@ github.com/pierrec/lz4 v2.3.0+incompatible h1:CZzRn4Ut9GbUkHlQ7jqBXeZQV41ZSKWFc3
github.com/pierrec/lz4 v2.3.0+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pierrec/xxHash v0.1.5 h1:n/jBpwTHiER4xYvK3/CdPVnLDPchj8eTJFFLUb4QHBo=
github.com/pierrec/xxHash v0.1.5/go.mod h1:w2waW5Zoa/Wc4Yqe0wgrIYAGKqRMf7czn2HNKXmuL+I=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
Expand All @@ -437,9 +442,13 @@ github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40T
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rs/cors v1.9.0 h1:l9HGsTsHJcvW14Nk7J9KFz8bzeAWXn3CG6bgt7LsrAE=
Expand Down Expand Up @@ -558,6 +567,8 @@ go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
Expand Down
39 changes: 39 additions & 0 deletions pkg/perf/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Perf

Perf is a performance test module that is scheduled to run and cache those tests results in redis which can be retrieved later over RMB call.

Perf tests are monitored by `noded` service from zos modules.

### Usage Example

1. Create a task that implement `Task` interface

```go
type LoggingTask struct {
TaskID string
Schedule string // should be in cron syntax with seconds ("* 0 * * * *")
}

func (t *LoggingTask) ID() string {
return t.TaskID
}

func (t *LoggingTask) Cron() string {
return t.Schedule
}

// a simple task that returns a string with the current time
func (t *LoggingTask) Run(ctx context.Context) (interface{}, error) {
result := fmt.Sprintf("time is: %v", time.Now())
return result, nil
}
```

2. Add the created task to scheduler

```go
perfMon.AddTask(&perf.LoggingTask{
TaskID: "LoggingTask",
Schedule: "* 0 * * * *", // when minutes is 0 (aka: every hour)
})
```
121 changes: 121 additions & 0 deletions pkg/perf/cache.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package perf

import (
"context"
"encoding/json"
"fmt"

"github.com/garyburd/redigo/redis"
"github.com/pkg/errors"
)

const (
moduleName = "perf"
)

var (
ErrResultNotFound = errors.New("result not found")
)

// TaskResult the result test schema
type TaskResult struct {
Name string `json:"name"`
Timestamp uint64 `json:"timestamp"`
Result interface{} `json:"result"`
}

// generateKey is helper method to add moduleName as prefix for the taskName
func generateKey(taskName string) string {
return fmt.Sprintf("%s.%s", moduleName, taskName)
}

// setCache set result in redis
func (pm *PerformanceMonitor) setCache(ctx context.Context, result TaskResult) error {
data, err := json.Marshal(result)
if err != nil {
return errors.Wrap(err, "failed to marshal data to JSON")
}

conn := pm.pool.Get()
defer conn.Close()

_, err = conn.Do("SET", generateKey(result.Name), data)
return err
}

// get directly gets result for some key
func get(conn redis.Conn, key string) (TaskResult, error) {
var res TaskResult

data, err := conn.Do("GET", key)
if err != nil {
return res, errors.Wrap(err, "failed to get the result")
}

if data == nil {
return res, ErrResultNotFound
}

err = json.Unmarshal(data.([]byte), &res)
if err != nil {
return res, errors.Wrap(err, "failed to unmarshal data from json")
}

return res, nil
}

// Get gets data from redis
func (pm *PerformanceMonitor) Get(taskName string) (TaskResult, error) {
conn := pm.pool.Get()
defer conn.Close()
return get(conn, generateKey(taskName))
}

// GetAll gets the results for all the tests with moduleName as prefix
func (pm *PerformanceMonitor) GetAll() ([]TaskResult, error) {
var res []TaskResult

conn := pm.pool.Get()
defer conn.Close()

var keys []string

cursor := 0
for {
values, err := redis.Values(conn.Do("SCAN", cursor, "MATCH", generateKey("*")))
if err != nil {
return nil, err
}

_, err = redis.Scan(values, &cursor, &keys)
if err != nil {
return nil, err
}

for _, key := range keys {
result, err := get(conn, key)
if err != nil {
continue
}
res = append(res, result)
}

if cursor == 0 {
break
}

}
return res, nil
}

// exists check if a key exists
func (pm *PerformanceMonitor) exists(key string) (bool, error) {
conn := pm.pool.Get()
defer conn.Close()

ok, err := redis.Bool(conn.Do("EXISTS", generateKey(key)))
if err != nil {
return false, errors.Wrapf(err, "error checking if key %s exists", generateKey(key))
}
return ok, nil
}
Loading

0 comments on commit 4944c63

Please sign in to comment.