-
Notifications
You must be signed in to change notification settings - Fork 9
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
Basic workers stats #148
base: master
Are you sure you want to change the base?
Basic workers stats #148
Changes from 14 commits
e4a6b91
4c9565b
236a170
4011378
4abae05
815aed3
b458a1b
30afd3d
a278f9d
a73df5d
8966316
79e4d6c
1dcca85
a8d4997
0bb4c69
7632da4
a1f5209
cf5aa80
a2e0be3
e9a0441
e27c3b4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
//go:generate msgp --tests=false | ||
//msgp:ignore isolate.MarkedWorkerMetrics | ||
package isolate | ||
|
||
type ( | ||
NetStat struct { | ||
RxBytes uint64 `msg:"rx_bytes"` | ||
TxBytes uint64 `msg:"tx_bytes"` | ||
} | ||
|
||
WorkerMetrics struct { | ||
UptimeSec uint64 `msg:"uptime"` | ||
CpuUsageSec uint64 `msg:"cpu_usage"` | ||
|
||
CpuLoad float64 `msg:"cpu_load"` | ||
|
||
Mem uint64 `msg:"mem"` | ||
|
||
// iface -> net stat | ||
Net map[string]NetStat `msg:"net"` | ||
} | ||
|
||
MetricsResponse map[string]*WorkerMetrics | ||
|
||
MarkedWorkerMetrics struct { | ||
uuid string | ||
m *WorkerMetrics | ||
} | ||
) | ||
|
||
func NewWorkerMetrics() (c WorkerMetrics) { | ||
c.Net = make(map[string]NetStat) | ||
return | ||
} | ||
|
||
func NewMarkedMetrics(uuid string, cm *WorkerMetrics) MarkedWorkerMetrics { | ||
return MarkedWorkerMetrics{uuid: uuid, m: cm} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,14 @@ | ||
package isolate | ||
|
||
import ( | ||
"bytes" | ||
"errors" | ||
"fmt" | ||
"reflect" | ||
"strings" | ||
"sync/atomic" | ||
"syscall" | ||
"time" | ||
|
||
"golang.org/x/net/context" | ||
|
||
|
@@ -24,13 +27,22 @@ const ( | |
replySpawnWrite = 0 | ||
replySpawnError = 1 | ||
replySpawnClose = 2 | ||
|
||
workersMetrics = 2 | ||
|
||
replyMetricsOk = 0 | ||
replyMetricsError = 1 | ||
replyMetricsClose = 2 | ||
) | ||
|
||
const expectedUuidsCount = 32 | ||
|
||
var ( | ||
// ErrInvalidArgsNum should be returned if number of arguments is wrong | ||
ErrInvalidArgsNum = errors.New("invalid arguments number") | ||
_onSpoolArgsNum = uint32(reflect.TypeOf(new(initialDispatch).onSpool).NumIn()) | ||
_onSpawnArgsNum = uint32(reflect.TypeOf(new(initialDispatch).onSpawn).NumIn()) | ||
_onMetricsArgsNum = uint32(reflect.TypeOf(new(initialDispatch).onWorkersMetrics).NumIn()) | ||
) | ||
|
||
func checkSize(num uint32, r *msgp.Reader) error { | ||
|
@@ -46,6 +58,26 @@ func checkSize(num uint32, r *msgp.Reader) error { | |
return nil | ||
} | ||
|
||
func readStringsSlice(r *msgp.Reader) (uuids []string, err error) { | ||
var sz uint32 | ||
|
||
sz, err = r.ReadArrayHeader() | ||
if err != nil { | ||
return uuids, err | ||
} | ||
|
||
for i := uint32(0); i < sz; i++ { | ||
var u string | ||
if u, err = r.ReadString(); err == nil { | ||
uuids = append(uuids, u) | ||
} else { | ||
return uuids, err | ||
} | ||
} | ||
|
||
return | ||
} | ||
|
||
func readMapStrStr(r *msgp.Reader, mp map[string]string) (err error) { | ||
var sz uint32 | ||
sz, err = r.ReadMapHeader() | ||
|
@@ -84,6 +116,7 @@ func newInitialDispatch(ctx context.Context, stream ResponseStream) Dispatcher { | |
|
||
func (d *initialDispatch) Handle(id uint64, r *msgp.Reader) (Dispatcher, error) { | ||
var err error | ||
|
||
switch id { | ||
case spool: | ||
var rawProfile = newCocaineProfile() | ||
|
@@ -155,6 +188,19 @@ func (d *initialDispatch) Handle(id uint64, r *msgp.Reader) (Dispatcher, error) | |
} | ||
|
||
return d.onSpawn(rawProfile, name, executable, args, env) | ||
case workersMetrics: | ||
if err = checkSize(_onMetricsArgsNum, r); err != nil { | ||
log.G(d.ctx).Errorf("wrong args count for slot %d", id) | ||
return nil, err | ||
} | ||
|
||
var uuids []string | ||
if uuids, err = readStringsSlice(r); err != nil { | ||
log.G(d.ctx).Errorf("wrong workersMetrics request framing: %v", err) | ||
return nil, err | ||
} | ||
|
||
return d.onWorkersMetrics(uuids) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. are u sure that not There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I bet he's sure) |
||
default: | ||
return nil, fmt.Errorf("unknown transition id: %d", id) | ||
} | ||
|
@@ -268,6 +314,66 @@ func (d *initialDispatch) onSpawn(opts *cocaineProfile, name, executable string, | |
return newSpawnDispatch(d.ctx, cancelSpawn, prCh, &flagKilled, d.stream), nil | ||
} | ||
|
||
func (d *initialDispatch) onWorkersMetrics(uuidsQuery []string) (Dispatcher, error) { | ||
|
||
log.G(d.ctx).Debugf("onWorkersMetrics() Uuids query (len %d): %s", len(uuidsQuery), strings.Join(uuidsQuery, ", ")) | ||
|
||
startTime := time.Now() | ||
|
||
sendMetricsFunc := func(metrics MetricsResponse) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe better to make as a standalone function or method? |
||
var ( | ||
buf bytes.Buffer | ||
err error | ||
) | ||
|
||
if d == nil { | ||
log.G(d.ctx).Error("strange: dispatch is `nil`") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can it happen? If no - maybe remove, so we can get a crash when logic is being somehow violated? |
||
return | ||
} | ||
|
||
if err = msgp.Encode(&buf, &metrics); err != nil { | ||
log.G(d.ctx).WithError(err).Errorf("unable to encode containers metrics response: %v", err) | ||
d.stream.Error(d.ctx, replyMetricsError, errMarshallingError, err.Error()) | ||
} | ||
|
||
if err = d.stream.WriteMessage(d.ctx, replyMetricsOk, buf.Bytes()); err != nil { | ||
log.G(d.ctx).WithError(err).Errorf("unable to send containers metrics: %v", err) | ||
d.stream.Error(d.ctx, replyMetricsError, errWorkerMetricsFailed, err.Error()) | ||
} | ||
|
||
log.G(d.ctx).WithField("time", time.Since(startTime)).Debugf("Containers metrics have been sent to runtime, response length %d", len(metrics)) | ||
} | ||
|
||
go func() { | ||
// | ||
// TODO: | ||
// - reduce complexity | ||
// DONE: | ||
// - log execution time | ||
// | ||
boxes := getBoxes(d.ctx) | ||
boxesSize := len(boxes) | ||
metricsResponse := make(MetricsResponse, len(uuidsQuery)) | ||
queryResCh := make(chan []MarkedWorkerMetrics) | ||
|
||
for _, b := range boxes { | ||
go func(b Box) { | ||
queryResCh <- b.QueryMetrics(uuidsQuery) | ||
}(b) | ||
} | ||
|
||
for i := 0; i < boxesSize; i++ { | ||
for _, m := range <- queryResCh { | ||
metricsResponse[m.uuid] = m.m | ||
} | ||
} | ||
|
||
sendMetricsFunc(metricsResponse) | ||
}() | ||
|
||
return nil, nil | ||
} | ||
|
||
type OutputCollector struct { | ||
ctx context.Context | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just return?