cache.go 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. /*
  2. Copyright 2017 Google Inc.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package client
  14. import (
  15. "fmt"
  16. log "github.com/golang/glog"
  17. "context"
  18. "notabug.org/themusicgod1/gnmi/ctree"
  19. )
  20. // Client adds a caching layer on top of a simple query client.
  21. //
  22. // It works similarly to BaseClient and adds the Leaves method to return
  23. // current tree state.
  24. type CacheClient struct {
  25. *BaseClient
  26. *ctree.Tree
  27. synced chan struct{}
  28. clientHandler NotificationHandler
  29. }
  30. var _ Client = &CacheClient{}
  31. // New returns an initialized caching client.
  32. func New() *CacheClient {
  33. c := &CacheClient{
  34. BaseClient: &BaseClient{},
  35. Tree: &ctree.Tree{},
  36. synced: make(chan struct{}),
  37. }
  38. return c
  39. }
  40. // Subscribe implements the Client interface.
  41. func (c *CacheClient) Subscribe(ctx context.Context, q Query, clientType ...string) error {
  42. q.ProtoHandler = nil
  43. if q.NotificationHandler != nil {
  44. c.clientHandler = q.NotificationHandler
  45. }
  46. q.NotificationHandler = c.defaultHandler
  47. return c.BaseClient.Subscribe(ctx, q, clientType...)
  48. }
  49. // defaultHandler is passed into the client specific implementations. It will
  50. // be called for each leaf notification generated by the client.
  51. func (c *CacheClient) defaultHandler(n Notification) error {
  52. switch v := n.(type) {
  53. default:
  54. return fmt.Errorf("invalid type %#v", v)
  55. case Connected: // Ignore.
  56. case Error:
  57. return fmt.Errorf("received error: %v", v)
  58. case Update:
  59. c.Add(v.Path, TreeVal{TS: v.TS, Val: v.Val})
  60. case Delete:
  61. c.Delete(v.Path)
  62. case Sync:
  63. select {
  64. default:
  65. close(c.synced)
  66. case <-c.synced:
  67. }
  68. }
  69. if c.clientHandler != nil {
  70. return c.clientHandler(n)
  71. }
  72. return nil
  73. }
  74. // Poll implements the Client interface.
  75. // Poll also closes the channel returned by Synced and resets it.
  76. func (c *CacheClient) Poll() error {
  77. select {
  78. default:
  79. close(c.synced)
  80. case <-c.synced:
  81. }
  82. return c.BaseClient.Poll()
  83. }
  84. // Synced will close when a sync is recieved from the query.
  85. func (c *CacheClient) Synced() <-chan struct{} {
  86. return c.synced
  87. }
  88. // Leaves returns the current state of the received tree. It's safe to call at
  89. // any point after New.
  90. func (c *CacheClient) Leaves() Leaves {
  91. // Convert node items into Leaf (expand TreeVal leaves).
  92. var pvs Leaves
  93. c.WalkSorted(func(path []string, _ *ctree.Leaf, value interface{}) {
  94. tv, ok := value.(TreeVal)
  95. if !ok {
  96. log.Errorf("Invalid value in tree: %s=%#v", path, value)
  97. return
  98. }
  99. pvs = append(pvs, Leaf{Path: path, Val: tv.Val, TS: tv.TS})
  100. })
  101. return pvs
  102. }