* * 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 (
	
	
	
	

	
	
)
entry represents a node in the channelz database.
addChild adds a child e, whose channelz id is id to child list
deleteChild deletes a child with channelz id to be id from child list
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.
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.
getParentID returns parent ID of the entry. 0 value parent ID means no parent.
	getParentID() int64
}
dummyEntry is a fake entry to handle entry not found case.
type dummyEntry struct {
	idNotFound int64
}

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.
	logger.Infof("attempt to add child of type %T with id %d to a parent (id=%d) that doesn't currently exist", , , .idNotFound)
}

It is possible for a normal program to reach here under race condition. Refer to the example described in addChild().
	logger.Infof("attempt to delete child with id %d from a parent (id=%d) that doesn't currently exist", , .idNotFound)
}

func ( *dummyEntry) () {
	logger.Warningf("attempt to delete an entry (id=%d) that doesn't currently exist", .idNotFound)
}

code should not reach here. deleteSelfIfReady is always called on an existing entry.
}

func (*dummyEntry) () int64 {
	return 0
}
ChannelMetric defines the info channelz provides for a specific Channel, which includes ChannelInternalMetric and channelz-specific data, such as channelz id, child list, etc.
ID is the channelz id of this channel.
RefName is the human readable reference string of this channel.
ChannelData contains channel internal metric reported by the channel through ChannelzMetric().
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.
SubChans tracks the subchannel type children of this channel in the format of a map from subchannel channelz id to corresponding reference 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.
Trace contains the most recent traced events.
SubChannelMetric defines the info channelz provides for a specific SubChannel, which includes ChannelInternalMetric and channelz-specific data, such as channelz id, child list, etc.
ID is the channelz id of this subchannel.
RefName is the human readable reference string of this subchannel.
ChannelData contains subchannel internal metric reported by the subchannel through ChannelzMetric().
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.
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.
Sockets tracks the socket type children of this subchannel in the format of a map from socket channelz id to corresponding reference string.
Trace contains the most recent traced events.
ChannelInternalMetric defines the struct that the implementor of Channel interface should return from ChannelzMetric().
current connectivity state of the channel.
The target this channel originally tried to connect to. May be absent
The number of calls started on the channel.
The number of calls that have completed with an OK status.
The number of calls that have a completed with a non-OK status.
The last time a call was started on the channel.
ChannelTrace stores traced events on a channel/subchannel and related info.
EventNum is the number of events that ever got traced (i.e. including those that have been deleted)
CreationTime is the creation time of the trace.
Events stores the most recent trace events (up to $maxTraceEntry, newer event will overwrite the oldest one)
TraceEvent represent a single trace event
Desc is a simple description of the trace event.
Severity states the severity of this trace event.
Timestamp is the event time.
RefID is the id of the entity that gets referenced in the event. RefID is 0 if no other entity is involved in this event. e.g. SubChannel (id: 4[]) Created. --> RefID = 4, RefName = "" (inside [])
RefName is the reference name for the entity that gets referenced in the event.
RefType indicates the referenced entity type, i.e Channel or SubChannel.
Channel is the interface that should be satisfied in order to be tracked by channelz as Channel or SubChannel.
traceRefCount is the number of trace events that reference this channel. Non-zero traceRefCount means the trace of this channel cannot be deleted.
	traceRefCount int32
}

func ( *channel) ( int64,  entry) {
	switch v := .(type) {
	case *subChannel:
		.subChans[] = .refName
	case *channel:
		.nestedChans[] = .refName
	default:
		logger.Errorf("cannot add a child (id = %d) of type %T to a channel", , )
	}
}

func ( *channel) ( int64) {
	delete(.subChans, )
	delete(.nestedChans, )
	.deleteSelfIfReady()
}

func ( *channel) () {
	.closeCalled = true
	.deleteSelfIfReady()
}

