go.ligato.io/vpp-agent/v3@v3.5.0/plugins/kvscheduler/internal/graph/graph_read.go (about)

     1  // Copyright (c) 2018 Cisco and/or its affiliates.
     2  //
     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  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package graph
    16  
    17  import (
    18  	"fmt"
    19  	"sort"
    20  	"strings"
    21  	"time"
    22  
    23  	"go.ligato.io/cn-infra/v2/idxmap"
    24  
    25  	. "go.ligato.io/vpp-agent/v3/plugins/kvscheduler/api"
    26  	"go.ligato.io/vpp-agent/v3/plugins/kvscheduler/internal/utils"
    27  )
    28  
    29  // printDelimiter is used in pretty-printing of the graph.
    30  const printDelimiter = ", "
    31  
    32  // edge lookup re-used between benchmark scale tests.
    33  var benchEl *edgeLookup
    34  
    35  // graphR implements ReadAccess.
    36  type graphR struct {
    37  	*edgeLookup
    38  
    39  	parent *kvgraph
    40  
    41  	nodes    map[string]*node
    42  	mappings map[string]idxmap.NamedMappingRW
    43  	timeline map[string][]*RecordedNode // key -> node records (from the oldest to the newest)
    44  
    45  	wCopy   bool
    46  	unsaved utils.KeySet
    47  }
    48  
    49  // newGraphR creates and initializes a new instance of graphR.
    50  func newGraphR(mt MethodTracker) *graphR {
    51  	var el *edgeLookup
    52  	if benchEl != nil {
    53  		// this is a benchmark
    54  		el = benchEl
    55  		el.reset()
    56  	} else {
    57  		el = newEdgeLookup(mt)
    58  	}
    59  	return &graphR{
    60  		edgeLookup: el,
    61  		nodes:      make(map[string]*node),
    62  		mappings:   make(map[string]idxmap.NamedMappingRW),
    63  		timeline:   make(map[string][]*RecordedNode),
    64  	}
    65  }
    66  
    67  // GetMetadataMap returns registered metadata map.
    68  func (graph *graphR) GetMetadataMap(mapName string) idxmap.NamedMapping {
    69  	metadataMap, has := graph.mappings[mapName]
    70  	if !has {
    71  		return nil
    72  	}
    73  	return metadataMap
    74  }
    75  
    76  // GetNode returns node with the given key or nil if the key is unused.
    77  func (graph *graphR) GetNode(key string) Node {
    78  	node, has := graph.nodes[key]
    79  	if !has {
    80  		return nil
    81  	}
    82  	return node.nodeR
    83  }
    84  
    85  // GetNodes returns a set of nodes matching the key selector (can be nil)
    86  // and every provided flag selector.
    87  func (graph *graphR) GetNodes(keySelector KeySelector, flagSelectors ...FlagSelector) (nodes []Node) {
    88  	if graph.parent.methodTracker != nil {
    89  		defer graph.parent.methodTracker("GetNodes")()
    90  	}
    91  
    92  	for key, node := range graph.nodes {
    93  		if keySelector != nil && !keySelector(key) {
    94  			continue
    95  		}
    96  		selected := true
    97  		for _, flagSelector := range flagSelectors {
    98  			for _, flag := range flagSelector.flags {
    99  				nodeFlag := node.flags[flag.GetIndex()]
   100  				hasFlag := nodeFlag != nil &&
   101  					(flag.GetValue() == "" || (nodeFlag.GetValue() == flag.GetValue()))
   102  				if hasFlag != flagSelector.with {
   103  					selected = false
   104  					break
   105  				}
   106  			}
   107  			if !selected {
   108  				break
   109  			}
   110  		}
   111  		if !selected {
   112  			continue
   113  		}
   114  		nodes = append(nodes, node.nodeR)
   115  	}
   116  	return nodes
   117  }
   118  
   119  // GetNodeTimeline returns timeline of all node revisions, ordered from
   120  // the oldest to the newest.
   121  func (graph *graphR) GetNodeTimeline(key string) []*RecordedNode {
   122  	timeline, has := graph.timeline[key]
   123  	if !has {
   124  		return nil
   125  	}
   126  	return timeline
   127  }
   128  
   129  // GetFlagStats returns stats for a given flag.
   130  func (graph *graphR) GetFlagStats(flagIndex int, selector KeySelector) FlagStats {
   131  	if graph.parent.methodTracker != nil {
   132  		defer graph.parent.methodTracker("GetFlagStats")()
   133  	}
   134  
   135  	stats := FlagStats{PerValueCount: make(map[string]uint)}
   136  
   137  	for key, timeline := range graph.timeline {
   138  		if selector != nil && !selector(key) {
   139  			continue
   140  		}
   141  		for /*idx*/ _, record := range timeline {
   142  			if record.TargetUpdateOnly {
   143  				continue
   144  			}
   145  			if flag := record.Flags.GetFlag(flagIndex); flag != nil {
   146  				//fmt.Printf("Found flag %s/%s in %dth record of %s\n", flagName, flag.GetValue(), idx, record.Key)
   147  				flagValue := flag.GetValue()
   148  				stats.TotalCount++
   149  				if _, hasValue := stats.PerValueCount[flagValue]; !hasValue {
   150  					stats.PerValueCount[flagValue] = 0
   151  				}
   152  				stats.PerValueCount[flagValue]++
   153  			}
   154  		}
   155  	}
   156  
   157  	return stats
   158  }
   159  
   160  // GetSnapshot returns the snapshot of the graph at a given time.
   161  func (graph *graphR) GetSnapshot(time time.Time) (nodes []*RecordedNode) {
   162  	if graph.parent.methodTracker != nil {
   163  		defer graph.parent.methodTracker("GetSnapshot")()
   164  	}
   165  
   166  	for _, timeline := range graph.timeline {
   167  		for _, record := range timeline {
   168  			if record.Since.Before(time) &&
   169  				(record.Until.IsZero() || record.Until.After(time)) {
   170  				nodes = append(nodes, record)
   171  				break
   172  			}
   173  		}
   174  	}
   175  	return nodes
   176  }
   177  
   178  // GetKeys returns sorted keys.
   179  func (graph *graphR) GetKeys() []string {
   180  	if graph.parent.methodTracker != nil {
   181  		defer graph.parent.methodTracker("GetKeys")()
   182  	}
   183  
   184  	var keys []string
   185  	for key := range graph.nodes {
   186  		keys = append(keys, key)
   187  	}
   188  	sort.Slice(keys, func(i, j int) bool {
   189  		return keys[i] < keys[j]
   190  	})
   191  	return keys
   192  }
   193  
   194  // Dump returns a human-readable string representation of the current graph
   195  // content for debugging purposes.
   196  func (graph *graphR) Dump() string {
   197  	if graph.parent.methodTracker != nil {
   198  		defer graph.parent.methodTracker("Dump")()
   199  	}
   200  
   201  	// order nodes by keys
   202  	var keys []string
   203  	for key := range graph.nodes {
   204  		keys = append(keys, key)
   205  	}
   206  	sort.Slice(keys, func(i, j int) bool {
   207  		return keys[i] < keys[j]
   208  	})
   209  
   210  	var buf strings.Builder
   211  	graphInfo := fmt.Sprintf("%d nodes", len(keys))
   212  	buf.WriteString("+======================================================================================================================+\n")
   213  	buf.WriteString(fmt.Sprintf("| GRAPH DUMP %105s |\n", graphInfo))
   214  	buf.WriteString("+======================================================================================================================+\n")
   215  
   216  	for i, key := range keys {
   217  		node := graph.nodes[key]
   218  
   219  		buf.WriteString(fmt.Sprintf("| Key: %111q |\n", key))
   220  		if label := node.GetLabel(); label != key {
   221  			buf.WriteString(fmt.Sprintf("| Label: %109s |\n", label))
   222  		}
   223  		buf.WriteString(fmt.Sprintf("| Value: %109s |\n", utils.ProtoToString(node.GetValue())))
   224  		buf.WriteString(fmt.Sprintf("| Flags: %109v |\n", prettyPrintFlags(node.flags)))
   225  		if len(node.targets) > 0 {
   226  			buf.WriteString(fmt.Sprintf("| Targets: %107v |\n", prettyPrintTargets(node.targets)))
   227  		}
   228  		if len(node.sources) > 0 {
   229  			buf.WriteString(fmt.Sprintf("| Sources: %107v |\n", prettyPrintTargets(node.sources)))
   230  		}
   231  		if metadata := graph.getMetadataFields(node); len(metadata) > 0 {
   232  			buf.WriteString(fmt.Sprintf("| Metadata: %106v |\n", metadata))
   233  		}
   234  		if i+1 != len(keys) {
   235  			buf.WriteString("+----------------------------------------------------------------------------------------------------------------------+\n")
   236  		}
   237  	}
   238  	buf.WriteString("+----------------------------------------------------------------------------------------------------------------------+\n")
   239  
   240  	return buf.String()
   241  }
   242  
   243  // Release releases the graph handle (both Read() & Write() should end with
   244  // release).
   245  func (graph *graphR) Release() {
   246  	graph.parent.rwLock.RUnlock()
   247  }
   248  
   249  // ValidateEdges checks if targets and sources of all nodes correspond with each
   250  // other.
   251  // Use only for UTs, debugging, etc.
   252  func (graph *graphR) ValidateEdges() error {
   253  	for key, node := range graph.nodes {
   254  		// validate targets
   255  		for _, target := range node.targets {
   256  			for _, targetKey := range target.MatchingKeys.Iterate() {
   257  				targetNode, ok := graph.nodes[targetKey]
   258  				if !ok {
   259  					return fmt.Errorf("broken target %s -> %s", key, targetKey)
   260  				}
   261  				source, _ := targetNode.sources.GetTargetForLabel(target.Relation, target.Label)
   262  				if source == nil || !source.MatchingKeys.Has(key) {
   263  					return fmt.Errorf("missing source for target %s -> %s", key, targetKey)
   264  				}
   265  			}
   266  		}
   267  		// validate sources
   268  		for _, source := range node.sources {
   269  			for _, sourceKey := range source.MatchingKeys.Iterate() {
   270  				sourceNode, ok := graph.nodes[sourceKey]
   271  				if !ok {
   272  					return fmt.Errorf("broken source %s -> %s", key, sourceKey)
   273  				}
   274  				target, _ := sourceNode.targets.GetTargetForLabel(source.Relation, source.Label)
   275  				if target == nil || !target.MatchingKeys.Has(key) {
   276  					return fmt.Errorf("missing target for source %s -> %s", key, sourceKey)
   277  				}
   278  			}
   279  		}
   280  	}
   281  	return nil
   282  }
   283  
   284  // copyNodesOnly returns a deep-copy of the graph, excluding the timelines
   285  // and the map with mappings.
   286  func (graph *graphR) copyNodesOnly() *graphR {
   287  	graphCopy := &graphR{
   288  		edgeLookup: graph.edgeLookup.makeOverlay(),
   289  		parent:     graph.parent,
   290  		nodes:      make(map[string]*node),
   291  		wCopy:      true,
   292  	}
   293  	for key, node := range graph.nodes {
   294  		nodeCopy := node.copy()
   295  		nodeCopy.graph = graphCopy
   296  		graphCopy.nodes[key] = newNode(nodeCopy)
   297  	}
   298  	return graphCopy
   299  }
   300  
   301  // recordNode builds a record for the node to be added into the timeline.
   302  func (graph *graphR) recordNode(node *node, targetUpdateOnly bool) *RecordedNode {
   303  	targets := node.targets
   304  	node.targets = targets.copy() // COW for node, original for record
   305  	record := &RecordedNode{
   306  		Since:            time.Now(),
   307  		Key:              node.key,
   308  		Label:            node.label,
   309  		Value:            utils.RecordProtoMessage(node.value),
   310  		Flags:            RecordedFlags{Flags: node.flags},
   311  		MetadataFields:   graph.getMetadataFields(node), // returned is already copied
   312  		Targets:          targets,
   313  		TargetUpdateOnly: targetUpdateOnly,
   314  	}
   315  	return record
   316  }
   317  
   318  // getMetadataFields returns secondary fields from metadata attached to the given node.
   319  func (graph *graphR) getMetadataFields(node *node) map[string][]string {
   320  	writeCopy := graph.parent.graph != graph
   321  	if !writeCopy && node.metadataAdded {
   322  		mapping := graph.mappings[node.metadataMap]
   323  		return mapping.ListFields(node.label)
   324  	}
   325  	return nil
   326  }
   327  
   328  // prettyPrintFlags returns nicely formatted string representation of the given list of flags.
   329  func prettyPrintFlags(flags [maxFlags]Flag) string {
   330  	var str string
   331  	for _, flag := range flags {
   332  		if flag == nil {
   333  			continue
   334  		}
   335  		if str != "" {
   336  			str += printDelimiter
   337  		}
   338  		if flag.GetValue() == "" {
   339  			str += flag.GetName()
   340  		} else {
   341  			str += fmt.Sprintf("%s:<%s>", flag.GetName(), flag.GetValue())
   342  		}
   343  	}
   344  	return str
   345  }
   346  
   347  // prettyPrintTargets returns nicely formatted relation targets.
   348  func prettyPrintTargets(targets Targets) string {
   349  	if len(targets) == 0 {
   350  		return "<NONE>"
   351  	}
   352  	idx := 0
   353  	relation := targets[0].Relation
   354  	str := fmt.Sprintf("[%s]{", relation)
   355  	for _, target := range targets {
   356  		if target.Relation != relation {
   357  			relation = target.Relation
   358  			str += fmt.Sprintf("}%s[%s]{", printDelimiter, relation)
   359  			idx = 0
   360  		}
   361  		if idx > 0 {
   362  			str += printDelimiter
   363  		}
   364  		if target.MatchingKeys.Length() == 1 && target.MatchingKeys.Has(target.Label) {
   365  			// special case: there 1:1 between label and the key
   366  			str += target.Label
   367  		} else {
   368  			str += target.Label + " -> " + target.MatchingKeys.String()
   369  		}
   370  		idx++
   371  	}
   372  	str += "}"
   373  	return str
   374  }