github.com/simpleiot/simpleiot@v0.18.3/client/node-tag-cache.go (about) 1 package client 2 3 import ( 4 "fmt" 5 "slices" 6 "sync" 7 8 "github.com/nats-io/nats.go" 9 "github.com/simpleiot/simpleiot/data" 10 ) 11 12 type tagEntry struct { 13 Type string // Point Type 14 Key string // Point Key 15 } 16 type nodeCacheEntry struct { 17 // Type is the cached node type 18 Type string 19 // Description is the cached node description 20 Description string 21 // Tags is a map of tags attached to this node, derived from the list of 22 // points with a Type matching one of the TagPointTypes. Keys are a 23 // concatenation of the point Type and point Key. Values are the point Text. 24 Tags map[tagEntry]string 25 } 26 type nodeCache struct { 27 // TagPointTypes is a slice of point types that are added as Influx tags 28 TagPointTypes []string 29 // Cache is a map of cache entries 30 Cache map[string]nodeCacheEntry 31 // Lock is the cache mutex 32 Lock *sync.RWMutex 33 } 34 35 // newNodeCache returns an initialized nodeCache 36 func newNodeCache(tagPointTypes []string) nodeCache { 37 tagPointTypes = slices.Clone(tagPointTypes) 38 slices.Sort(tagPointTypes) 39 return nodeCache{ 40 // We sort the slice, so we can use BinarySearch 41 TagPointTypes: tagPointTypes, 42 Cache: make(map[string]nodeCacheEntry), 43 Lock: new(sync.RWMutex), 44 } 45 } 46 47 // CopyTags finds the specified node in the cache and copies the node ID 48 // (into key "node.id"), the node description (into key "node.description"), 49 // the node type (into key "node.type"), and tags from the node's "tag" points 50 // (into key "node.tag.*" where * is the name of each tag) to the specified 51 // `tags` map, returning true if the node was found in the cache. If the node is 52 // not present in the cache, false is returned and tags is unmodified. 53 func (c nodeCache) CopyTags(nodeID string, tags map[string]string) bool { 54 c.Lock.RLock() 55 defer c.Lock.RUnlock() 56 57 entry, found := c.Cache[nodeID] 58 if !found { 59 return false 60 } 61 62 tags["node.id"] = nodeID 63 tags["node.description"] = entry.Description 64 tags["node.type"] = entry.Type 65 for tagEntry, val := range entry.Tags { 66 tags["node."+tagEntry.Type+"."+tagEntry.Key] = val 67 } 68 return true 69 } 70 71 // Update iterates through each Point and updates the cache. If a cache entry 72 // does not exist for the node, the node is retrieved, and the cache is 73 // subsequently updated. 74 func (c nodeCache) Update(nc *nats.Conn, pts NewPoints) error { 75 c.Lock.Lock() 76 defer c.Lock.Unlock() 77 78 entry, found := c.Cache[pts.ID] 79 if !found { 80 // We need to fetch the node and populate the cache 81 ne, err := GetNodes(nc, "all", pts.ID, "", false) 82 if err != nil { 83 return err 84 } 85 if len(ne) <= 0 { 86 return fmt.Errorf("Tag Cache, node of ID %v not found in DB", pts.ID) 87 } 88 entry.Type = ne[0].Type 89 entry.Tags = make(map[tagEntry]string) 90 for _, p := range ne[0].Points { 91 if p.Tombstone%2 == 1 { 92 continue 93 } 94 if p.Type == data.PointTypeDescription { 95 entry.Description = p.Text 96 } 97 if _, found := slices.BinarySearch(c.TagPointTypes, p.Type); found { 98 key := tagEntry{Type: p.Type, Key: p.Key} 99 entry.Tags[key] = p.Text 100 } 101 } 102 } 103 104 // Update the entry from the specified points 105 for _, p := range pts.Points { 106 if p.Type == data.PointTypeDescription { 107 if p.Tombstone%2 == 0 { 108 entry.Description = p.Text 109 } else { 110 entry.Description = "" 111 } 112 } 113 if _, found := slices.BinarySearch(c.TagPointTypes, p.Type); found { 114 key := tagEntry{Type: p.Type, Key: p.Key} 115 if p.Tombstone%2 == 0 && p.Text != "" { 116 entry.Tags[key] = p.Text 117 } else { 118 delete(entry.Tags, key) 119 } 120 } 121 } 122 c.Cache[pts.ID] = entry 123 124 return nil 125 } 126 127 // Clear deletes all cache entries 128 func (c *nodeCache) Clear() { 129 c.Lock.Lock() 130 defer c.Lock.Unlock() 131 132 c.Cache = make(map[string]nodeCacheEntry) 133 }