aboutsummaryrefslogtreecommitdiff
path: root/vendor/google.golang.org/grpc/internal/channelz
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/google.golang.org/grpc/internal/channelz')
-rw-r--r--vendor/google.golang.org/grpc/internal/channelz/funcs.go573
-rw-r--r--vendor/google.golang.org/grpc/internal/channelz/types.go418
2 files changed, 991 insertions, 0 deletions
diff --git a/vendor/google.golang.org/grpc/internal/channelz/funcs.go b/vendor/google.golang.org/grpc/internal/channelz/funcs.go
new file mode 100644
index 0000000..586a033
--- /dev/null
+++ b/vendor/google.golang.org/grpc/internal/channelz/funcs.go
@@ -0,0 +1,573 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// Package channelz defines APIs for enabling channelz service, entry
+// registration/deletion, and accessing channelz data. It also defines channelz
+// metric struct formats.
+//
+// All APIs in this package are experimental.
+package channelz
+
+import (
+ "sort"
+ "sync"
+ "sync/atomic"
+
+ "google.golang.org/grpc/grpclog"
+)
+
+var (
+ db dbWrapper
+ idGen idGenerator
+ // EntryPerPage defines the number of channelz entries to be shown on a web page.
+ EntryPerPage = 50
+ curState int32
+)
+
+// TurnOn turns on channelz data collection.
+func TurnOn() {
+ if !IsOn() {
+ NewChannelzStorage()
+ atomic.StoreInt32(&curState, 1)
+ }
+}
+
+// IsOn returns whether channelz data collection is on.
+func IsOn() bool {
+ return atomic.CompareAndSwapInt32(&curState, 1, 1)
+}
+
+// dbWarpper wraps around a reference to internal channelz data storage, and
+// provide synchronized functionality to set and get the reference.
+type dbWrapper struct {
+ mu sync.RWMutex
+ DB *channelMap
+}
+
+func (d *dbWrapper) set(db *channelMap) {
+ d.mu.Lock()
+ d.DB = db
+ d.mu.Unlock()
+}
+
+func (d *dbWrapper) get() *channelMap {
+ d.mu.RLock()
+ defer d.mu.RUnlock()
+ return d.DB
+}
+
+// NewChannelzStorage initializes channelz data storage and id generator.
+//
+// Note: This function is exported for testing purpose only. User should not call
+// it in most cases.
+func NewChannelzStorage() {
+ db.set(&channelMap{
+ topLevelChannels: make(map[int64]struct{}),
+ channels: make(map[int64]*channel),
+ listenSockets: make(map[int64]*listenSocket),
+ normalSockets: make(map[int64]*normalSocket),
+ servers: make(map[int64]*server),
+ subChannels: make(map[int64]*subChannel),
+ })
+ idGen.reset()
+}
+
+// GetTopChannels returns a slice of top channel's ChannelMetric, along with a
+// boolean indicating whether there's more top channels to be queried for.
+//
+// The arg id specifies that only top channel with id at or above it will be included
+// in the result. The returned slice is up to a length of EntryPerPage, and is
+// sorted in ascending id order.
+func GetTopChannels(id int64) ([]*ChannelMetric, bool) {
+ return db.get().GetTopChannels(id)
+}
+
+// GetServers returns a slice of server's ServerMetric, along with a
+// boolean indicating whether there's more servers to be queried for.
+//
+// The arg id specifies that only server with id at or above it will be included
+// in the result. The returned slice is up to a length of EntryPerPage, and is
+// sorted in ascending id order.
+func GetServers(id int64) ([]*ServerMetric, bool) {
+ return db.get().GetServers(id)
+}
+
+// GetServerSockets returns a slice of server's (identified by id) normal socket's
+// SocketMetric, along with a boolean indicating whether there's more sockets to
+// be queried for.
+//
+// The arg startID specifies that only sockets with id at or above it will be
+// included in the result. The returned slice is up to a length of EntryPerPage,
+// and is sorted in ascending id order.
+func GetServerSockets(id int64, startID int64) ([]*SocketMetric, bool) {
+ return db.get().GetServerSockets(id, startID)
+}
+
+// GetChannel returns the ChannelMetric for the channel (identified by id).
+func GetChannel(id int64) *ChannelMetric {
+ return db.get().GetChannel(id)
+}
+
+// GetSubChannel returns the SubChannelMetric for the subchannel (identified by id).
+func GetSubChannel(id int64) *SubChannelMetric {
+ return db.get().GetSubChannel(id)
+}
+
+// GetSocket returns the SocketInternalMetric for the socket (identified by id).
+func GetSocket(id int64) *SocketMetric {
+ return db.get().GetSocket(id)
+}
+
+// RegisterChannel registers the given channel c in channelz database with ref
+// as its reference name, and add it to the child list of its parent (identified
+// by pid). pid = 0 means no parent. It returns the unique channelz tracking id
+// assigned to this channel.
+func RegisterChannel(c Channel, pid int64, ref string) int64 {
+ id := idGen.genID()
+ cn := &channel{
+ refName: ref,
+ c: c,
+ subChans: make(map[int64]string),
+ nestedChans: make(map[int64]string),
+ id: id,
+ pid: pid,
+ }
+ if pid == 0 {
+ db.get().addChannel(id, cn, true, pid, ref)
+ } else {
+ db.get().addChannel(id, cn, false, pid, ref)
+ }
+ return id
+}
+
+// RegisterSubChannel registers the given channel c in channelz database with ref
+// as its reference name, and add it to the child list of its parent (identified
+// by pid). It returns the unique channelz tracking id assigned to this subchannel.
+func RegisterSubChannel(c Channel, pid int64, ref string) int64 {
+ if pid == 0 {
+ grpclog.Error("a SubChannel's parent id cannot be 0")
+ return 0
+ }
+ id := idGen.genID()
+ sc := &subChannel{
+ refName: ref,
+ c: c,
+ sockets: make(map[int64]string),
+ id: id,
+ pid: pid,
+ }
+ db.get().addSubChannel(id, sc, pid, ref)
+ return id
+}
+
+// RegisterServer registers the given server s in channelz database. It returns
+// the unique channelz tracking id assigned to this server.
+func RegisterServer(s Server, ref string) int64 {
+ id := idGen.genID()
+ svr := &server{
+ refName: ref,
+ s: s,
+ sockets: make(map[int64]string),
+ listenSockets: make(map[int64]string),
+ id: id,
+ }
+ db.get().addServer(id, svr)
+ return id
+}
+
+// RegisterListenSocket registers the given listen socket s in channelz database
+// with ref as its reference name, and add it to the child list of its parent
+// (identified by pid). It returns the unique channelz tracking id assigned to
+// this listen socket.
+func RegisterListenSocket(s Socket, pid int64, ref string) int64 {
+ if pid == 0 {
+ grpclog.Error("a ListenSocket's parent id cannot be 0")
+ return 0
+ }
+ id := idGen.genID()
+ ls := &listenSocket{refName: ref, s: s, id: id, pid: pid}
+ db.get().addListenSocket(id, ls, pid, ref)
+ return id
+}
+
+// RegisterNormalSocket registers the given normal socket s in channelz database
+// with ref as its reference name, and add it to the child list of its parent
+// (identified by pid). It returns the unique channelz tracking id assigned to
+// this normal socket.
+func RegisterNormalSocket(s Socket, pid int64, ref string) int64 {
+ if pid == 0 {
+ grpclog.Error("a NormalSocket's parent id cannot be 0")
+ return 0
+ }
+ id := idGen.genID()
+ ns := &normalSocket{refName: ref, s: s, id: id, pid: pid}
+ db.get().addNormalSocket(id, ns, pid, ref)
+ return id
+}
+
+// RemoveEntry removes an entry with unique channelz trakcing id to be id from
+// channelz database.
+func RemoveEntry(id int64) {
+ db.get().removeEntry(id)
+}
+
+// channelMap is the storage data structure for channelz.
+// Methods of channelMap can be divided in two two categories with respect to locking.
+// 1. Methods acquire the global lock.
+// 2. Methods that can only be called when global lock is held.
+// A second type of method need always to be called inside a first type of method.
+type channelMap struct {
+ mu sync.RWMutex
+ topLevelChannels map[int64]struct{}
+ servers map[int64]*server
+ channels map[int64]*channel
+ subChannels map[int64]*subChannel
+ listenSockets map[int64]*listenSocket
+ normalSockets map[int64]*normalSocket
+}
+
+func (c *channelMap) addServer(id int64, s *server) {
+ c.mu.Lock()
+ s.cm = c
+ c.servers[id] = s
+ c.mu.Unlock()
+}
+
+func (c *channelMap) addChannel(id int64, cn *channel, isTopChannel bool, pid int64, ref string) {
+ c.mu.Lock()
+ cn.cm = c
+ c.channels[id] = cn
+ if isTopChannel {
+ c.topLevelChannels[id] = struct{}{}
+ } else {
+ c.findEntry(pid).addChild(id, cn)
+ }
+ c.mu.Unlock()
+}
+
+func (c *channelMap) addSubChannel(id int64, sc *subChannel, pid int64, ref string) {
+ c.mu.Lock()
+ sc.cm = c
+ c.subChannels[id] = sc
+ c.findEntry(pid).addChild(id, sc)
+ c.mu.Unlock()
+}
+
+func (c *channelMap) addListenSocket(id int64, ls *listenSocket, pid int64, ref string) {
+ c.mu.Lock()
+ ls.cm = c
+ c.listenSockets[id] = ls
+ c.findEntry(pid).addChild(id, ls)
+ c.mu.Unlock()
+}
+
+func (c *channelMap) addNormalSocket(id int64, ns *normalSocket, pid int64, ref string) {
+ c.mu.Lock()
+ ns.cm = c
+ c.normalSockets[id] = ns
+ c.findEntry(pid).addChild(id, ns)
+ c.mu.Unlock()
+}
+
+// removeEntry triggers the removal of an entry, which may not indeed delete the
+// entry, if it has to wait on the deletion of its children, or may lead to a chain
+// of entry deletion. For example, deleting the last socket of a gracefully shutting
+// down server will lead to the server being also deleted.
+func (c *channelMap) removeEntry(id int64) {
+ c.mu.Lock()
+ c.findEntry(id).triggerDelete()
+ c.mu.Unlock()
+}
+
+// c.mu must be held by the caller.
+func (c *channelMap) findEntry(id int64) entry {
+ var v entry
+ var ok bool
+ if v, ok = c.channels[id]; ok {
+ return v
+ }
+ if v, ok = c.subChannels[id]; ok {
+ return v
+ }
+ if v, ok = c.servers[id]; ok {
+ return v
+ }
+ if v, ok = c.listenSockets[id]; ok {
+ return v
+ }
+ if v, ok = c.normalSockets[id]; ok {
+ return v
+ }
+ return &dummyEntry{idNotFound: id}
+}
+
+// c.mu must be held by the caller
+// deleteEntry simply deletes an entry from the channelMap. Before calling this
+// method, caller must check this entry is ready to be deleted, i.e removeEntry()
+// has been called on it, and no children still exist.
+// Conditionals are ordered by the expected frequency of deletion of each entity
+// type, in order to optimize performance.
+func (c *channelMap) deleteEntry(id int64) {
+ var ok bool
+ if _, ok = c.normalSockets[id]; ok {
+ delete(c.normalSockets, id)
+ return
+ }
+ if _, ok = c.subChannels[id]; ok {
+ delete(c.subChannels, id)
+ return
+ }
+ if _, ok = c.channels[id]; ok {
+ delete(c.channels, id)
+ delete(c.topLevelChannels, id)
+ return
+ }
+ if _, ok = c.listenSockets[id]; ok {
+ delete(c.listenSockets, id)
+ return
+ }
+ if _, ok = c.servers[id]; ok {
+ delete(c.servers, id)
+ return
+ }
+}
+
+type int64Slice []int64
+
+func (s int64Slice) Len() int { return len(s) }
+func (s int64Slice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+func (s int64Slice) Less(i, j int) bool { return s[i] < s[j] }
+
+func copyMap(m map[int64]string) map[int64]string {
+ n := make(map[int64]string)
+ for k, v := range m {
+ n[k] = v
+ }
+ return n
+}
+
+func min(a, b int) int {
+ if a < b {
+ return a
+ }
+ return b
+}
+
+func (c *channelMap) GetTopChannels(id int64) ([]*ChannelMetric, bool) {
+ c.mu.RLock()
+ l := len(c.topLevelChannels)
+ ids := make([]int64, 0, l)
+ cns := make([]*channel, 0, min(l, EntryPerPage))
+
+ for k := range c.topLevelChannels {
+ ids = append(ids, k)
+ }
+ sort.Sort(int64Slice(ids))
+ idx := sort.Search(len(ids), func(i int) bool { return ids[i] >= id })
+ count := 0
+ var end bool
+ var t []*ChannelMetric
+ for i, v := range ids[idx:] {
+ if count == EntryPerPage {
+ break
+ }
+ if cn, ok := c.channels[v]; ok {
+ cns = append(cns, cn)
+ t = append(t, &ChannelMetric{
+ NestedChans: copyMap(cn.nestedChans),
+ SubChans: copyMap(cn.subChans),
+ })
+ count++
+ }
+ if i == len(ids[idx:])-1 {
+ end = true
+ break
+ }
+ }
+ c.mu.RUnlock()
+ if count == 0 {
+ end = true
+ }
+
+ for i, cn := range cns {
+ t[i].ChannelData = cn.c.ChannelzMetric()
+ t[i].ID = cn.id
+ t[i].RefName = cn.refName
+ }
+ return t, end
+}
+
+func (c *channelMap) GetServers(id int64) ([]*ServerMetric, bool) {
+ c.mu.RLock()
+ l := len(c.servers)
+ ids := make([]int64, 0, l)
+ ss := make([]*server, 0, min(l, EntryPerPage))
+ for k := range c.servers {
+ ids = append(ids, k)
+ }
+ sort.Sort(int64Slice(ids))
+ idx := sort.Search(len(ids), func(i int) bool { return ids[i] >= id })
+ count := 0
+ var end bool
+ var s []*ServerMetric
+ for i, v := range ids[idx:] {
+ if count == EntryPerPage {
+ break
+ }
+ if svr, ok := c.servers[v]; ok {
+ ss = append(ss, svr)
+ s = append(s, &ServerMetric{
+ ListenSockets: copyMap(svr.listenSockets),
+ })
+ count++
+ }
+ if i == len(ids[idx:])-1 {
+ end = true
+ break
+ }
+ }
+ c.mu.RUnlock()
+ if count == 0 {
+ end = true
+ }
+
+ for i, svr := range ss {
+ s[i].ServerData = svr.s.ChannelzMetric()
+ s[i].ID = svr.id
+ s[i].RefName = svr.refName
+ }
+ return s, end
+}
+
+func (c *channelMap) GetServerSockets(id int64, startID int64) ([]*SocketMetric, bool) {
+ var svr *server
+ var ok bool
+ c.mu.RLock()
+ if svr, ok = c.servers[id]; !ok {
+ // server with id doesn't exist.
+ c.mu.RUnlock()
+ return nil, true
+ }
+ svrskts := svr.sockets
+ l := len(svrskts)
+ ids := make([]int64, 0, l)
+ sks := make([]*normalSocket, 0, min(l, EntryPerPage))
+ for k := range svrskts {
+ ids = append(ids, k)
+ }
+ sort.Sort((int64Slice(ids)))
+ idx := sort.Search(len(ids), func(i int) bool { return ids[i] >= id })
+ count := 0
+ var end bool
+ for i, v := range ids[idx:] {
+ if count == EntryPerPage {
+ break
+ }
+ if ns, ok := c.normalSockets[v]; ok {
+ sks = append(sks, ns)
+ count++
+ }
+ if i == len(ids[idx:])-1 {
+ end = true
+ break
+ }
+ }
+ c.mu.RUnlock()
+ if count == 0 {
+ end = true
+ }
+ var s []*SocketMetric
+ for _, ns := range sks {
+ sm := &SocketMetric{}
+ sm.SocketData = ns.s.ChannelzMetric()
+ sm.ID = ns.id
+ sm.RefName = ns.refName
+ s = append(s, sm)
+ }
+ return s, end
+}
+
+func (c *channelMap) GetChannel(id int64) *ChannelMetric {
+ cm := &ChannelMetric{}
+ var cn *channel
+ var ok bool
+ c.mu.RLock()
+ if cn, ok = c.channels[id]; !ok {
+ // channel with id doesn't exist.
+ c.mu.RUnlock()
+ return nil
+ }
+ cm.NestedChans = copyMap(cn.nestedChans)
+ cm.SubChans = copyMap(cn.subChans)
+ c.mu.RUnlock()
+ cm.ChannelData = cn.c.ChannelzMetric()
+ cm.ID = cn.id
+ cm.RefName = cn.refName
+ return cm
+}
+
+func (c *channelMap) GetSubChannel(id int64) *SubChannelMetric {
+ cm := &SubChannelMetric{}
+ var sc *subChannel
+ var ok bool
+ c.mu.RLock()
+ if sc, ok = c.subChannels[id]; !ok {
+ // subchannel with id doesn't exist.
+ c.mu.RUnlock()
+ return nil
+ }
+ cm.Sockets = copyMap(sc.sockets)
+ c.mu.RUnlock()
+ cm.ChannelData = sc.c.ChannelzMetric()
+ cm.ID = sc.id
+ cm.RefName = sc.refName
+ return cm
+}
+
+func (c *channelMap) GetSocket(id int64) *SocketMetric {
+ sm := &SocketMetric{}
+ c.mu.RLock()
+ if ls, ok := c.listenSockets[id]; ok {
+ c.mu.RUnlock()
+ sm.SocketData = ls.s.ChannelzMetric()
+ sm.ID = ls.id
+ sm.RefName = ls.refName
+ return sm
+ }
+ if ns, ok := c.normalSockets[id]; ok {
+ c.mu.RUnlock()
+ sm.SocketData = ns.s.ChannelzMetric()
+ sm.ID = ns.id
+ sm.RefName = ns.refName
+ return sm
+ }
+ c.mu.RUnlock()
+ return nil
+}
+
+type idGenerator struct {
+ id int64
+}
+
+func (i *idGenerator) reset() {
+ atomic.StoreInt64(&i.id, 0)
+}
+
+func (i *idGenerator) genID() int64 {
+ return atomic.AddInt64(&i.id, 1)
+}
diff --git a/vendor/google.golang.org/grpc/internal/channelz/types.go b/vendor/google.golang.org/grpc/internal/channelz/types.go
new file mode 100644
index 0000000..153d753
--- /dev/null
+++ b/vendor/google.golang.org/grpc/internal/channelz/types.go
@@ -0,0 +1,418 @@
+/*
+ *
+ * Copyright 2018 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package channelz
+
+import (
+ "net"
+ "time"
+
+ "google.golang.org/grpc/connectivity"
+ "google.golang.org/grpc/grpclog"
+)
+
+// entry represents a node in the channelz database.
+type entry interface {
+ // addChild adds a child e, whose channelz id is id to child list
+ addChild(id int64, e entry)
+ // deleteChild deletes a child with channelz id to be id from child list
+ deleteChild(id int64)
+ // triggerDelete tries to delete self from channelz database. However, if child
+ // list is not empty, then deletion from the database is on hold until the last
+ // child is deleted from database.
+ triggerDelete()
+ // deleteSelfIfReady check whether triggerDelete() has been called before, and whether child
+ // list is now empty. If both conditions are met, then delete self from database.
+ deleteSelfIfReady()
+}
+
+// dummyEntry is a fake entry to handle entry not found case.
+type dummyEntry struct {
+ idNotFound int64
+}
+
+func (d *dummyEntry) addChild(id int64, e entry) {
+ // Note: It is possible for a normal program to reach here under race condition.
+ // For example, there could be a race between ClientConn.Close() info being propagated
+ // to addrConn and http2Client. ClientConn.Close() cancel the context and result
+ // in http2Client to error. The error info is then caught by transport monitor
+ // and before addrConn.tearDown() is called in side ClientConn.Close(). Therefore,
+ // the addrConn will create a new transport. And when registering the new transport in
+ // channelz, its parent addrConn could have already been torn down and deleted
+ // from channelz tracking, and thus reach the code here.
+ grpclog.Infof("attempt to add child of type %T with id %d to a parent (id=%d) that doesn't currently exist", e, id, d.idNotFound)
+}
+
+func (d *dummyEntry) deleteChild(id int64) {
+ // It is possible for a normal program to reach here under race condition.
+ // Refer to the example described in addChild().
+ grpclog.Infof("attempt to delete child with id %d from a parent (id=%d) that doesn't currently exist", id, d.idNotFound)
+}
+
+func (d *dummyEntry) triggerDelete() {
+ grpclog.Warningf("attempt to delete an entry (id=%d) that doesn't currently exist", d.idNotFound)
+}
+
+func (*dummyEntry) deleteSelfIfReady() {
+ // code should not reach here. deleteSelfIfReady is always called on an existing entry.
+}
+
+// ChannelMetric defines the info channelz provides for a specific Channel, which
+// includes ChannelInternalMetric and channelz-specific data, such as channelz id,
+// child list, etc.
+type ChannelMetric struct {
+ // ID is the channelz id of this channel.
+ ID int64
+ // RefName is the human readable reference string of this channel.
+ RefName string
+ // ChannelData contains channel internal metric reported by the channel through
+ // ChannelzMetric().
+ ChannelData *ChannelInternalMetric
+ // NestedChans tracks the nested channel type children of this channel in the format of
+ // a map from nested channel channelz id to corresponding reference string.
+ NestedChans map[int64]string
+ // SubChans tracks the subchannel type children of this channel in the format of a
+ // map from subchannel channelz id to corresponding reference string.
+ SubChans map[int64]string
+ // Sockets tracks the socket type children of this channel in the format of a map
+ // from socket channelz id to corresponding reference string.
+ // Note current grpc implementation doesn't allow channel having sockets directly,
+ // therefore, this is field is unused.
+ Sockets map[int64]string
+}
+
+// SubChannelMetric defines the info channelz provides for a specific SubChannel,
+// which includes ChannelInternalMetric and channelz-specific data, such as
+// channelz id, child list, etc.
+type SubChannelMetric struct {
+ // ID is the channelz id of this subchannel.
+ ID int64
+ // RefName is the human readable reference string of this subchannel.
+ RefName string
+ // ChannelData contains subchannel internal metric reported by the subchannel
+ // through ChannelzMetric().
+ ChannelData *ChannelInternalMetric
+ // NestedChans tracks the nested channel type children of this subchannel in the format of
+ // a map from nested channel channelz id to corresponding reference string.
+ // Note current grpc implementation doesn't allow subchannel to have nested channels
+ // as children, therefore, this field is unused.
+ NestedChans map[int64]string
+ // SubChans tracks the subchannel type children of this subchannel in the format of a
+ // map from subchannel channelz id to corresponding reference string.
+ // Note current grpc implementation doesn't allow subchannel to have subchannels
+ // as children, therefore, this field is unused.
+ SubChans map[int64]string
+ // Sockets tracks the socket type children of this subchannel in the format of a map
+ // from socket channelz id to corresponding reference string.
+ Sockets map[int64]string
+}
+
+// ChannelInternalMetric defines the struct that the implementor of Channel interface
+// should return from ChannelzMetric().
+type ChannelInternalMetric struct {
+ // current connectivity state of the channel.
+ State connectivity.State
+ // The target this channel originally tried to connect to. May be absent
+ Target string
+ // The number of calls started on the channel.
+ CallsStarted int64
+ // The number of calls that have completed with an OK status.
+ CallsSucceeded int64
+ // The number of calls that have a completed with a non-OK status.
+ CallsFailed int64
+ // The last time a call was started on the channel.
+ LastCallStartedTimestamp time.Time
+ //TODO: trace
+}
+
+// Channel is the interface that should be satisfied in order to be tracked by
+// channelz as Channel or SubChannel.
+type Channel interface {
+ ChannelzMetric() *ChannelInternalMetric
+}
+
+type channel struct {
+ refName string
+ c Channel
+ closeCalled bool
+ nestedChans map[int64]string
+ subChans map[int64]string
+ id int64
+ pid int64
+ cm *channelMap
+}
+
+func (c *channel) addChild(id int64, e entry) {
+ switch v := e.(type) {
+ case *subChannel:
+ c.subChans[id] = v.refName
+ case *channel:
+ c.nestedChans[id] = v.refName
+ default:
+ grpclog.Errorf("cannot add a child (id = %d) of type %T to a channel", id, e)
+ }
+}
+
+func (c *channel) deleteChild(id int64) {
+ delete(c.subChans, id)
+ delete(c.nestedChans, id)
+ c.deleteSelfIfReady()
+}
+
+func (c *channel) triggerDelete() {
+ c.closeCalled = true
+ c.deleteSelfIfReady()
+}
+
+func (c *channel) deleteSelfIfReady() {
+ if !c.closeCalled || len(c.subChans)+len(c.nestedChans) != 0 {
+ return
+ }
+ c.cm.deleteEntry(c.id)
+ // not top channel
+ if c.pid != 0 {
+ c.cm.findEntry(c.pid).deleteChild(c.id)
+ }
+}
+
+type subChannel struct {
+ refName string
+ c Channel
+ closeCalled bool
+ sockets map[int64]string
+ id int64
+ pid int64
+ cm *channelMap
+}
+
+func (sc *subChannel) addChild(id int64, e entry) {
+ if v, ok := e.(*normalSocket); ok {
+ sc.sockets[id] = v.refName
+ } else {
+ grpclog.Errorf("cannot add a child (id = %d) of type %T to a subChannel", id, e)
+ }
+}
+
+func (sc *subChannel) deleteChild(id int64) {
+ delete(sc.sockets, id)
+ sc.deleteSelfIfReady()
+}
+
+func (sc *subChannel) triggerDelete() {
+ sc.closeCalled = true
+ sc.deleteSelfIfReady()
+}
+
+func (sc *subChannel) deleteSelfIfReady() {
+ if !sc.closeCalled || len(sc.sockets) != 0 {
+ return
+ }
+ sc.cm.deleteEntry(sc.id)
+ sc.cm.findEntry(sc.pid).deleteChild(sc.id)
+}
+
+// SocketMetric defines the info channelz provides for a specific Socket, which
+// includes SocketInternalMetric and channelz-specific data, such as channelz id, etc.
+type SocketMetric struct {
+ // ID is the channelz id of this socket.
+ ID int64
+ // RefName is the human readable reference string of this socket.
+ RefName string
+ // SocketData contains socket internal metric reported by the socket through
+ // ChannelzMetric().
+ SocketData *SocketInternalMetric
+}
+
+// SocketInternalMetric defines the struct that the implementor of Socket interface
+// should return from ChannelzMetric().
+type SocketInternalMetric struct {
+ // The number of streams that have been started.
+ StreamsStarted int64
+ // The number of streams that have ended successfully:
+ // On client side, receiving frame with eos bit set.
+ // On server side, sending frame with eos bit set.
+ StreamsSucceeded int64
+ // The number of streams that have ended unsuccessfully:
+ // On client side, termination without receiving frame with eos bit set.
+ // On server side, termination without sending frame with eos bit set.
+ StreamsFailed int64
+ // The number of messages successfully sent on this socket.
+ MessagesSent int64
+ MessagesReceived int64
+ // The number of keep alives sent. This is typically implemented with HTTP/2
+ // ping messages.
+ KeepAlivesSent int64
+ // The last time a stream was created by this endpoint. Usually unset for
+ // servers.
+ LastLocalStreamCreatedTimestamp time.Time
+ // The last time a stream was created by the remote endpoint. Usually unset
+ // for clients.
+ LastRemoteStreamCreatedTimestamp time.Time
+ // The last time a message was sent by this endpoint.
+ LastMessageSentTimestamp time.Time
+ // The last time a message was received by this endpoint.
+ LastMessageReceivedTimestamp time.Time
+ // The amount of window, granted to the local endpoint by the remote endpoint.
+ // This may be slightly out of date due to network latency. This does NOT
+ // include stream level or TCP level flow control info.
+ LocalFlowControlWindow int64
+ // The amount of window, granted to the remote endpoint by the local endpoint.
+ // This may be slightly out of date due to network latency. This does NOT
+ // include stream level or TCP level flow control info.
+ RemoteFlowControlWindow int64
+ // The locally bound address.
+ LocalAddr net.Addr
+ // The remote bound address. May be absent.
+ RemoteAddr net.Addr
+ // Optional, represents the name of the remote endpoint, if different than
+ // the original target name.
+ RemoteName string
+ //TODO: socket options
+ //TODO: Security
+}
+
+// Socket is the interface that should be satisfied in order to be tracked by
+// channelz as Socket.
+type Socket interface {
+ ChannelzMetric() *SocketInternalMetric
+}
+
+type listenSocket struct {
+ refName string
+ s Socket
+ id int64
+ pid int64
+ cm *channelMap
+}
+
+func (ls *listenSocket) addChild(id int64, e entry) {
+ grpclog.Errorf("cannot add a child (id = %d) of type %T to a listen socket", id, e)
+}
+
+func (ls *listenSocket) deleteChild(id int64) {
+ grpclog.Errorf("cannot delete a child (id = %d) from a listen socket", id)
+}
+
+func (ls *listenSocket) triggerDelete() {
+ ls.cm.deleteEntry(ls.id)
+ ls.cm.findEntry(ls.pid).deleteChild(ls.id)
+}
+
+func (ls *listenSocket) deleteSelfIfReady() {
+ grpclog.Errorf("cannot call deleteSelfIfReady on a listen socket")
+}
+
+type normalSocket struct {
+ refName string
+ s Socket
+ id int64
+ pid int64
+ cm *channelMap
+}
+
+func (ns *normalSocket) addChild(id int64, e entry) {
+ grpclog.Errorf("cannot add a child (id = %d) of type %T to a normal socket", id, e)
+}
+
+func (ns *normalSocket) deleteChild(id int64) {
+ grpclog.Errorf("cannot delete a child (id = %d) from a normal socket", id)
+}
+
+func (ns *normalSocket) triggerDelete() {
+ ns.cm.deleteEntry(ns.id)
+ ns.cm.findEntry(ns.pid).deleteChild(ns.id)
+}
+
+func (ns *normalSocket) deleteSelfIfReady() {
+ grpclog.Errorf("cannot call deleteSelfIfReady on a normal socket")
+}
+
+// ServerMetric defines the info channelz provides for a specific Server, which
+// includes ServerInternalMetric and channelz-specific data, such as channelz id,
+// child list, etc.
+type ServerMetric struct {
+ // ID is the channelz id of this server.
+ ID int64
+ // RefName is the human readable reference string of this server.
+ RefName string
+ // ServerData contains server internal metric reported by the server through
+ // ChannelzMetric().
+ ServerData *ServerInternalMetric
+ // ListenSockets tracks the listener socket type children of this server in the
+ // format of a map from socket channelz id to corresponding reference string.
+ ListenSockets map[int64]string
+}
+
+// ServerInternalMetric defines the struct that the implementor of Server interface
+// should return from ChannelzMetric().
+type ServerInternalMetric struct {
+ // The number of incoming calls started on the server.
+ CallsStarted int64
+ // The number of incoming calls that have completed with an OK status.
+ CallsSucceeded int64
+ // The number of incoming calls that have a completed with a non-OK status.
+ CallsFailed int64
+ // The last time a call was started on the server.
+ LastCallStartedTimestamp time.Time
+ //TODO: trace
+}
+
+// Server is the interface to be satisfied in order to be tracked by channelz as
+// Server.
+type Server interface {
+ ChannelzMetric() *ServerInternalMetric
+}
+
+type server struct {
+ refName string
+ s Server
+ closeCalled bool
+ sockets map[int64]string
+ listenSockets map[int64]string
+ id int64
+ cm *channelMap
+}
+
+func (s *server) addChild(id int64, e entry) {
+ switch v := e.(type) {
+ case *normalSocket:
+ s.sockets[id] = v.refName
+ case *listenSocket:
+ s.listenSockets[id] = v.refName
+ default:
+ grpclog.Errorf("cannot add a child (id = %d) of type %T to a server", id, e)
+ }
+}
+
+func (s *server) deleteChild(id int64) {
+ delete(s.sockets, id)
+ delete(s.listenSockets, id)
+ s.deleteSelfIfReady()
+}
+
+func (s *server) triggerDelete() {
+ s.closeCalled = true
+ s.deleteSelfIfReady()
+}
+
+func (s *server) deleteSelfIfReady() {
+ if !s.closeCalled || len(s.sockets)+len(s.listenSockets) != 0 {
+ return
+ }
+ s.cm.deleteEntry(s.id)
+}