6222: Add memory stats to status.json.
authorTom Clegg <tom@curoverse.com>
Tue, 7 Jul 2015 20:31:55 +0000 (16:31 -0400)
committerTom Clegg <tom@curoverse.com>
Tue, 7 Jul 2015 20:31:55 +0000 (16:31 -0400)
Reduce malloc activity in status.json.

Remove abandoned GetVolumeStatus, and move its tests over to
volume_unix_test.go.

services/keepstore/bufferpool.go
services/keepstore/handlers.go
services/keepstore/keepstore_test.go
services/keepstore/volume_unix.go
services/keepstore/volume_unix_test.go

index 373bfc75a1ca251b0bf851bfe45dfe78fc635ae7..9a3509424a3b10a1b8361c06be8475ddaa31f832 100644 (file)
@@ -3,12 +3,15 @@ package main
 import (
        "log"
        "sync"
+       "sync/atomic"
        "time"
 )
 
 type bufferPool struct {
        // limiter has a "true" placeholder for each in-use buffer.
        limiter chan bool
+       // allocated is the number of bytes currently allocated to buffers.
+       allocated uint64
        // Pool has unused buffers.
        sync.Pool
 }
@@ -16,6 +19,7 @@ type bufferPool struct {
 func newBufferPool(count int, bufSize int) *bufferPool {
        p := bufferPool{}
        p.New = func() interface{} {
+               atomic.AddUint64(&p.allocated, uint64(bufSize))
                return make([]byte, bufSize)
        }
        p.limiter = make(chan bool, count)
@@ -42,3 +46,18 @@ func (p *bufferPool) Put(buf []byte) {
        p.Pool.Put(buf)
        <-p.limiter
 }
+
+// Alloc returns the number of bytes allocated to buffers.
+func (p *bufferPool) Alloc() uint64 {
+       return atomic.LoadUint64(&p.allocated)
+}
+
+// Cap returns the maximum number of buffers allowed.
+func (p *bufferPool) Cap() int {
+       return cap(p.limiter)
+}
+
+// Len returns the number of buffers in use right now.
+func (p *bufferPool) Len() int {
+       return len(p.limiter)
+}
index 3898b55b61b7da393566930f5eeec742f24c2c85..d1169d5df9ce5549cdfd4f5a56fb095ad12cc8f6 100644 (file)
@@ -19,8 +19,9 @@ import (
        "net/http"
        "os"
        "regexp"
+       "runtime"
        "strconv"
-       "syscall"
+       "sync"
        "time"
 )
 
@@ -185,60 +186,52 @@ type VolumeStatus struct {
        BytesUsed  uint64 `json:"bytes_used"`
 }
 
+type PoolStatus struct {
+       Alloc uint64 `json:"BytesAllocated"`
+       Cap   int    `json:"BuffersMax"`
+       Len   int    `json:"BuffersInUse"`
+}
+
 type NodeStatus struct {
-       Volumes []*VolumeStatus `json:"volumes"`
+       Volumes    []*VolumeStatus  `json:"volumes"`
+       BufferPool PoolStatus
+       Memory     runtime.MemStats
 }
 
+var st NodeStatus
+var stLock sync.Mutex
 func StatusHandler(resp http.ResponseWriter, req *http.Request) {
-       st := GetNodeStatus()
-       if jstat, err := json.Marshal(st); err == nil {
+       stLock.Lock()
+       ReadNodeStatus(&st)
+       jstat, err := json.Marshal(&st)
+       stLock.Unlock()
+       if err == nil {
                resp.Write(jstat)
        } else {
                log.Printf("json.Marshal: %s\n", err)
-               log.Printf("NodeStatus = %v\n", st)
+               log.Printf("NodeStatus = %v\n", &st)
                http.Error(resp, err.Error(), 500)
        }
 }
 
-// GetNodeStatus
-//     Returns a NodeStatus struct describing this Keep
-//     node's current status.
+// ReadNodeStatus populates the given NodeStatus struct with current
+// values.
 //
-func GetNodeStatus() *NodeStatus {
-       st := new(NodeStatus)
-
-       st.Volumes = make([]*VolumeStatus, len(KeepVM.AllReadable()))
-       for i, vol := range KeepVM.AllReadable() {
-               st.Volumes[i] = vol.Status()
+func ReadNodeStatus(st *NodeStatus) {
+       vols := KeepVM.AllReadable()
+       if cap(st.Volumes) < len(vols) {
+               st.Volumes = make([]*VolumeStatus, len(vols))
        }
-       return st
-}
-
-// GetVolumeStatus
-//     Returns a VolumeStatus describing the requested volume.
-//
-func GetVolumeStatus(volume string) *VolumeStatus {
-       var fs syscall.Statfs_t
-       var devnum uint64
-
-       if fi, err := os.Stat(volume); err == nil {
-               devnum = fi.Sys().(*syscall.Stat_t).Dev
-       } else {
-               log.Printf("GetVolumeStatus: os.Stat: %s\n", err)
-               return nil
+       st.Volumes = st.Volumes[:0]
+       for _, vol := range vols {
+               if s := vol.Status(); s != nil {
+                       st.Volumes = append(st.Volumes, s)
+               }
        }
-
-       err := syscall.Statfs(volume, &fs)
-       if err != nil {
-               log.Printf("GetVolumeStatus: statfs: %s\n", err)
-               return nil
-       }
-       // These calculations match the way df calculates disk usage:
-       // "free" space is measured by fs.Bavail, but "used" space
-       // uses fs.Blocks - fs.Bfree.
-       free := fs.Bavail * uint64(fs.Bsize)
-       used := (fs.Blocks - fs.Bfree) * uint64(fs.Bsize)
-       return &VolumeStatus{volume, devnum, free, used}
+       st.BufferPool.Alloc = bufs.Alloc()
+       st.BufferPool.Cap = bufs.Cap()
+       st.BufferPool.Len = bufs.Len()
+       runtime.ReadMemStats(&st.Memory)
 }
 
 // DeleteHandler processes DELETE requests.
index 811cc70d3f75460642a0af60e8138301764b21ad..e01b01363d4e2de2f77b854ea5789fb996276234 100644 (file)
@@ -414,43 +414,6 @@ func TestIndex(t *testing.T) {
        }
 }
 
-// TestNodeStatus
-//     Test that GetNodeStatus returns valid info about available volumes.
-//
-//     TODO(twp): set up appropriate interfaces to permit more rigorous
-//     testing.
-//
-func TestNodeStatus(t *testing.T) {
-       defer teardown()
-
-       // Set up test Keep volumes with some blocks.
-       KeepVM = MakeTestVolumeManager(2)
-       defer KeepVM.Close()
-
-       vols := KeepVM.AllReadable()
-       vols[0].Put(TEST_HASH, TEST_BLOCK)
-       vols[1].Put(TEST_HASH_2, TEST_BLOCK_2)
-
-       // Get node status and make a basic sanity check.
-       st := GetNodeStatus()
-       for i := range vols {
-               volinfo := st.Volumes[i]
-               mtp := volinfo.MountPoint
-               if mtp != "/bogo" {
-                       t.Errorf("GetNodeStatus mount_point %s, expected /bogo", mtp)
-               }
-               if volinfo.DeviceNum == 0 {
-                       t.Errorf("uninitialized device_num in %v", volinfo)
-               }
-               if volinfo.BytesFree == 0 {
-                       t.Errorf("uninitialized bytes_free in %v", volinfo)
-               }
-               if volinfo.BytesUsed == 0 {
-                       t.Errorf("uninitialized bytes_used in %v", volinfo)
-               }
-       }
-}
-
 // ========================================
 // Helper functions for unit tests.
 // ========================================
index 61a98b5c736a8d3821fd0975df0d5dd5a0977a21..7c8e40cf9abe326f645b1aceb4760de832529ea3 100644 (file)
@@ -141,7 +141,7 @@ func (v *UnixVolume) Put(loc string, block []byte) error {
 }
 
 // Status returns a VolumeStatus struct describing the volume's
-// current state.
+// current state, or nil if an error occurs.
 //
 func (v *UnixVolume) Status() *VolumeStatus {
        var fs syscall.Statfs_t
index 1320d315858d83b7c84064e528a43b792ab5f19e..6bafa7c1ca03a23ff39037e95a656b27222696c9 100644 (file)
@@ -326,3 +326,23 @@ func TestIsFull(t *testing.T) {
                t.Errorf("%s: should no longer be full", v)
        }
 }
+
+func TestNodeStatus(t *testing.T) {
+       v := TempUnixVolume(t, false, false)
+       defer _teardown(v)
+
+       // Get node status and make a basic sanity check.
+       volinfo := v.Status()
+       if volinfo.MountPoint != v.root {
+               t.Errorf("GetNodeStatus mount_point %s, expected %s", volinfo.MountPoint, v.root)
+       }
+       if volinfo.DeviceNum == 0 {
+               t.Errorf("uninitialized device_num in %v", volinfo)
+       }
+       if volinfo.BytesFree == 0 {
+               t.Errorf("uninitialized bytes_free in %v", volinfo)
+       }
+       if volinfo.BytesUsed == 0 {
+               t.Errorf("uninitialized bytes_used in %v", volinfo)
+       }
+}