go.ligato.io/vpp-agent/v3@v3.5.0/plugins/kvscheduler/internal/graph/graph_api.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  	"bytes"
    19  	"fmt"
    20  	"sort"
    21  	"time"
    22  
    23  	"go.ligato.io/cn-infra/v2/idxmap"
    24  	"google.golang.org/protobuf/proto"
    25  
    26  	. "go.ligato.io/vpp-agent/v3/plugins/kvscheduler/api"
    27  	"go.ligato.io/vpp-agent/v3/plugins/kvscheduler/internal/utils"
    28  )
    29  
    30  // Graph is an in-memory graph representation of key-value pairs and their
    31  // relations, where nodes are kv-pairs and each relation is a separate set of
    32  // direct labeled edges.
    33  //
    34  // The graph furthermore allows to associate metadata and flags (idx/name:value
    35  // pairs) with every node. It is possible to register instances of NamedMapping,
    36  // each for a different set of selected nodes, and the graph will keep them
    37  // up-to-date with the latest value-label->metadata associations.
    38  //
    39  // The graph provides various getter method, for example it is possible to select
    40  // a set of nodes using a key selector and/or a flag selector.
    41  // As for editing, Graph allows to either write in-place (immediate effect)
    42  // or to prepare new changes and then save them later or let them get discarded
    43  // by GC.
    44  //
    45  // The graph supports multiple-readers single-writer access, i.e. it is assumed
    46  // there is no write-concurrency.
    47  //
    48  // Last but not least, the graph maintains a history of revisions for all nodes
    49  // that have ever existed. The history of changes and a graph snapshot from
    50  // a selected moment in time are exposed via ReadAccess interface.
    51  type Graph interface {
    52  	// Read returns a graph handle for read-only access.
    53  	// The graph supports multiple concurrent readers.
    54  	// Release eventually using Release() method.
    55  	Read() ReadAccess // acquires R-lock
    56  
    57  	// Write returns a graph handle for read-write access.
    58  	// The graph supports at most one writer at a time - i.e. it is assumed
    59  	// there is no write-concurrency.
    60  	// If <inPlace> is enabled, the changes are applied with immediate effect,
    61  	// otherwise they are propagated to the graph using Save().
    62  	// In-place Write handle holds write lock, therefore reading is blocked until
    63  	// the handle is released.
    64  	// If <record> is true, the changes will be recorded once the handle is
    65  	// released.
    66  	// Release eventually using Release() method.
    67  	Write(inPlace, record bool) RWAccess
    68  }
    69  
    70  // ReadAccess lists operations provided by the read-only graph handle.
    71  type ReadAccess interface {
    72  	// GetMetadataMap returns registered metadata map.
    73  	GetMetadataMap(mapName string) idxmap.NamedMapping
    74  
    75  	// GetKeys returns sorted keys.
    76  	GetKeys() []string
    77  
    78  	// GetNode returns node with the given key or nil if the key is unused.
    79  	GetNode(key string) Node
    80  
    81  	// GetNodes returns a set of nodes matching the key selector (can be nil)
    82  	// and every provided flag selector.
    83  	GetNodes(keySelector KeySelector, flagSelectors ...FlagSelector) []Node
    84  
    85  	// GetFlagStats returns stats for a given flag.
    86  	GetFlagStats(flagIndex int, filter KeySelector) FlagStats
    87  
    88  	// GetNodeTimeline returns timeline of all node revisions, ordered from
    89  	// the oldest to the newest.
    90  	GetNodeTimeline(key string) []*RecordedNode
    91  
    92  	// GetSnapshot returns the snapshot of the graph at a given time.
    93  	GetSnapshot(time time.Time) []*RecordedNode
    94  
    95  	// Dump returns a human-readable string representation of the current graph
    96  	// content for debugging purposes.
    97  	Dump() string
    98  
    99  	// Release releases the graph handle (both Read() & Write() should end with
   100  	// release).
   101  	// For reader, the method releases R-lock.
   102  	// For in-place writer, the method releases W-lock.
   103  	Release()
   104  
   105  	// ValidateEdges checks if targets and sources of all nodes correspond with
   106  	// each other.
   107  	// Use only for UTs, debugging, etc.
   108  	ValidateEdges() error
   109  }
   110  
   111  // RWAccess lists operations provided by the read-write graph handle.
   112  type RWAccess interface {
   113  	ReadAccess
   114  
   115  	// RegisterMetadataMap registers new metadata map for value-label->metadata
   116  	// associations of selected node.
   117  	RegisterMetadataMap(mapName string, mapping idxmap.NamedMappingRW)
   118  
   119  	// SetNode creates new node or returns read-write handle to an existing node.
   120  	// If in-place writing is disabled, the changes are propagated to the graph
   121  	// only after Save() is called.
   122  	SetNode(key string) NodeRW
   123  
   124  	// DeleteNode deletes node with the given key.
   125  	// Returns true if the node really existed before the operation.
   126  	DeleteNode(key string) bool
   127  
   128  	// Save propagates all changes to the graph.
   129  	// Use for **not-in-place** writing.
   130  	// NOOP if no changes performed, acquires RW-lock for the time of the operation
   131  	Save()
   132  }
   133  
   134  // TargetIterator is a callback applied on every target.
   135  // For each label it will be called n+1 times, where n is the number of targets
   136  // available for the given label and the extra call will be made with nil target.
   137  type TargetIterator func(target Node, label string) (skipLabel, abort bool)
   138  
   139  // Node is a read-only handle to a single graph node.
   140  type Node interface {
   141  	// GetKey returns the key associated with the node.
   142  	GetKey() string
   143  
   144  	// GetLabel returns the label associated with this node.
   145  	GetLabel() string
   146  
   147  	// GetValue returns the value associated with the node.
   148  	GetValue() proto.Message
   149  
   150  	// GetFlag returns reference to the given flag or nil if the node doesn't have
   151  	// this flag associated.
   152  	GetFlag(flagIndex int) Flag
   153  
   154  	// GetMetadata returns the value metadata associated with the node.
   155  	GetMetadata() interface{}
   156  
   157  	// GetTargets returns a set of nodes, indexed by relation labels, that the
   158  	// edges of the given relation points to.
   159  	GetTargets(relation string) RuntimeTargets
   160  
   161  	// IterTargets allows to iterate over the set of nodes that the edges of the given
   162  	// relation points to.
   163  	IterTargets(relation string, callback TargetIterator)
   164  
   165  	// GetSources returns edges pointing to this node in the reverse
   166  	// orientation.
   167  	GetSources(relation string) RuntimeTargets
   168  }
   169  
   170  // NodeRW is a read-write handle to a single graph node.
   171  type NodeRW interface {
   172  	Node
   173  
   174  	// SetLabel associates given label with this node.
   175  	SetLabel(label string)
   176  
   177  	// SetValue associates given value with this node.
   178  	SetValue(value proto.Message)
   179  
   180  	// SetFlags associates given flag with this node.
   181  	SetFlags(flags ...Flag)
   182  
   183  	// DelFlags removes given flags from this node.
   184  	DelFlags(flagIndexes ...int)
   185  
   186  	// SetMetadataMap chooses metadata map to be used to store the association
   187  	// between this node's value label and metadata.
   188  	SetMetadataMap(mapName string)
   189  
   190  	// SetMetadata associates given value metadata with this node.
   191  	SetMetadata(metadata interface{})
   192  
   193  	// SetTargets updates definitions of all edges pointing from this node.
   194  	SetTargets(targets []RelationTargetDef)
   195  }
   196  
   197  // Flag is a (index+name):value pair.
   198  type Flag interface {
   199  	// GetIndex should return unique index among all defined flags, starting
   200  	// from 0.
   201  	GetIndex() int
   202  
   203  	// GetName should return name of the flag.
   204  	GetName() string
   205  
   206  	// GetValue return the associated value. Can be empty.
   207  	GetValue() string
   208  }
   209  
   210  // FlagSelector is used to select node with(out) given flags assigned.
   211  //
   212  // Flag value=="" => any value
   213  type FlagSelector struct {
   214  	with  bool
   215  	flags []Flag
   216  }
   217  
   218  // WithFlags creates flag selector selecting nodes that have all the listed flags
   219  // assigned.
   220  func WithFlags(flags ...Flag) FlagSelector {
   221  	return FlagSelector{with: true, flags: flags}
   222  }
   223  
   224  // WithoutFlags creates flag selector selecting nodes that do not have
   225  // any of the listed flags assigned.
   226  func WithoutFlags(flags ...Flag) FlagSelector {
   227  	return FlagSelector{flags: flags}
   228  }
   229  
   230  // RelationTargetDef is a definition of a relation between a source node and a set
   231  // of target nodes.
   232  type RelationTargetDef struct {
   233  	// Relation name.
   234  	Relation string
   235  
   236  	// Label for the edge.
   237  	Label string // mandatory, unique for a given (source, relation)
   238  
   239  	// Either Key or Selector should be defined:
   240  
   241  	// Key of the target node.
   242  	Key string
   243  
   244  	// Selector selecting a set of target nodes.
   245  	Selector TargetSelector
   246  }
   247  
   248  // Compare compares two relation target definitions (with the exception of KeySelector-s).
   249  func (t RelationTargetDef) Compare(t2 RelationTargetDef) (equal bool, order int) {
   250  	if t.Relation < t2.Relation {
   251  		return false, -1
   252  	}
   253  	if t.Relation > t2.Relation {
   254  		return false, 1
   255  	}
   256  	if t.Label < t2.Label {
   257  		return false, -1
   258  	}
   259  	if t.Label > t2.Label {
   260  		return false, 1
   261  	}
   262  	if t.Key != t2.Key {
   263  		return false, 0
   264  	}
   265  	if len(t.Selector.KeyPrefixes) != len(t2.Selector.KeyPrefixes) {
   266  		return false, 0
   267  	}
   268  	for i := 0; i < len(t.Selector.KeyPrefixes); i++ {
   269  		if t.Selector.KeyPrefixes[i] != t2.Selector.KeyPrefixes[i] {
   270  			return false, 0
   271  		}
   272  	}
   273  	return true, 0
   274  }
   275  
   276  // WithKeySelector returns true if the target is defined with key selector.
   277  func (t RelationTargetDef) WithKeySelector() bool {
   278  	return t.Key == "" && t.Selector.KeySelector != nil
   279  }
   280  
   281  // Singleton returns true if the target matches at most one key.
   282  func (t RelationTargetDef) Singleton() bool {
   283  	return t.Key != "" || (t.Selector.KeySelector == nil && len(t.Selector.KeyPrefixes) == 0)
   284  }
   285  
   286  // TargetSelector allows to dynamically select a set of target nodes.
   287  // The selections of KeyPrefixes and KeySelector are **intersected**.
   288  type TargetSelector struct {
   289  	// KeyPrefixes is a list of key prefixes, each selecting a subset of target
   290  	// nodes, which are then combined together - i.e. **union** is computed.
   291  	KeyPrefixes []string
   292  
   293  	// KeySelector allows to dynamically select target nodes.
   294  	KeySelector KeySelector
   295  }
   296  
   297  // Target nodes - not referenced directly, instead via their keys (suitable
   298  // for recording).
   299  type Target struct {
   300  	Relation     string
   301  	Label        string
   302  	ExpectedKey  string // empty if Selector is used instead
   303  	MatchingKeys utils.KeySet
   304  }
   305  
   306  // Targets is a slice of all targets of a single node, sorted by relation+label
   307  // (in this order).
   308  type Targets []Target
   309  
   310  // String returns human-readable string representation of Targets.
   311  func (ts Targets) String() string {
   312  	var (
   313  		idx      int
   314  		str      string
   315  		relation string
   316  	)
   317  	if len(ts) > 0 {
   318  		relation = ts[0].Relation
   319  		str += relation + ":"
   320  	}
   321  	str += "{"
   322  	for _, target := range ts {
   323  		if target.Relation != relation {
   324  			relation = target.Relation
   325  			str += "} " + relation + ":{"
   326  			idx = 0
   327  		}
   328  		if idx > 0 {
   329  			str += ", "
   330  		}
   331  		str += fmt.Sprintf("%s->%s", target.Label, target.MatchingKeys.String())
   332  		idx++
   333  	}
   334  	str += "}"
   335  	return str
   336  }
   337  
   338  // RelationBegin returns index where targets for a given relation start
   339  // in the array, or len(ts) if there are none.
   340  func (ts Targets) RelationBegin(relation string) int {
   341  	idx := ts.lookupIdx(relation, "")
   342  	if idx < len(ts) && ts[idx].Relation == relation {
   343  		return idx
   344  	}
   345  	return len(ts)
   346  }
   347  
   348  // GetTargetForLabel returns reference(+index) to target with the given
   349  // relation+label.
   350  func (ts Targets) GetTargetForLabel(relation, label string) (t *Target, idx int) {
   351  	idx = ts.lookupIdx(relation, label)
   352  	if idx < len(ts) &&
   353  		ts[idx].Relation == relation && ts[idx].Label == label {
   354  		return &ts[idx], idx
   355  	}
   356  	return nil, idx
   357  }
   358  
   359  // lookupIdx returns index where target for the given (relation,label) pair should
   360  // be stored in the array.
   361  func (ts Targets) lookupIdx(relation, label string) int {
   362  	idx := sort.Search(len(ts),
   363  		func(i int) bool {
   364  			if relation < ts[i].Relation {
   365  				return true
   366  			}
   367  			if relation == ts[i].Relation && label <= ts[i].Label {
   368  				return true
   369  			}
   370  			return false
   371  		})
   372  	return idx
   373  }
   374  
   375  // copy returns deep copy of targets (key sets deep copied on write).
   376  func (ts Targets) copy() Targets {
   377  	tCopy := make(Targets, len(ts))
   378  	copy(tCopy, ts)
   379  	for i := range tCopy {
   380  		tCopy[i].MatchingKeys = ts[i].MatchingKeys.CopyOnWrite()
   381  	}
   382  	return tCopy
   383  }
   384  
   385  // RuntimeTarget, unlike Target, contains direct runtime references pointing
   386  // to instances of target nodes (suitable for runtime processing but not for
   387  // recording).
   388  type RuntimeTarget struct {
   389  	Label string
   390  	Nodes []Node
   391  }
   392  
   393  // RuntimeTargets is a slice of single-relation (runtime reference-based)
   394  // targets, grouped by labels.
   395  type RuntimeTargets []RuntimeTarget
   396  
   397  // GetTargetForLabel returns target (single node or a set of nodes) for
   398  // the given label.
   399  // Linear complexity is OK, it is used only in UTs.
   400  func (rt RuntimeTargets) GetTargetForLabel(label string) *RuntimeTarget {
   401  	for idx := range rt {
   402  		if rt[idx].Label == label {
   403  			return &rt[idx]
   404  		}
   405  	}
   406  	return nil
   407  }
   408  
   409  // RecordedNode saves all attributes of a single node revision.
   410  type RecordedNode struct {
   411  	Since            time.Time
   412  	Until            time.Time
   413  	Key              string
   414  	Label            string
   415  	Value            proto.Message
   416  	Flags            RecordedFlags
   417  	MetadataFields   map[string][]string // field name -> values
   418  	Targets          Targets
   419  	TargetUpdateOnly bool // true if only runtime Targets have changed since the last rev
   420  }
   421  
   422  // GetFlag returns reference to the given flag or nil if the node didn't have
   423  // this flag associated at the time when it was recorded.
   424  func (node *RecordedNode) GetFlag(flagIndex int) Flag {
   425  	return node.Flags.GetFlag(flagIndex)
   426  }
   427  
   428  // RecordedFlags is a record of assigned flags at a given time.
   429  type RecordedFlags struct {
   430  	Flags [maxFlags]Flag
   431  }
   432  
   433  // MarshalJSON marshalls recorded flags into JSON.
   434  func (rf RecordedFlags) MarshalJSON() ([]byte, error) {
   435  	buffer := bytes.NewBufferString("{")
   436  	first := true
   437  	for _, flag := range rf.Flags {
   438  		if flag == nil {
   439  			continue
   440  		}
   441  		if !first {
   442  			buffer.WriteString(",")
   443  		}
   444  		first = false
   445  		buffer.WriteString(fmt.Sprintf("\"%s\":\"%s\"", flag.GetName(), flag.GetValue()))
   446  	}
   447  	buffer.WriteString("}")
   448  	return buffer.Bytes(), nil
   449  }
   450  
   451  // GetFlag returns reference to the given flag or nil if the node hasn't had
   452  // this flag associated at the given time.
   453  func (rf RecordedFlags) GetFlag(flagIndex int) Flag {
   454  	return rf.Flags[flagIndex]
   455  }
   456  
   457  // FlagStats is a summary of the usage for a given flag.
   458  type FlagStats struct {
   459  	TotalCount    uint            // number of revisions with the given flag assigned
   460  	PerValueCount map[string]uint // number of revisions with the given flag having the given value
   461  }