func ( *channel) () int64 {
	return .pid
}
deleteSelfFromTree tries to delete the channel from the channelz entry relation tree, which means deleting the channel reference from its parent's child list. In order for a channel to be deleted from the tree, it must meet the criteria that, removal of the corresponding grpc object has been invoked, and the channel does not have any children left. The returned boolean value indicates whether the channel has been successfully deleted from tree.
func ( *channel) () ( bool) {
	if !.closeCalled || len(.subChans)+len(.nestedChans) != 0 {
		return false
not top channel
	if .pid != 0 {
		.cm.findEntry(.pid).deleteChild(.id)
	}
	return true
}
deleteSelfFromMap checks whether it is valid to delete the channel from the map, which means deleting the channel from channelz's tracking entirely. Users can no longer use id to query the channel, and its memory will be garbage collected. The trace reference count of the channel must be 0 in order to be deleted from the map. This is specified in the channel tracing gRFC that as long as some other trace has reference to an entity, the trace of the referenced entity must not be deleted. In order to release the resource allocated by grpc, the reference to the grpc object is reset to a dummy object. deleteSelfFromMap must be called after deleteSelfFromTree returns true. It returns a bool to indicate whether the channel can be safely deleted from map.
func ( *channel) () ( bool) {
	if .getTraceRefCount() != 0 {
		.c = &dummyChannel{}
		return false
	}
	return true
}
deleteSelfIfReady tries to delete the channel itself from the channelz database. The delete process includes two steps: 1. delete the channel from the entry relation tree, i.e. delete the channel reference from its parent's child list. 2. delete the channel from the map, i.e. delete the channel entirely from channelz. Lookup by id will return entry not found error.
func ( *channel) () {
	if !.deleteSelfFromTree() {
		return
	}
	if !.deleteSelfFromMap() {
		return
	}
	.cm.deleteEntry(.id)
	.trace.clear()
}

func ( *channel) () *channelTrace {
	return .trace
}

func ( *channel) () {
	atomic.AddInt32(&.traceRefCount, 1)
}

func ( *channel) () {
	atomic.AddInt32(&.traceRefCount, -1)
}

func ( *channel) () int {
	 := atomic.LoadInt32(&.traceRefCount)
	return int()
}

func ( *channel) () string {
	return .refName
}

type subChannel struct {
	refName       string
	c             Channel
	closeCalled   bool
	sockets       map[int64]string
	id            int64
	pid           int64
	cm            *channelMap
	trace         *channelTrace
	traceRefCount int32
}

func ( *subChannel) ( int64,  entry) {
	if ,  := .(*normalSocket);  {
		.sockets[] = .refName
	} else {
		logger.Errorf("cannot add a child (id = %d) of type %T to a subChannel", , )
	}
}

func ( *subChannel) ( int64) {
	delete(.sockets, )
	.deleteSelfIfReady()
}

func ( *subChannel) () {
	.closeCalled = true
	.deleteSelfIfReady()
}

func ( *subChannel) () int64 {
	return .pid
}
deleteSelfFromTree tries to delete the subchannel from the channelz entry relation tree, which means deleting the subchannel reference from its parent's child list. In order for a subchannel to be deleted from the tree, it must meet the criteria that, removal of the corresponding grpc object has been invoked, and the subchannel does not have any children left. The returned boolean value indicates whether the channel has been successfully deleted from tree.
func ( *subChannel) () ( bool) {
	if !.closeCalled || len(.sockets) != 0 {
		return false
	}
	.cm.findEntry(.pid).deleteChild(.id)
	return true
}
deleteSelfFromMap checks whether it is valid to delete the subchannel from the map, which means deleting the subchannel from channelz's tracking entirely. Users can no longer use id to query the subchannel, and its memory will be garbage collected. The trace reference count of the subchannel must be 0 in order to be deleted from the map. This is specified in the channel tracing gRFC that as long as some other trace has reference to an entity, the trace of the referenced entity must not be deleted. In order to release the resource allocated by grpc, the reference to the grpc object is reset to a dummy object. deleteSelfFromMap must be called after deleteSelfFromTree returns true. It returns a bool to indicate whether the channel can be safely deleted from map.
func ( *subChannel) () ( bool) {
free the grpc struct (i.e. addrConn)
		.c = &dummyChannel{}
		return false
	}
	return true
}
deleteSelfIfReady tries to delete the subchannel itself from the channelz database. The delete process includes two steps: 1. delete the subchannel from the entry relation tree, i.e. delete the subchannel reference from its parent's child list. 2. delete the subchannel from the map, i.e. delete the subchannel entirely from channelz. Lookup by id will return entry not found error.
func ( *subChannel) () {
	if !.deleteSelfFromTree() {
		return
	}
	if !.deleteSelfFromMap() {
		return
	}
	.cm.deleteEntry(.id)
	.trace.clear()
}

func ( *subChannel) () *channelTrace {
	return .trace
}

func ( *subChannel) () {
	atomic.AddInt32(&.traceRefCount, 1)
}

func ( *subChannel) () {
	atomic.AddInt32(&.traceRefCount, -1)
}

func ( *subChannel) () int {
	 := atomic.LoadInt32(&.traceRefCount)
	return int()
}

