You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

700 lines
18 KiB

  1. /*
  2. *
  3. * Copyright 2018 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. // Package channelz defines APIs for enabling channelz service, entry
  19. // registration/deletion, and accessing channelz data. It also defines channelz
  20. // metric struct formats.
  21. //
  22. // All APIs in this package are experimental.
  23. package channelz
  24. import (
  25. "sort"
  26. "sync"
  27. "sync/atomic"
  28. "time"
  29. "google.golang.org/grpc/grpclog"
  30. )
  31. const (
  32. defaultMaxTraceEntry int32 = 30
  33. )
  34. var (
  35. db dbWrapper
  36. idGen idGenerator
  37. // EntryPerPage defines the number of channelz entries to be shown on a web page.
  38. EntryPerPage = int64(50)
  39. curState int32
  40. maxTraceEntry = defaultMaxTraceEntry
  41. )
  42. // TurnOn turns on channelz data collection.
  43. func TurnOn() {
  44. if !IsOn() {
  45. NewChannelzStorage()
  46. atomic.StoreInt32(&curState, 1)
  47. }
  48. }
  49. // IsOn returns whether channelz data collection is on.
  50. func IsOn() bool {
  51. return atomic.CompareAndSwapInt32(&curState, 1, 1)
  52. }
  53. // SetMaxTraceEntry sets maximum number of trace entry per entity (i.e. channel/subchannel).
  54. // Setting it to 0 will disable channel tracing.
  55. func SetMaxTraceEntry(i int32) {
  56. atomic.StoreInt32(&maxTraceEntry, i)
  57. }
  58. // ResetMaxTraceEntryToDefault resets the maximum number of trace entry per entity to default.
  59. func ResetMaxTraceEntryToDefault() {
  60. atomic.StoreInt32(&maxTraceEntry, defaultMaxTraceEntry)
  61. }
  62. func getMaxTraceEntry() int {
  63. i := atomic.LoadInt32(&maxTraceEntry)
  64. return int(i)
  65. }
  66. // dbWarpper wraps around a reference to internal channelz data storage, and
  67. // provide synchronized functionality to set and get the reference.
  68. type dbWrapper struct {
  69. mu sync.RWMutex
  70. DB *channelMap
  71. }
  72. func (d *dbWrapper) set(db *channelMap) {
  73. d.mu.Lock()
  74. d.DB = db
  75. d.mu.Unlock()
  76. }
  77. func (d *dbWrapper) get() *channelMap {
  78. d.mu.RLock()
  79. defer d.mu.RUnlock()
  80. return d.DB
  81. }
  82. // NewChannelzStorage initializes channelz data storage and id generator.
  83. //
  84. // Note: This function is exported for testing purpose only. User should not call
  85. // it in most cases.
  86. func NewChannelzStorage() {
  87. db.set(&channelMap{
  88. topLevelChannels: make(map[int64]struct{}),
  89. channels: make(map[int64]*channel),
  90. listenSockets: make(map[int64]*listenSocket),
  91. normalSockets: make(map[int64]*normalSocket),
  92. servers: make(map[int64]*server),
  93. subChannels: make(map[int64]*subChannel),
  94. })
  95. idGen.reset()
  96. }
  97. // GetTopChannels returns a slice of top channel's ChannelMetric, along with a
  98. // boolean indicating whether there's more top channels to be queried for.
  99. //
  100. // The arg id specifies that only top channel with id at or above it will be included
  101. // in the result. The returned slice is up to a length of the arg maxResults or
  102. // EntryPerPage if maxResults is zero, and is sorted in ascending id order.
  103. func GetTopChannels(id int64, maxResults int64) ([]*ChannelMetric, bool) {
  104. return db.get().GetTopChannels(id, maxResults)
  105. }
  106. // GetServers returns a slice of server's ServerMetric, along with a
  107. // boolean indicating whether there's more servers to be queried for.
  108. //
  109. // The arg id specifies that only server with id at or above it will be included
  110. // in the result. The returned slice is up to a length of the arg maxResults or
  111. // EntryPerPage if maxResults is zero, and is sorted in ascending id order.
  112. func GetServers(id int64, maxResults int64) ([]*ServerMetric, bool) {
  113. return db.get().GetServers(id, maxResults)
  114. }
  115. // GetServerSockets returns a slice of server's (identified by id) normal socket's
  116. // SocketMetric, along with a boolean indicating whether there's more sockets to
  117. // be queried for.
  118. //
  119. // The arg startID specifies that only sockets with id at or above it will be
  120. // included in the result. The returned slice is up to a length of the arg maxResults
  121. // or EntryPerPage if maxResults is zero, and is sorted in ascending id order.
  122. func GetServerSockets(id int64, startID int64, maxResults int64) ([]*SocketMetric, bool) {
  123. return db.get().GetServerSockets(id, startID, maxResults)
  124. }
  125. // GetChannel returns the ChannelMetric for the channel (identified by id).
  126. func GetChannel(id int64) *ChannelMetric {
  127. return db.get().GetChannel(id)
  128. }
  129. // GetSubChannel returns the SubChannelMetric for the subchannel (identified by id).
  130. func GetSubChannel(id int64) *SubChannelMetric {
  131. return db.get().GetSubChannel(id)
  132. }
  133. // GetSocket returns the SocketInternalMetric for the socket (identified by id).
  134. func GetSocket(id int64) *SocketMetric {
  135. return db.get().GetSocket(id)
  136. }
  137. // GetServer returns the ServerMetric for the server (identified by id).
  138. func GetServer(id int64) *ServerMetric {
  139. return db.get().GetServer(id)
  140. }
  141. // RegisterChannel registers the given channel c in channelz database with ref
  142. // as its reference name, and add it to the child list of its parent (identified
  143. // by pid). pid = 0 means no parent. It returns the unique channelz tracking id
  144. // assigned to this channel.
  145. func RegisterChannel(c Channel, pid int64, ref string) int64 {
  146. id := idGen.genID()
  147. cn := &channel{
  148. refName: ref,
  149. c: c,
  150. subChans: make(map[int64]string),
  151. nestedChans: make(map[int64]string),
  152. id: id,
  153. pid: pid,
  154. trace: &channelTrace{createdTime: time.Now(), events: make([]*TraceEvent, 0, getMaxTraceEntry())},
  155. }
  156. if pid == 0 {
  157. db.get().addChannel(id, cn, true, pid, ref)
  158. } else {
  159. db.get().addChannel(id, cn, false, pid, ref)
  160. }
  161. return id
  162. }
  163. // RegisterSubChannel registers the given channel c in channelz database with ref
  164. // as its reference name, and add it to the child list of its parent (identified
  165. // by pid). It returns the unique channelz tracking id assigned to this subchannel.
  166. func RegisterSubChannel(c Channel, pid int64, ref string) int64 {
  167. if pid == 0 {
  168. grpclog.Error("a SubChannel's parent id cannot be 0")
  169. return 0
  170. }
  171. id := idGen.genID()
  172. sc := &subChannel{
  173. refName: ref,
  174. c: c,
  175. sockets: make(map[int64]string),
  176. id: id,
  177. pid: pid,
  178. trace: &channelTrace{createdTime: time.Now(), events: make([]*TraceEvent, 0, getMaxTraceEntry())},
  179. }
  180. db.get().addSubChannel(id, sc, pid, ref)
  181. return id
  182. }
  183. // RegisterServer registers the given server s in channelz database. It returns
  184. // the unique channelz tracking id assigned to this server.
  185. func RegisterServer(s Server, ref string) int64 {
  186. id := idGen.genID()
  187. svr := &server{
  188. refName: ref,
  189. s: s,
  190. sockets: make(map[int64]string),
  191. listenSockets: make(map[int64]string),
  192. id: id,
  193. }
  194. db.get().addServer(id, svr)
  195. return id
  196. }
  197. // RegisterListenSocket registers the given listen socket s in channelz database
  198. // with ref as its reference name, and add it to the child list of its parent
  199. // (identified by pid). It returns the unique channelz tracking id assigned to
  200. // this listen socket.
  201. func RegisterListenSocket(s Socket, pid int64, ref string) int64 {
  202. if pid == 0 {
  203. grpclog.Error("a ListenSocket's parent id cannot be 0")
  204. return 0
  205. }
  206. id := idGen.genID()
  207. ls := &listenSocket{refName: ref, s: s, id: id, pid: pid}
  208. db.get().addListenSocket(id, ls, pid, ref)
  209. return id
  210. }
  211. // RegisterNormalSocket registers the given normal socket s in channelz database
  212. // with ref as its reference name, and add it to the child list of its parent
  213. // (identified by pid). It returns the unique channelz tracking id assigned to
  214. // this normal socket.
  215. func RegisterNormalSocket(s Socket, pid int64, ref string) int64 {
  216. if pid == 0 {
  217. grpclog.Error("a NormalSocket's parent id cannot be 0")
  218. return 0
  219. }
  220. id := idGen.genID()
  221. ns := &normalSocket{refName: ref, s: s, id: id, pid: pid}
  222. db.get().addNormalSocket(id, ns, pid, ref)
  223. return id
  224. }
  225. // RemoveEntry removes an entry with unique channelz trakcing id to be id from
  226. // channelz database.
  227. func RemoveEntry(id int64) {
  228. db.get().removeEntry(id)
  229. }
  230. // TraceEventDesc is what the caller of AddTraceEvent should provide to describe the event to be added
  231. // to the channel trace.
  232. // The Parent field is optional. It is used for event that will be recorded in the entity's parent
  233. // trace also.
  234. type TraceEventDesc struct {
  235. Desc string
  236. Severity Severity
  237. Parent *TraceEventDesc
  238. }
  239. // AddTraceEvent adds trace related to the entity with specified id, using the provided TraceEventDesc.
  240. func AddTraceEvent(id int64, desc *TraceEventDesc) {
  241. if getMaxTraceEntry() == 0 {
  242. return
  243. }
  244. db.get().traceEvent(id, desc)
  245. }
  246. // channelMap is the storage data structure for channelz.
  247. // Methods of channelMap can be divided in two two categories with respect to locking.
  248. // 1. Methods acquire the global lock.
  249. // 2. Methods that can only be called when global lock is held.
  250. // A second type of method need always to be called inside a first type of method.
  251. type channelMap struct {
  252. mu sync.RWMutex
  253. topLevelChannels map[int64]struct{}
  254. servers map[int64]*server
  255. channels map[int64]*channel
  256. subChannels map[int64]*subChannel
  257. listenSockets map[int64]*listenSocket
  258. normalSockets map[int64]*normalSocket
  259. }
  260. func (c *channelMap) addServer(id int64, s *server) {
  261. c.mu.Lock()
  262. s.cm = c
  263. c.servers[id] = s
  264. c.mu.Unlock()
  265. }
  266. func (c *channelMap) addChannel(id int64, cn *channel, isTopChannel bool, pid int64, ref string) {
  267. c.mu.Lock()
  268. cn.cm = c
  269. cn.trace.cm = c
  270. c.channels[id] = cn
  271. if isTopChannel {
  272. c.topLevelChannels[id] = struct{}{}
  273. } else {
  274. c.findEntry(pid).addChild(id, cn)
  275. }
  276. c.mu.Unlock()
  277. }
  278. func (c *channelMap) addSubChannel(id int64, sc *subChannel, pid int64, ref string) {
  279. c.mu.Lock()
  280. sc.cm = c
  281. sc.trace.cm = c
  282. c.subChannels[id] = sc
  283. c.findEntry(pid).addChild(id, sc)
  284. c.mu.Unlock()
  285. }
  286. func (c *channelMap) addListenSocket(id int64, ls *listenSocket, pid int64, ref string) {
  287. c.mu.Lock()
  288. ls.cm = c
  289. c.listenSockets[id] = ls
  290. c.findEntry(pid).addChild(id, ls)
  291. c.mu.Unlock()
  292. }
  293. func (c *channelMap) addNormalSocket(id int64, ns *normalSocket, pid int64, ref string) {
  294. c.mu.Lock()
  295. ns.cm = c
  296. c.normalSockets[id] = ns
  297. c.findEntry(pid).addChild(id, ns)
  298. c.mu.Unlock()
  299. }
  300. // removeEntry triggers the removal of an entry, which may not indeed delete the entry, if it has to
  301. // wait on the deletion of its children and until no other entity's channel trace references it.
  302. // It may lead to a chain of entry deletion. For example, deleting the last socket of a gracefully
  303. // shutting down server will lead to the server being also deleted.
  304. func (c *channelMap) removeEntry(id int64) {
  305. c.mu.Lock()
  306. c.findEntry(id).triggerDelete()
  307. c.mu.Unlock()
  308. }
  309. // c.mu must be held by the caller
  310. func (c *channelMap) decrTraceRefCount(id int64) {
  311. e := c.findEntry(id)
  312. if v, ok := e.(tracedChannel); ok {
  313. v.decrTraceRefCount()
  314. e.deleteSelfIfReady()
  315. }
  316. }
  317. // c.mu must be held by the caller.
  318. func (c *channelMap) findEntry(id int64) entry {
  319. var v entry
  320. var ok bool
  321. if v, ok = c.channels[id]; ok {
  322. return v
  323. }
  324. if v, ok = c.subChannels[id]; ok {
  325. return v
  326. }
  327. if v, ok = c.servers[id]; ok {
  328. return v
  329. }
  330. if v, ok = c.listenSockets[id]; ok {
  331. return v
  332. }
  333. if v, ok = c.normalSockets[id]; ok {
  334. return v
  335. }
  336. return &dummyEntry{idNotFound: id}
  337. }
  338. // c.mu must be held by the caller
  339. // deleteEntry simply deletes an entry from the channelMap. Before calling this
  340. // method, caller must check this entry is ready to be deleted, i.e removeEntry()
  341. // has been called on it, and no children still exist.
  342. // Conditionals are ordered by the expected frequency of deletion of each entity
  343. // type, in order to optimize performance.
  344. func (c *channelMap) deleteEntry(id int64) {
  345. var ok bool
  346. if _, ok = c.normalSockets[id]; ok {
  347. delete(c.normalSockets, id)
  348. return
  349. }
  350. if _, ok = c.subChannels[id]; ok {
  351. delete(c.subChannels, id)
  352. return
  353. }
  354. if _, ok = c.channels[id]; ok {
  355. delete(c.channels, id)
  356. delete(c.topLevelChannels, id)
  357. return
  358. }
  359. if _, ok = c.listenSockets[id]; ok {
  360. delete(c.listenSockets, id)
  361. return
  362. }
  363. if _, ok = c.servers[id]; ok {
  364. delete(c.servers, id)
  365. return
  366. }
  367. }
  368. func (c *channelMap) traceEvent(id int64, desc *TraceEventDesc) {
  369. c.mu.Lock()
  370. child := c.findEntry(id)
  371. childTC, ok := child.(tracedChannel)
  372. if !ok {
  373. c.mu.Unlock()
  374. return
  375. }
  376. childTC.getChannelTrace().append(&TraceEvent{Desc: desc.Desc, Severity: desc.Severity, Timestamp: time.Now()})
  377. if desc.Parent != nil {
  378. parent := c.findEntry(child.getParentID())
  379. var chanType RefChannelType
  380. switch child.(type) {
  381. case *channel:
  382. chanType = RefChannel
  383. case *subChannel:
  384. chanType = RefSubChannel
  385. }
  386. if parentTC, ok := parent.(tracedChannel); ok {
  387. parentTC.getChannelTrace().append(&TraceEvent{
  388. Desc: desc.Parent.Desc,
  389. Severity: desc.Parent.Severity,
  390. Timestamp: time.Now(),
  391. RefID: id,
  392. RefName: childTC.getRefName(),
  393. RefType: chanType,
  394. })
  395. childTC.incrTraceRefCount()
  396. }
  397. }
  398. c.mu.Unlock()
  399. }
  400. type int64Slice []int64
  401. func (s int64Slice) Len() int { return len(s) }
  402. func (s int64Slice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
  403. func (s int64Slice) Less(i, j int) bool { return s[i] < s[j] }
  404. func copyMap(m map[int64]string) map[int64]string {
  405. n := make(map[int64]string)
  406. for k, v := range m {
  407. n[k] = v
  408. }
  409. return n
  410. }
  411. func min(a, b int64) int64 {
  412. if a < b {
  413. return a
  414. }
  415. return b
  416. }
  417. func (c *channelMap) GetTopChannels(id int64, maxResults int64) ([]*ChannelMetric, bool) {
  418. if maxResults <= 0 {
  419. maxResults = EntryPerPage
  420. }
  421. c.mu.RLock()
  422. l := int64(len(c.topLevelChannels))
  423. ids := make([]int64, 0, l)
  424. cns := make([]*channel, 0, min(l, maxResults))
  425. for k := range c.topLevelChannels {
  426. ids = append(ids, k)
  427. }
  428. sort.Sort(int64Slice(ids))
  429. idx := sort.Search(len(ids), func(i int) bool { return ids[i] >= id })
  430. count := int64(0)
  431. var end bool
  432. var t []*ChannelMetric
  433. for i, v := range ids[idx:] {
  434. if count == maxResults {
  435. break
  436. }
  437. if cn, ok := c.channels[v]; ok {
  438. cns = append(cns, cn)
  439. t = append(t, &ChannelMetric{
  440. NestedChans: copyMap(cn.nestedChans),
  441. SubChans: copyMap(cn.subChans),
  442. })
  443. count++
  444. }
  445. if i == len(ids[idx:])-1 {
  446. end = true
  447. break
  448. }
  449. }
  450. c.mu.RUnlock()
  451. if count == 0 {
  452. end = true
  453. }
  454. for i, cn := range cns {
  455. t[i].ChannelData = cn.c.ChannelzMetric()
  456. t[i].ID = cn.id
  457. t[i].RefName = cn.refName
  458. t[i].Trace = cn.trace.dumpData()
  459. }
  460. return t, end
  461. }
  462. func (c *channelMap) GetServers(id, maxResults int64) ([]*ServerMetric, bool) {
  463. if maxResults <= 0 {
  464. maxResults = EntryPerPage
  465. }
  466. c.mu.RLock()
  467. l := int64(len(c.servers))
  468. ids := make([]int64, 0, l)
  469. ss := make([]*server, 0, min(l, maxResults))
  470. for k := range c.servers {
  471. ids = append(ids, k)
  472. }
  473. sort.Sort(int64Slice(ids))
  474. idx := sort.Search(len(ids), func(i int) bool { return ids[i] >= id })
  475. count := int64(0)
  476. var end bool
  477. var s []*ServerMetric
  478. for i, v := range ids[idx:] {
  479. if count == maxResults {
  480. break
  481. }
  482. if svr, ok := c.servers[v]; ok {
  483. ss = append(ss, svr)
  484. s = append(s, &ServerMetric{
  485. ListenSockets: copyMap(svr.listenSockets),
  486. })
  487. count++
  488. }
  489. if i == len(ids[idx:])-1 {
  490. end = true
  491. break
  492. }
  493. }
  494. c.mu.RUnlock()
  495. if count == 0 {
  496. end = true
  497. }
  498. for i, svr := range ss {
  499. s[i].ServerData = svr.s.ChannelzMetric()
  500. s[i].ID = svr.id
  501. s[i].RefName = svr.refName
  502. }
  503. return s, end
  504. }
  505. func (c *channelMap) GetServerSockets(id int64, startID int64, maxResults int64) ([]*SocketMetric, bool) {
  506. if maxResults <= 0 {
  507. maxResults = EntryPerPage
  508. }
  509. var svr *server
  510. var ok bool
  511. c.mu.RLock()
  512. if svr, ok = c.servers[id]; !ok {
  513. // server with id doesn't exist.
  514. c.mu.RUnlock()
  515. return nil, true
  516. }
  517. svrskts := svr.sockets
  518. l := int64(len(svrskts))
  519. ids := make([]int64, 0, l)
  520. sks := make([]*normalSocket, 0, min(l, maxResults))
  521. for k := range svrskts {
  522. ids = append(ids, k)
  523. }
  524. sort.Sort(int64Slice(ids))
  525. idx := sort.Search(len(ids), func(i int) bool { return ids[i] >= startID })
  526. count := int64(0)
  527. var end bool
  528. for i, v := range ids[idx:] {
  529. if count == maxResults {
  530. break
  531. }
  532. if ns, ok := c.normalSockets[v]; ok {
  533. sks = append(sks, ns)
  534. count++
  535. }
  536. if i == len(ids[idx:])-1 {
  537. end = true
  538. break
  539. }
  540. }
  541. c.mu.RUnlock()
  542. if count == 0 {
  543. end = true
  544. }
  545. var s []*SocketMetric
  546. for _, ns := range sks {
  547. sm := &SocketMetric{}
  548. sm.SocketData = ns.s.ChannelzMetric()
  549. sm.ID = ns.id
  550. sm.RefName = ns.refName
  551. s = append(s, sm)
  552. }
  553. return s, end
  554. }
  555. func (c *channelMap) GetChannel(id int64) *ChannelMetric {
  556. cm := &ChannelMetric{}
  557. var cn *channel
  558. var ok bool
  559. c.mu.RLock()
  560. if cn, ok = c.channels[id]; !ok {
  561. // channel with id doesn't exist.
  562. c.mu.RUnlock()
  563. return nil
  564. }
  565. cm.NestedChans = copyMap(cn.nestedChans)
  566. cm.SubChans = copyMap(cn.subChans)
  567. // cn.c can be set to &dummyChannel{} when deleteSelfFromMap is called. Save a copy of cn.c when
  568. // holding the lock to prevent potential data race.
  569. chanCopy := cn.c
  570. c.mu.RUnlock()
  571. cm.ChannelData = chanCopy.ChannelzMetric()
  572. cm.ID = cn.id
  573. cm.RefName = cn.refName
  574. cm.Trace = cn.trace.dumpData()
  575. return cm
  576. }
  577. func (c *channelMap) GetSubChannel(id int64) *SubChannelMetric {
  578. cm := &SubChannelMetric{}
  579. var sc *subChannel
  580. var ok bool
  581. c.mu.RLock()
  582. if sc, ok = c.subChannels[id]; !ok {
  583. // subchannel with id doesn't exist.
  584. c.mu.RUnlock()
  585. return nil
  586. }
  587. cm.Sockets = copyMap(sc.sockets)
  588. // sc.c can be set to &dummyChannel{} when deleteSelfFromMap is called. Save a copy of sc.c when
  589. // holding the lock to prevent potential data race.
  590. chanCopy := sc.c
  591. c.mu.RUnlock()
  592. cm.ChannelData = chanCopy.ChannelzMetric()
  593. cm.ID = sc.id
  594. cm.RefName = sc.refName
  595. cm.Trace = sc.trace.dumpData()
  596. return cm
  597. }
  598. func (c *channelMap) GetSocket(id int64) *SocketMetric {
  599. sm := &SocketMetric{}
  600. c.mu.RLock()
  601. if ls, ok := c.listenSockets[id]; ok {
  602. c.mu.RUnlock()
  603. sm.SocketData = ls.s.ChannelzMetric()
  604. sm.ID = ls.id
  605. sm.RefName = ls.refName
  606. return sm
  607. }
  608. if ns, ok := c.normalSockets[id]; ok {
  609. c.mu.RUnlock()
  610. sm.SocketData = ns.s.ChannelzMetric()
  611. sm.ID = ns.id
  612. sm.RefName = ns.refName
  613. return sm
  614. }
  615. c.mu.RUnlock()
  616. return nil
  617. }
  618. func (c *channelMap) GetServer(id int64) *ServerMetric {
  619. sm := &ServerMetric{}
  620. var svr *server
  621. var ok bool
  622. c.mu.RLock()
  623. if svr, ok = c.servers[id]; !ok {
  624. c.mu.RUnlock()
  625. return nil
  626. }
  627. sm.ListenSockets = copyMap(svr.listenSockets)
  628. c.mu.RUnlock()
  629. sm.ID = svr.id
  630. sm.RefName = svr.refName
  631. sm.ServerData = svr.s.ChannelzMetric()
  632. return sm
  633. }
  634. type idGenerator struct {
  635. id int64
  636. }
  637. func (i *idGenerator) reset() {
  638. atomic.StoreInt64(&i.id, 0)
  639. }
  640. func (i *idGenerator) genID() int64 {
  641. return atomic.AddInt64(&i.id, 1)
  642. }