1 /*
2 *
3 * Copyright 2024 gRPC authors.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 */
18 19 package channelz
20 21 import (
22 "fmt"
23 "sync/atomic"
24 )
25 26 // SubChannel is the channelz representation of a subchannel.
27 type SubChannel struct {
28 Entity
29 // ID is the channelz id of this subchannel.
30 ID int64
31 // RefName is the human readable reference string of this subchannel.
32 RefName string
33 closeCalled bool
34 sockets map[int64]string
35 parent *Channel
36 trace *ChannelTrace
37 traceRefCount int32
38 39 ChannelMetrics ChannelMetrics
40 }
41 42 func (sc *SubChannel) String() string {
43 return fmt.Sprintf("%s SubChannel #%d", sc.parent, sc.ID)
44 }
45 46 func (sc *SubChannel) id() int64 {
47 return sc.ID
48 }
49 50 // Sockets returns a copy of the sockets map associated with the SubChannel.
51 func (sc *SubChannel) Sockets() map[int64]string {
52 db.mu.RLock()
53 defer db.mu.RUnlock()
54 return copyMap(sc.sockets)
55 }
56 57 // Trace returns a copy of the ChannelTrace associated with the SubChannel.
58 func (sc *SubChannel) Trace() *ChannelTrace {
59 db.mu.RLock()
60 defer db.mu.RUnlock()
61 return sc.trace.copy()
62 }
63 64 func (sc *SubChannel) addChild(id int64, e entry) {
65 if v, ok := e.(*Socket); ok && v.SocketType == SocketTypeNormal {
66 sc.sockets[id] = v.RefName
67 } else {
68 logger.Errorf("cannot add a child (id = %d) of type %T to a subChannel", id, e)
69 }
70 }
71 72 func (sc *SubChannel) deleteChild(id int64) {
73 delete(sc.sockets, id)
74 sc.deleteSelfIfReady()
75 }
76 77 func (sc *SubChannel) triggerDelete() {
78 sc.closeCalled = true
79 sc.deleteSelfIfReady()
80 }
81 82 func (sc *SubChannel) getParentID() int64 {
83 return sc.parent.ID
84 }
85 86 // deleteSelfFromTree tries to delete the subchannel from the channelz entry relation tree, which
87 // means deleting the subchannel reference from its parent's child list.
88 //
89 // In order for a subchannel to be deleted from the tree, it must meet the criteria that, removal of
90 // the corresponding grpc object has been invoked, and the subchannel does not have any children left.
91 //
92 // The returned boolean value indicates whether the channel has been successfully deleted from tree.
93 func (sc *SubChannel) deleteSelfFromTree() (deleted bool) {
94 if !sc.closeCalled || len(sc.sockets) != 0 {
95 return false
96 }
97 sc.parent.deleteChild(sc.ID)
98 return true
99 }
100 101 // deleteSelfFromMap checks whether it is valid to delete the subchannel from the map, which means
102 // deleting the subchannel from channelz's tracking entirely. Users can no longer use id to query
103 // the subchannel, and its memory will be garbage collected.
104 //
105 // The trace reference count of the subchannel must be 0 in order to be deleted from the map. This is
106 // specified in the channel tracing gRFC that as long as some other trace has reference to an entity,
107 // the trace of the referenced entity must not be deleted. In order to release the resource allocated
108 // by grpc, the reference to the grpc object is reset to a dummy object.
109 //
110 // deleteSelfFromMap must be called after deleteSelfFromTree returns true.
111 //
112 // It returns a bool to indicate whether the channel can be safely deleted from map.
113 func (sc *SubChannel) deleteSelfFromMap() (delete bool) {
114 return sc.getTraceRefCount() == 0
115 }
116 117 // deleteSelfIfReady tries to delete the subchannel itself from the channelz database.
118 // The delete process includes two steps:
119 // 1. delete the subchannel from the entry relation tree, i.e. delete the subchannel reference from
120 // its parent's child list.
121 // 2. delete the subchannel from the map, i.e. delete the subchannel entirely from channelz. Lookup
122 // by id will return entry not found error.
123 func (sc *SubChannel) deleteSelfIfReady() {
124 if !sc.deleteSelfFromTree() {
125 return
126 }
127 if !sc.deleteSelfFromMap() {
128 return
129 }
130 db.deleteEntry(sc.ID)
131 sc.trace.clear()
132 }
133 134 func (sc *SubChannel) getChannelTrace() *ChannelTrace {
135 return sc.trace
136 }
137 138 func (sc *SubChannel) incrTraceRefCount() {
139 atomic.AddInt32(&sc.traceRefCount, 1)
140 }
141 142 func (sc *SubChannel) decrTraceRefCount() {
143 atomic.AddInt32(&sc.traceRefCount, -1)
144 }
145 146 func (sc *SubChannel) getTraceRefCount() int {
147 i := atomic.LoadInt32(&sc.traceRefCount)
148 return int(i)
149 }
150 151 func (sc *SubChannel) getRefName() string {
152 return sc.RefName
153 }
154