Merge branch 'Ext-server-stats'

This commit is contained in:
tidwall 2018-11-29 15:24:32 -08:00
commit 453468cfed
1 changed files with 195 additions and 18 deletions

View File

@ -66,16 +66,44 @@ func (c *Server) cmdStats(msg *Message) (res resp.Value, err error) {
} }
return res, nil return res, nil
} }
func (c *Server) cmdServer(msg *Message) (res resp.Value, err error) { func (c *Server) cmdServer(msg *Message) (res resp.Value, err error) {
start := time.Now() start := time.Now()
m := make(map[string]interface{})
args := msg.Args[1:]
if len(msg.Args) != 1 { // Switch on the type of stats requested
switch len(args) {
case 0:
c.basicStats(m)
case 1:
if strings.ToLower(args[0]) == "ext" {
c.extStats(m)
}
default:
return NOMessage, errInvalidNumberOfArguments return NOMessage, errInvalidNumberOfArguments
} }
m := make(map[string]interface{})
switch msg.OutputType {
case JSON:
data, err := json.Marshal(m)
if err != nil {
return NOMessage, err
}
res = resp.StringValue(`{"ok":true,"stats":` + string(data) + `,"elapsed":"` + time.Since(start).String() + "\"}")
case RESP:
vals := respValuesSimpleMap(m)
res = resp.ArrayValue(vals)
}
return res, nil
}
// basicStats populates the passed map with basic system/go/tile38 statistics
func (c *Server) basicStats(m map[string]interface{}) {
m["id"] = c.config.serverID() m["id"] = c.config.serverID()
if c.config.followHost() != "" { if c.config.followHost() != "" {
m["following"] = fmt.Sprintf("%s:%d", c.config.followHost(), c.config.followPort()) m["following"] = fmt.Sprintf("%s:%d", c.config.followHost(),
c.config.followPort())
m["caught_up"] = c.fcup m["caught_up"] = c.fcup
m["caught_up_once"] = c.fcuponce m["caught_up_once"] = c.fcuponce
} }
@ -119,26 +147,175 @@ func (c *Server) cmdServer(msg *Message) (res resp.Value, err error) {
m["pointer_size"] = (32 << uintptr(uint64(^uintptr(0))>>63)) / 8 m["pointer_size"] = (32 << uintptr(uint64(^uintptr(0))>>63)) / 8
m["read_only"] = c.config.readOnly() m["read_only"] = c.config.readOnly()
m["cpus"] = runtime.NumCPU() m["cpus"] = runtime.NumCPU()
m["threads"] = runtime.GOMAXPROCS(0) n, _ := runtime.ThreadCreateProfile(nil)
m["threads"] = float64(n)
}
switch msg.OutputType { // extStats populates the passed map with extended system/go/tile38 statistics
case JSON: func (c *Server) extStats(m map[string]interface{}) {
data, err := json.Marshal(m) var mem runtime.MemStats
if err != nil { n, _ := runtime.ThreadCreateProfile(nil)
return NOMessage, err runtime.ReadMemStats(&mem)
// Go/Memory Stats
// Number of goroutines that currently exist
m["go_goroutines"] = runtime.NumGoroutine()
// Number of OS threads created
m["go_threads"] = float64(n)
// A summary of the GC invocation durations
m["go_version"] = runtime.Version()
// Number of bytes allocated and still in use
m["alloc_bytes"] = mem.Alloc
// Total number of bytes allocated, even if freed
m["alloc_bytes_total"] = mem.TotalAlloc
// Number of CPUS available on the system
m["sys_cpus"] = runtime.NumCPU()
// Number of bytes obtained from system
m["sys_bytes"] = mem.Sys
// Total number of pointer lookups
m["lookups_total"] = mem.Lookups
// Total number of mallocs
m["mallocs_total"] = mem.Mallocs
// Total number of frees
m["frees_total"] = mem.Frees
// Number of heap bytes allocated and still in use
m["heap_alloc_bytes"] = mem.HeapAlloc
// Number of heap bytes obtained from system
m["heap_sys_bytes"] = mem.HeapSys
// Number of heap bytes waiting to be used
m["heap_idle_bytes"] = mem.HeapIdle
// Number of heap bytes that are in use
m["heap_inuse_bytes"] = mem.HeapInuse
// Number of heap bytes released to OS
m["heap_released_bytes"] = mem.HeapReleased
// Number of allocated objects
m["heap_objects"] = mem.HeapObjects
// Number of bytes in use by the stack allocator
m["stack_inuse_bytes"] = mem.StackInuse
// Number of bytes obtained from system for stack allocator
m["stack_sys_bytes"] = mem.StackSys
// Number of bytes in use by mspan structures
m["mspan_inuse_bytes"] = mem.MSpanInuse
// Number of bytes used for mspan structures obtained from system
m["mspan_sys_bytes"] = mem.MSpanSys
// Number of bytes in use by mcache structures
m["mcache_inuse_bytes"] = mem.MCacheInuse
// Number of bytes used for mcache structures obtained from system
m["mcache_sys_bytes"] = mem.MCacheSys
// Number of bytes used by the profiling bucket hash table
m["buck_hash_sys_bytes"] = mem.BuckHashSys
// Number of bytes used for garbage collection system metadata
m["gc_sys_bytes"] = mem.GCSys
// Number of bytes used for other system allocations
m["other_sys_bytes"] = mem.OtherSys
// Number of heap bytes when next garbage collection will take place
m["next_gc_bytes"] = mem.NextGC
// Number of seconds since 1970 of last garbage collection
m["last_gc_time_seconds"] = float64(mem.LastGC) / 1e9
// The fraction of this program's available CPU time used by the GC since
// the program started
m["gc_cpu_fraction"] = mem.GCCPUFraction
// Tile38 Stats
// ID of the server
m["tile38_id"] = c.config.serverID()
// The process ID of the server
m["tile38_pid"] = os.Getpid()
// Version of Tile38 running
m["tile38_version"] = core.Version
// Maximum heap size allowed
m["tile38_max_heap_size"] = c.config.maxMemory()
// Type of instance running
if c.config.followHost() == "" {
m["tile38_type"] = "leader"
} else {
m["tile38_type"] = "follower"
} }
res = resp.StringValue(`{"ok":true,"stats":` + string(data) + `,"elapsed":"` + time.Now().Sub(start).String() + "\"}") // Whether or not the server is read-only
case RESP: m["tile38_read_only"] = c.config.readOnly()
vals := respValuesSimpleMap(m) // Size of pointer
res = resp.ArrayValue(vals) m["tile38_pointer_size"] = (32 << uintptr(uint64(^uintptr(0))>>63)) / 8
// Uptime of the Tile38 server in seconds
m["tile38_uptime_in_seconds"] = time.Since(c.started).Seconds()
// Number of currently connected Tile38 clients
c.connsmu.RLock()
m["tile38_connected_clients"] = len(c.conns)
c.connsmu.RUnlock()
// Whether or not a cluster is enabled
m["tile38_cluster_enabled"] = false
// Whether or not the Tile38 AOF is enabled
m["tile38_aof_enabled"] = core.AppendOnly
// Whether or not an AOF shrink is currently in progress
m["tile38_aof_rewrite_in_progress"] = c.shrinking
// Length of time the last AOF shrink took
m["tile38_aof_last_rewrite_time_sec"] = c.lastShrinkDuration.get() / int(time.Second)
// Duration of the on-going AOF rewrite operation if any
var currentShrinkStart time.Time
if currentShrinkStart.IsZero() {
m["tile38_aof_current_rewrite_time_sec"] = 0
} else {
m["tile38_aof_current_rewrite_time_sec"] = time.Since(currentShrinkStart).Seconds()
} }
return res, nil // Total size of the AOF in bytes
m["tile38_aof_size"] = c.aofsz
// Whether or no the HTTP transport is being served
m["tile38_http_transport"] = c.http
// Number of connections accepted by the server
m["tile38_total_connections_received"] = c.statsTotalConns.get()
// Number of commands processed by the server
m["tile38_total_commands_processed"] = c.statsTotalCommands.get()
// Number of key expiration events
m["tile38_expired_keys"] = c.statsExpired.get()
// Number of connected slaves
m["tile38_connected_slaves"] = len(c.aofconnM)
points := 0
objects := 0
strings := 0
c.cols.Scan(func(key string, value interface{}) bool {
col := value.(*collection.Collection)
points += col.PointCount()
objects += col.Count()
strings += col.StringCount()
return true
})
// Number of points in the database
m["tile38_num_points"] = points
// Number of objects in the database
m["tile38_num_objects"] = objects
// Number of string in the database
m["tile38_num_strings"] = strings
// Number of collections in the database
m["tile38_num_collections"] = c.cols.Len()
// Number of hooks in the database
m["tile38_num_hooks"] = len(c.hooks)
avgsz := 0
if points != 0 {
avgsz = int(mem.HeapAlloc) / points
}
// Average point size in bytes
m["tile38_avg_point_size"] = avgsz
sz := 0
c.cols.Scan(func(key string, value interface{}) bool {
col := value.(*collection.Collection)
sz += col.TotalWeight()
return true
})
// Total in memory size of all collections
m["tile38_in_memory_size"] = sz
} }
func (c *Server) writeInfoServer(w *bytes.Buffer) { func (c *Server) writeInfoServer(w *bytes.Buffer) {
fmt.Fprintf(w, "tile38_version:%s\r\n", core.Version) fmt.Fprintf(w, "tile38_version:%s\r\n", core.Version)
fmt.Fprintf(w, "redis_version:%s\r\n", core.Version) //Version of the Redis server fmt.Fprintf(w, "redis_version:%s\r\n", core.Version) // Version of the Redis server
fmt.Fprintf(w, "uptime_in_seconds:%d\r\n", time.Now().Sub(c.started)/time.Second) //Number of seconds since Redis server start fmt.Fprintf(w, "uptime_in_seconds:%d\r\n", int(time.Since(c.started).Seconds())) // Number of seconds since Redis server start
} }
func (c *Server) writeInfoClients(w *bytes.Buffer) { func (c *Server) writeInfoClients(w *bytes.Buffer) {
c.connsmu.RLock() c.connsmu.RLock()
@ -157,7 +334,7 @@ func boolInt(t bool) int {
return 0 return 0
} }
func (c *Server) writeInfoPersistence(w *bytes.Buffer) { func (c *Server) writeInfoPersistence(w *bytes.Buffer) {
fmt.Fprintf(w, "aof_enabled:1\r\n") fmt.Fprintf(w, "aof_enabled:%d\r\n", boolInt(core.AppendOnly))
fmt.Fprintf(w, "aof_rewrite_in_progress:%d\r\n", boolInt(c.shrinking)) // Flag indicating a AOF rewrite operation is on-going fmt.Fprintf(w, "aof_rewrite_in_progress:%d\r\n", boolInt(c.shrinking)) // Flag indicating a AOF rewrite operation is on-going
fmt.Fprintf(w, "aof_last_rewrite_time_sec:%d\r\n", c.lastShrinkDuration.get()/int(time.Second)) // Duration of the last AOF rewrite operation in seconds fmt.Fprintf(w, "aof_last_rewrite_time_sec:%d\r\n", c.lastShrinkDuration.get()/int(time.Second)) // Duration of the last AOF rewrite operation in seconds