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  }