Extend /stats API to handle two routes with the same path in different apps (#735)

* Extend deprecated /stats API to handle apps and paths correctly

* More changes (bugfixes) to the JSON structure returned by the stats API call
This commit is contained in:
Nigel Deakin
2018-02-05 15:51:53 +00:00
committed by GitHub
parent ac4dfa6077
commit 5089dd6119

View File

@@ -10,62 +10,85 @@ import (
// * hot containers active
// * memory used / available
// global statistics
// stats is the top-level struct containing composite and individial statistics all routes in all apps
// an instance of this struc is maintained in memory to keep a record of the current stats since the server was started
// access must be synchronized using the Mutex
type stats struct {
mu sync.Mutex
// statistics for all functions combined
// composite statistics for all routes in all apps
queue uint64
running uint64
complete uint64
failed uint64
// statistics for individual functions, keyed by function path
functionStatsMap map[string]*functionStats
// statistics for individual apps, keyed by appname
apps map[string]appStats
}
// statistics for an individual function
type functionStats struct {
// appStats holds statistics for the routes in an individual app, keyed by the path of the route
// instances of this struc are used to maintain an in-memory record of the current stats
// access must be synchronized using the Mutex on the parent stats
type appStats struct {
routes map[string]*routeStats
}
// routeStats holds statistics for an individual route
// instances of this struc are used to maintain an in-memory record of the current stats
// access must be synchronized using the Mutex on the parent stats
type routeStats struct {
queue uint64
running uint64
complete uint64
failed uint64
}
// Stats hold the statistics for all functions combined
// and the statistics for each individual function
// Stats is the top-level struct containing composite and individial statistics all routes in all apps
// an instance of this struc is created when converting the current stats to JSON
type Stats struct {
Queue uint64
Running uint64
Complete uint64
Failed uint64
// statistics for individual functions, keyed by function path
FunctionStatsMap map[string]*FunctionStats
// statistics for individual apps, keyed by appname
Apps map[string]AppStats
}
// FunctionStats holds the statistics for an individual function
type FunctionStats struct {
// AppStats holds statistics for the routes in an individual app, keyed by the path of the route
// instances of this struc are used when converting the current stats to JSON
type AppStats struct {
Routes map[string]*RouteStats
}
// RouteStats holds statistics for an individual route
// instances of this struc are used when converting the current stats to JSON
type RouteStats struct {
Queue uint64
Running uint64
Complete uint64
Failed uint64
}
func (s *stats) getStatsForFunction(path string) *functionStats {
if s.functionStatsMap == nil {
s.functionStatsMap = make(map[string]*functionStats)
// return the stats corresponding to the specified app name and route path, creating a new stats if one does not already exist
func (s *stats) getStatsForRoute(app string, path string) *routeStats {
if s.apps == nil {
s.apps = make(map[string]appStats)
}
thisFunctionStats, found := s.functionStatsMap[path]
if !found {
thisFunctionStats = &functionStats{}
s.functionStatsMap[path] = thisFunctionStats
thisAppStats, appFound := s.apps[app]
if !appFound {
thisAppStats = appStats{routes: make(map[string]*routeStats)}
s.apps[app] = thisAppStats
}
return thisFunctionStats
thisRouteStats, pathFound := thisAppStats.routes[path]
if !pathFound {
thisRouteStats = &routeStats{}
thisAppStats.routes[path] = thisRouteStats
}
return thisRouteStats
}
func (s *stats) Enqueue(ctx context.Context, app string, path string) {
s.mu.Lock()
fstats := s.getStatsForFunction(path)
fstats := s.getStatsForRoute(app, path)
s.queue++
fstats.queue++
@@ -79,7 +102,7 @@ func (s *stats) Enqueue(ctx context.Context, app string, path string) {
func (s *stats) Dequeue(ctx context.Context, app string, path string) {
s.mu.Lock()
fstats := s.getStatsForFunction(path)
fstats := s.getStatsForRoute(app, path)
s.queue--
fstats.queue--
@@ -91,7 +114,7 @@ func (s *stats) Dequeue(ctx context.Context, app string, path string) {
func (s *stats) DequeueAndStart(ctx context.Context, app string, path string) {
s.mu.Lock()
fstats := s.getStatsForFunction(path)
fstats := s.getStatsForRoute(app, path)
s.queue--
s.running++
fstats.queue--
@@ -106,7 +129,7 @@ func (s *stats) DequeueAndStart(ctx context.Context, app string, path string) {
func (s *stats) Complete(ctx context.Context, app string, path string) {
s.mu.Lock()
fstats := s.getStatsForFunction(path)
fstats := s.getStatsForRoute(app, path)
s.running--
s.complete++
fstats.running--
@@ -121,7 +144,7 @@ func (s *stats) Complete(ctx context.Context, app string, path string) {
func (s *stats) Failed(ctx context.Context, app string, path string) {
s.mu.Lock()
fstats := s.getStatsForFunction(path)
fstats := s.getStatsForRoute(app, path)
s.running--
s.failed++
fstats.running--
@@ -136,7 +159,7 @@ func (s *stats) Failed(ctx context.Context, app string, path string) {
func (s *stats) DequeueAndFail(ctx context.Context, app string, path string) {
s.mu.Lock()
fstats := s.getStatsForFunction(path)
fstats := s.getStatsForRoute(app, path)
s.queue--
s.failed++
fstats.queue--
@@ -161,16 +184,22 @@ func IncrementTooBusy(ctx context.Context) {
}
func (s *stats) Stats() Stats {
// this creates a Stats from a stats
// stats is the internal struc which is continuously updated, and access is controlled using its Mutex
// Stats is a deep copy for external use, and can be converted to JSON
var stats Stats
s.mu.Lock()
stats.Running = s.running
stats.Complete = s.complete
stats.Queue = s.queue
stats.Failed = s.failed
stats.FunctionStatsMap = make(map[string]*FunctionStats)
for key, value := range s.functionStatsMap {
thisFunctionStats := &FunctionStats{Queue: value.queue, Running: value.running, Complete: value.complete, Failed: value.failed}
stats.FunctionStatsMap[key] = thisFunctionStats
stats.Apps = make(map[string]AppStats)
for appname, thisAppStats := range s.apps {
newAppStats := AppStats{Routes: make(map[string]*RouteStats)}
stats.Apps[appname] = newAppStats
for path, thisRouteStats := range thisAppStats.routes {
newAppStats.Routes[path] = &RouteStats{Queue: thisRouteStats.queue, Running: thisRouteStats.running, Complete: thisRouteStats.complete, Failed: thisRouteStats.failed}
}
}
s.mu.Unlock()
return stats