func ( *subChannel) () string {
	return .refName
}
SocketMetric defines the info channelz provides for a specific Socket, which includes SocketInternalMetric and channelz-specific data, such as channelz id, etc.
ID is the channelz id of this socket.
RefName is the human readable reference string of this socket.
SocketData contains socket internal metric reported by the socket through ChannelzMetric().
SocketInternalMetric defines the struct that the implementor of Socket interface should return from ChannelzMetric().
The number of streams that have been started.
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.
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.
The number of messages successfully sent on this socket.
The number of keep alives sent. This is typically implemented with HTTP/2 ping messages.
The last time a stream was created by this endpoint. Usually unset for servers.
The last time a stream was created by the remote endpoint. Usually unset for clients.
The last time a message was sent by this endpoint.
The last time a message was received by this endpoint.
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.
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.
The locally bound address.
The remote bound address. May be absent.
Optional, represents the name of the remote endpoint, if different than the original target name.
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 ( *listenSocket) ( int64,  entry) {
	logger.Errorf("cannot add a child (id = %d) of type %T to a listen socket", , )
}

func ( *listenSocket) ( int64) {
	logger.Errorf("cannot delete a child (id = %d) from a listen socket", )
}

func ( *listenSocket) () {
	.cm.deleteEntry(.id)
	.cm.findEntry(.pid).deleteChild(.id)
}

func ( *listenSocket) () {
	logger.Errorf("cannot call deleteSelfIfReady on a listen socket")
}

func ( *listenSocket) () int64 {
	return .pid
}

type normalSocket struct {
	refName string
	s       Socket
	id      int64
	pid     int64
	cm      *channelMap
}

func ( *normalSocket) ( int64,  entry) {
	logger.Errorf("cannot add a child (id = %d) of type %T to a normal socket", , )
}

func ( *normalSocket) ( int64) {
	logger.Errorf("cannot delete a child (id = %d) from a normal socket", )
}

func ( *normalSocket) () {
	.cm.deleteEntry(.id)
	.cm.findEntry(.pid).deleteChild(.id)
}

func ( *normalSocket) () {
	logger.Errorf("cannot call deleteSelfIfReady on a normal socket")
}

func ( *normalSocket) () int64 {
	return .pid
}
ServerMetric defines the info channelz provides for a specific Server, which includes ServerInternalMetric and channelz-specific data, such as channelz id, child list, etc.
ID is the channelz id of this server.
RefName is the human readable reference string of this server.
ServerData contains server internal metric reported by the server through ChannelzMetric().
ListenSockets tracks the listener socket type children of this server in the format of a map from socket channelz id to corresponding reference string.
ServerInternalMetric defines the struct that the implementor of Server interface should return from ChannelzMetric().
The number of incoming calls started on the server.
The number of incoming calls that have completed with an OK status.
The number of incoming calls that have a completed with a non-OK status.
The last time a call was started on the server.
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 ( *server) ( int64,  entry) {
	switch v := .(type) {
	case *normalSocket:
		.sockets[] = .refName
	case *listenSocket:
		.listenSockets[] = .refName
	default:
		logger.Errorf("cannot add a child (id = %d) of type %T to a server", , )
	}
}

func ( *server) ( int64) {
	delete(.sockets, )
	delete(.listenSockets, )
	.deleteSelfIfReady()
}

func ( *server) () {
	.closeCalled = true
	.deleteSelfIfReady()
}

func ( *server) () {
	if !.closeCalled || len(.sockets)+len(.listenSockets) != 0 {
		return
	}
	.cm.deleteEntry(.id)
}

func ( *server) () int64 {
	return 0
}

type tracedChannel interface {
	getChannelTrace() *channelTrace
	incrTraceRefCount()
	decrTraceRefCount()
	getRefName() string
}

type channelTrace struct {
	cm          *channelMap
	createdTime time.Time
	eventCount  int64
	mu          sync.Mutex
	events      []*TraceEvent
}

func ( *channelTrace) ( *TraceEvent) {
	.mu.Lock()
	if len(.events) == getMaxTraceEntry() {
		 := .events[0]
		.events = .events[1:]
start recursive cleanup in a goroutine to not block the call originated from grpc.
need to acquire c.cm.mu lock to call the unlocked attemptCleanup func.
				.cm.mu.Lock()
				.cm.decrTraceRefCount(.RefID)
				.cm.mu.Unlock()
			}()
		}
	}
	.Timestamp = time.Now()
	.events = append(.events, )
	.eventCount++
	.mu.Unlock()
}

func ( *channelTrace) () {
	.mu.Lock()
	for ,  := range .events {
caller should have already held the c.cm.mu lock.
Severity is the severity level of a trace event. The canonical enumeration of all valid values is here: https://github.com/grpc/grpc-proto/blob/9b13d199cc0d4703c7ea26c9c330ba695866eb23/grpc/channelz/v1/channelz.proto#L126.
CtUNKNOWN indicates unknown severity of a trace event.
CtINFO indicates info level severity of a trace event.
CtWarning indicates warning level severity of a trace event.
CtError indicates error level severity of a trace event.
RefChannelType is the type of the entity being referenced in a trace event.
RefChannel indicates the referenced entity is a Channel.
RefSubChannel indicates the referenced entity is a SubChannel.