go.ligato.io/vpp-agent/v3@v3.5.0/plugins/kvscheduler/refresh.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 kvscheduler
    16  
    17  import (
    18  	"fmt"
    19  	"os"
    20  	"strings"
    21  
    22  	"go.ligato.io/cn-infra/v2/logging"
    23  	"google.golang.org/protobuf/encoding/prototext"
    24  
    25  	kvs "go.ligato.io/vpp-agent/v3/plugins/kvscheduler/api"
    26  	"go.ligato.io/vpp-agent/v3/plugins/kvscheduler/internal/graph"
    27  	"go.ligato.io/vpp-agent/v3/plugins/kvscheduler/internal/utils"
    28  	"go.ligato.io/vpp-agent/v3/proto/ligato/kvscheduler"
    29  )
    30  
    31  var enableGraphDump = os.Getenv("KVSCHEDULER_GRAPHDUMP") != ""
    32  
    33  const (
    34  	nodeVisitBeginMark = "[BEGIN]"
    35  	nodeVisitEndMark   = "[END]"
    36  )
    37  
    38  // resyncData stores data to be used for resync after refresh.
    39  type resyncData struct {
    40  	first  bool // true if startup-resync
    41  	values []kvForTxn
    42  }
    43  
    44  // refreshGraph updates all/some values in the graph to their *real* state
    45  // using the Retrieve methods from descriptors.
    46  func (s *Scheduler) refreshGraph(graphW graph.RWAccess,
    47  	keys utils.KeySet, resyncData *resyncData, verbose bool,
    48  ) {
    49  	if s.logGraphWalk {
    50  		keysToRefresh := "<ALL>"
    51  		if keys != nil && keys.Length() > 0 {
    52  			keysToRefresh = keys.String()
    53  		}
    54  		msg := fmt.Sprintf("refreshGrap (keys=%s)", keysToRefresh)
    55  		fmt.Printf("%s %s\n", nodeVisitBeginMark, msg)
    56  		defer fmt.Printf("%s %s\n", nodeVisitEndMark, msg)
    57  	}
    58  	refreshedKeys := utils.NewMapBasedKeySet()
    59  
    60  	// iterate over all descriptors, in order given by retrieve dependencies
    61  	for _, descriptor := range s.registry.GetAllDescriptors() {
    62  		handler := newDescriptorHandler(descriptor)
    63  
    64  		// get base values for this descriptor from memory before refresh
    65  		// (including those marked as unavailable which may need metadata update)
    66  		descrNodes := graphW.GetNodes(nil,
    67  			descrValsSelectors(descriptor.Name, false)...)
    68  
    69  		// check if this descriptor's key space should be refreshed as well
    70  		var skip bool
    71  		if keys != nil {
    72  			skip = keys.Length() > 0
    73  			for _, key := range keys.Iterate() {
    74  				if descriptor.KeySelector(key) {
    75  					skip = false
    76  					break
    77  				}
    78  			}
    79  		}
    80  		if skip {
    81  			// nothing to refresh in the key space of this descriptor
    82  			s.skipRefresh(descrNodes, nil, refreshedKeys)
    83  			continue
    84  		}
    85  
    86  		// get key-value pairs for correlation
    87  		var correlateCap int
    88  		if resyncData != nil && resyncData.first {
    89  			correlateCap = len(resyncData.values)
    90  		} else {
    91  			correlateCap = len(descrNodes)
    92  		}
    93  		correlate := make([]kvs.KVWithMetadata, 0, correlateCap)
    94  		if resyncData != nil && resyncData.first {
    95  			// for startup resync, use data received from NB
    96  			for _, kv := range resyncData.values {
    97  				if descriptor.KeySelector(kv.key) {
    98  					correlate = append(correlate,
    99  						kvs.KVWithMetadata{
   100  							Key:    kv.key,
   101  							Value:  kv.value,
   102  							Origin: kv.origin,
   103  						})
   104  				}
   105  			}
   106  		} else {
   107  			// for refresh of failed values or run-time resync, use in-memory
   108  			// kv-pairs for correlation
   109  			for _, node := range descrNodes {
   110  				if isNodeAvailable(node) {
   111  					correlate = append(correlate, nodeToKVPairWithMetadata(node))
   112  				}
   113  			}
   114  		}
   115  
   116  		// execute Retrieve operation
   117  		retrieved, ableToRetrieve, err := handler.retrieve(correlate)
   118  
   119  		// mark un-retrievable as refreshed
   120  		if !ableToRetrieve || err != nil {
   121  			l := s.Log.WithField("descriptor", descriptor.Name)
   122  			if err != nil {
   123  				l.Errorf("failed to retrieve values: %v", err)
   124  				l.Debugf("skipping refresh for the descriptor")
   125  			}
   126  			s.skipRefresh(descrNodes, nil, refreshedKeys)
   127  			continue
   128  		} else if verbose {
   129  			plural := "s"
   130  			if len(retrieved) == 1 {
   131  				plural = ""
   132  			}
   133  
   134  			var list strings.Builder
   135  			for i, d := range retrieved {
   136  				num := fmt.Sprintf("%d.", i+1)
   137  				list.WriteString(fmt.Sprintf("\n - %3s [%s]: %q (%s)\n   %v",
   138  					num, descriptor.Name, d.Key, d.Origin, utils.ProtoToString(d.Value)))
   139  				if d.Metadata != nil {
   140  					list.WriteString(fmt.Sprintf("\n   Metadata: %+v", d.Metadata))
   141  				}
   142  			}
   143  			s.Log.Debugf("%s descriptor retrieved %d item%s: %v",
   144  				descriptor.Name, len(retrieved), plural, list.String())
   145  
   146  		}
   147  
   148  		if keys != nil && keys.Length() > 0 {
   149  			// mark keys that should not be touched as refreshed
   150  			s.skipRefresh(descrNodes, keys, refreshedKeys)
   151  		}
   152  
   153  		// process retrieved kv-pairs
   154  		for _, retrievedKV := range retrieved {
   155  			if keys != nil && keys.Length() > 0 {
   156  				// do no touch values that aren't meant to be refreshed
   157  				if toRefresh := keys.Has(retrievedKV.Key); !toRefresh {
   158  					continue
   159  				}
   160  			}
   161  			if !s.validRetrievedKV(retrievedKV, descriptor, refreshedKeys) {
   162  				continue
   163  			}
   164  
   165  			// 1st attempt to determine value origin
   166  			if retrievedKV.Origin == kvs.UnknownOrigin {
   167  				// determine value origin based on the values for correlation
   168  				for _, kv := range correlate {
   169  					if kv.Key == retrievedKV.Key {
   170  						retrievedKV.Origin = kv.Origin
   171  						break
   172  					}
   173  				}
   174  			}
   175  
   176  			// 2nd attempt to determine value origin
   177  			if retrievedKV.Origin == kvs.UnknownOrigin {
   178  				// determine value origin based on the last revision
   179  				timeline := graphW.GetNodeTimeline(retrievedKV.Key)
   180  				if len(timeline) > 0 {
   181  					lastRev := timeline[len(timeline)-1]
   182  					valueStateFlag := lastRev.Flags.GetFlag(ValueStateFlagIndex)
   183  					valueState := valueStateFlag.(*ValueStateFlag).valueState
   184  					retrievedKV.Origin = valueStateToOrigin(valueState)
   185  				}
   186  			}
   187  
   188  			if retrievedKV.Origin == kvs.UnknownOrigin {
   189  				// will assume this is from SB
   190  				retrievedKV.Origin = kvs.FromSB
   191  			}
   192  
   193  			// refresh node that represents this kv-pair
   194  			s.refreshValue(graphW, retrievedKV, handler, refreshedKeys, 2)
   195  		}
   196  
   197  		// unset the metadata from base NB values that do not actually exists
   198  		for _, node := range descrNodes {
   199  			if refreshed := refreshedKeys.Has(node.GetKey()); !refreshed {
   200  				if getNodeOrigin(node) == kvs.FromNB && node.GetMetadata() != nil {
   201  					if s.logGraphWalk {
   202  						fmt.Printf("  -> unset metadata for key=%s\n", node.GetKey())
   203  					}
   204  					missingNode := graphW.SetNode(node.GetKey())
   205  					missingNode.SetMetadata(nil)
   206  				}
   207  			}
   208  		}
   209  	}
   210  
   211  	// update state of values that do not actually exist
   212  	for _, node := range graphW.GetNodes(nil) {
   213  		if refreshed := refreshedKeys.Has(node.GetKey()); refreshed {
   214  			continue
   215  		}
   216  		s.refreshUnavailNode(graphW, node, refreshedKeys, 2)
   217  	}
   218  
   219  	if enableGraphDump && verbose && s.config.PrintTxnSummary {
   220  		fmt.Println(dumpGraph(graphW))
   221  	}
   222  }
   223  
   224  // refreshValue refreshes node that represents the given retrieved key-value pair.
   225  func (s *Scheduler) refreshValue(graphW graph.RWAccess, retrievedKV kvs.KVWithMetadata,
   226  	handler *descriptorHandler, refreshed utils.KeySet, indent int) {
   227  	if s.logGraphWalk {
   228  		indentStr := strings.Repeat(" ", indent)
   229  		msg := fmt.Sprintf("refreshValue (key=%s)", retrievedKV.Key)
   230  		fmt.Printf("%s%s %s\n", indentStr, nodeVisitBeginMark, msg)
   231  		defer fmt.Printf("%s%s %s\n", indentStr, nodeVisitEndMark, msg)
   232  	}
   233  
   234  	// refresh node that represents this kv-pair
   235  	node := graphW.SetNode(retrievedKV.Key)
   236  	node.SetLabel(handler.keyLabel(node.GetKey()))
   237  	node.SetValue(retrievedKV.Value)
   238  	if handler.descriptor.WithMetadata {
   239  		node.SetMetadataMap(handler.descriptor.Name)
   240  		node.SetMetadata(retrievedKV.Metadata)
   241  	}
   242  	s.refreshAvailNode(graphW, node, retrievedKV.Origin, false, node.GetKey(), refreshed, indent+2)
   243  
   244  	// determine the set of unavailable derived values
   245  	obsolete := getDerivedKeys(node)
   246  	derives := handler.derivedValues(node.GetKey(), node.GetValue())
   247  	for _, newDerived := range derives {
   248  		obsolete.Del(newDerived.Key)
   249  	}
   250  
   251  	// keep obsolete derived values still in the relation
   252  	for _, key := range obsolete.Iterate() {
   253  		derives = append(derives, kvs.KeyValuePair{Key: key}) // value unused
   254  	}
   255  
   256  	// refresh relations
   257  	dependencies := handler.dependencies(node.GetKey(), node.GetValue())
   258  	node.SetTargets(constructTargets(dependencies, derives))
   259  
   260  	// refresh derived values
   261  	for _, kv := range derives {
   262  		isObsolete := obsolete.Has(kv.Key)
   263  		derNode := graphW.SetNode(kv.Key)
   264  		if !isObsolete {
   265  			derDescr := s.registry.GetDescriptorForKey(kv.Key)
   266  			derHandler := newDescriptorHandler(derDescr)
   267  			derNode.SetValue(kv.Value)
   268  			dependencies := derHandler.dependencies(derNode.GetKey(), derNode.GetValue())
   269  			derNode.SetTargets(constructTargets(dependencies, nil))
   270  			s.refreshAvailNode(graphW, derNode, retrievedKV.Origin, true, node.GetKey(), refreshed, indent+2)
   271  		} else {
   272  			s.refreshUnavailNode(graphW, derNode, refreshed, indent+2)
   273  		}
   274  	}
   275  }
   276  
   277  // refreshAvailNode refreshes state of a node whose value was returned by Retrieve.
   278  func (s *Scheduler) refreshAvailNode(graphW graph.RWAccess, node graph.NodeRW,
   279  	origin kvs.ValueOrigin, derived bool, baseKey string, refreshed utils.KeySet, indent int) {
   280  	if s.logGraphWalk {
   281  		indentStr := strings.Repeat(" ", indent)
   282  		var derivedMark string
   283  		if derived {
   284  			derivedMark = ", is-derived"
   285  		}
   286  		msg := fmt.Sprintf("refreshAvailNode (key=%s%s)", node.GetKey(), derivedMark)
   287  		fmt.Printf("%s%s %s\n", indentStr, nodeVisitBeginMark, msg)
   288  		defer fmt.Printf("%s%s %s\n", indentStr, nodeVisitEndMark, msg)
   289  	}
   290  
   291  	// validate first
   292  	descriptor := s.registry.GetDescriptorForKey(node.GetKey()) // nil for properties
   293  	if derived && !s.validRetrievedDerivedKV(node, descriptor, refreshed) {
   294  		graphW.DeleteNode(node.GetKey())
   295  		return
   296  	}
   297  
   298  	// update availability
   299  	if !isNodeAvailable(node) {
   300  		s.updatedStates.Add(baseKey)
   301  		node.DelFlags(UnavailValueFlagIndex)
   302  	}
   303  	refreshed.Add(node.GetKey())
   304  
   305  	// refresh state
   306  	if getNodeState(node) == kvscheduler.ValueState_NONEXISTENT {
   307  		// newly found node
   308  		if origin == kvs.FromSB {
   309  			s.refreshNodeState(node, kvscheduler.ValueState_OBTAINED, indent)
   310  		} else {
   311  			s.refreshNodeState(node, kvscheduler.ValueState_DISCOVERED, indent)
   312  		}
   313  	}
   314  	if getNodeState(node) == kvscheduler.ValueState_PENDING {
   315  		// no longer pending apparently
   316  		s.refreshNodeState(node, kvscheduler.ValueState_CONFIGURED, indent)
   317  	}
   318  
   319  	// update descriptor flag
   320  	if descriptor != nil {
   321  		node.SetFlags(&DescriptorFlag{descriptor.Name})
   322  	} else {
   323  		node.DelFlags(DescriptorFlagIndex)
   324  	}
   325  
   326  	// updated flags for derived values
   327  	if !derived {
   328  		node.DelFlags(DerivedFlagIndex)
   329  	} else {
   330  		node.SetFlags(&DerivedFlag{baseKey})
   331  	}
   332  }
   333  
   334  // refreshUnavailNode refreshes state of a node whose value is found to be unavailable.
   335  func (s *Scheduler) refreshUnavailNode(graphW graph.RWAccess, node graph.Node, refreshed utils.KeySet, indent int) {
   336  	if s.logGraphWalk {
   337  		indentStr := strings.Repeat(" ", indent)
   338  		msg := fmt.Sprintf("refreshUnavailNode (key=%s, isDerived=%t)", node.GetKey(), isNodeDerived(node))
   339  		fmt.Printf("%s%s %s\n", indentStr, nodeVisitBeginMark, msg)
   340  		defer fmt.Printf("%s%s %s\n", indentStr, nodeVisitEndMark, msg)
   341  	}
   342  
   343  	refreshed.Add(node.GetKey())
   344  	if isNodeAvailable(node) {
   345  		s.updatedStates.Add(getNodeBaseKey(node))
   346  	}
   347  	state := getNodeState(node)
   348  	if getNodeOrigin(node) == kvs.FromSB || state == kvscheduler.ValueState_DISCOVERED {
   349  		// just remove from the graph
   350  		graphW.DeleteNode(node.GetKey())
   351  		return
   352  	}
   353  
   354  	// mark node as unavailable, but do not delete
   355  	nodeW := graphW.SetNode(node.GetKey())
   356  	if isNodeAvailable(node) {
   357  		nodeW.SetFlags(&UnavailValueFlag{})
   358  	}
   359  
   360  	// update state
   361  	if state == kvscheduler.ValueState_UNIMPLEMENTED {
   362  		// it is expected that unimplemented value is not retrieved
   363  		return
   364  	}
   365  	if state == kvscheduler.ValueState_CONFIGURED {
   366  		if getNodeLastUpdate(node).value == nil {
   367  			s.refreshNodeState(nodeW, kvscheduler.ValueState_REMOVED, indent)
   368  		} else {
   369  			s.refreshNodeState(nodeW, kvscheduler.ValueState_MISSING, indent)
   370  		}
   371  	}
   372  }
   373  
   374  func (s *Scheduler) refreshNodeState(node graph.NodeRW, newState kvscheduler.ValueState, indent int) {
   375  	if getNodeState(node) != newState {
   376  		if s.logGraphWalk {
   377  			fmt.Printf("%s  -> change value state from %v to %v\n",
   378  				strings.Repeat(" ", indent), getNodeState(node), newState)
   379  		}
   380  		node.SetFlags(&ValueStateFlag{valueState: newState})
   381  	}
   382  }
   383  
   384  // skipRefresh is used to mark nodes as refreshed without actual refreshing
   385  // if they should not (or cannot) be refreshed.
   386  func (s *Scheduler) skipRefresh(nodes []graph.Node, except utils.KeySet, refreshed utils.KeySet) {
   387  	for _, node := range nodes {
   388  		if except != nil {
   389  			if toRefresh := except.Has(node.GetKey()); toRefresh {
   390  				continue
   391  			}
   392  		}
   393  		refreshed.Add(node.GetKey())
   394  
   395  		// skip refresh for derived nodes
   396  		for _, derivedNode := range getDerivedNodes(node) {
   397  			refreshed.Add(derivedNode.GetKey())
   398  		}
   399  	}
   400  }
   401  
   402  func dumpGraph(g graph.RWAccess) string {
   403  	keys := g.GetKeys()
   404  
   405  	var buf strings.Builder
   406  	graphInfo := fmt.Sprintf("%d nodes", len(keys))
   407  	buf.WriteString("+======================================================================================================================+\n")
   408  	buf.WriteString(fmt.Sprintf("| GRAPH %110s |\n", graphInfo))
   409  	buf.WriteString("+======================================================================================================================+\n")
   410  	writeLine := func(left, right string) {
   411  		n := 115 - len(left)
   412  		buf.WriteString(fmt.Sprintf("| %s %"+fmt.Sprint(n)+"s |\n", left, right))
   413  
   414  	}
   415  	writeLines := func(linesStr string, prefix string) {
   416  		lines := strings.Split(linesStr, "\n")
   417  		for _, line := range lines {
   418  			if line == "" {
   419  				continue
   420  			}
   421  			writeLine(fmt.Sprintf("%s%s", prefix, line), "")
   422  		}
   423  	}
   424  	for i, key := range keys {
   425  		node := g.GetNode(key)
   426  		keyLabel := key
   427  		if label := node.GetLabel(); label != key && label != "" {
   428  			keyLabel = fmt.Sprintf("%s (%s)", key, label)
   429  		}
   430  		descriptor := ""
   431  		if f := node.GetFlag(DescriptorFlagIndex); f != nil {
   432  			descriptor = fmt.Sprintf("[%s] ", f.GetValue())
   433  		}
   434  		lastUpdate := "-"
   435  		if f := node.GetFlag(LastUpdateFlagIndex); f != nil {
   436  			lastUpdate = f.GetValue()
   437  		}
   438  		unavailable := ""
   439  		if f := node.GetFlag(UnavailValueFlagIndex); f != nil {
   440  			unavailable = "<UNAVAILABLE> "
   441  		}
   442  		writeLine(fmt.Sprintf("%s%s", descriptor, keyLabel), fmt.Sprintf("%s %s %s",
   443  			unavailable,
   444  			lastUpdate,
   445  			getNodeState(node).String(),
   446  		))
   447  		writeLines(prototext.Format(node.GetValue()), "  ")
   448  
   449  		if f := node.GetTargets(DependencyRelation); len(f) > 0 {
   450  			writeLine("Depends on:", "")
   451  			for _, dep := range f {
   452  				var nodeDeps []string
   453  				for _, node := range dep.Nodes {
   454  					nodeDeps = append(nodeDeps, node.GetKey())
   455  				}
   456  				if len(nodeDeps) > 1 {
   457  					writeLine(fmt.Sprintf(" - %s", dep.Label), "")
   458  					writeLines(strings.Join(nodeDeps, "\n"), "  -> ")
   459  				} else if len(nodeDeps) == 1 {
   460  					writeLine(fmt.Sprintf(" - %s -> %v", dep.Label, strings.Join(nodeDeps, " ")), "")
   461  				} else {
   462  					writeLine(fmt.Sprintf(" - %s -> <UNAVAILABLE>", dep.Label), "")
   463  				}
   464  			}
   465  		}
   466  		if f := node.GetTargets(DerivesRelation); len(f) > 0 {
   467  			writeLine("Derives:", "")
   468  			var nodeDers []string
   469  			for _, der := range f {
   470  				if len(der.Nodes) == 0 {
   471  					nodeDers = append(nodeDers, der.Label)
   472  				} else {
   473  					for _, node := range der.Nodes {
   474  						desc := ""
   475  						if d := node.GetFlag(DescriptorFlagIndex); d != nil {
   476  							desc = fmt.Sprintf("[%s] ", d.GetValue())
   477  						}
   478  						nodeDers = append(nodeDers, fmt.Sprintf("%s%s", desc, node.GetKey()))
   479  					}
   480  				}
   481  			}
   482  			writeLines(strings.Join(nodeDers, "\n"), " - ")
   483  		}
   484  		if f := node.GetSources(DependencyRelation); len(f) > 0 {
   485  			writeLine("Dependency for:", "")
   486  			var nodeDeps []string
   487  			for _, perLabel := range f {
   488  				for _, node := range perLabel.Nodes {
   489  					desc := ""
   490  					if d := node.GetFlag(DescriptorFlagIndex); d != nil {
   491  						desc = fmt.Sprintf("[%s] ", d.GetValue())
   492  					}
   493  					nodeDeps = append(nodeDeps, fmt.Sprintf("%s%s", desc, node.GetKey()))
   494  				}
   495  			}
   496  			writeLines(strings.Join(nodeDeps, "\n"), " - ")
   497  		}
   498  		if f := node.GetSources(DerivesRelation); len(f) > 0 {
   499  			var nodeDers []string
   500  			for _, perLabel := range f {
   501  				for _, der := range perLabel.Nodes {
   502  					nodeDers = append(nodeDers, der.GetKey())
   503  				}
   504  			}
   505  			writeLine(fmt.Sprintf("Derived from: %s", strings.Join(nodeDers, " ")), "")
   506  		}
   507  		if f := node.GetMetadata(); f != nil {
   508  			writeLine(fmt.Sprintf("Metadata: %+v", f), "")
   509  		}
   510  		if f := node.GetFlag(ErrorFlagIndex); f != nil {
   511  			writeLine(fmt.Sprintf("Errors: %+v", f.GetValue()), "")
   512  		}
   513  
   514  		if i+1 != len(keys) {
   515  			buf.WriteString("+----------------------------------------------------------------------------------------------------------------------+\n")
   516  		}
   517  	}
   518  	buf.WriteString("+======================================================================================================================+\n")
   519  
   520  	return buf.String()
   521  }
   522  
   523  // validRetrievedKV verifies validity of a retrieved KV-pair.
   524  func (s *Scheduler) validRetrievedKV(kv kvs.KVWithMetadata, descriptor *kvs.KVDescriptor, refreshed utils.KeySet) bool {
   525  	if kv.Key == "" {
   526  		s.Log.WithFields(logging.Fields{
   527  			"descriptor": descriptor.Name,
   528  		}).Warn("Descriptor retrieved value with empty key")
   529  		return false
   530  	}
   531  	if alreadyRetrieved := refreshed.Has(kv.Key); alreadyRetrieved {
   532  		s.Log.WithFields(logging.Fields{
   533  			"descriptor": descriptor.Name,
   534  			"key":        kv.Key,
   535  		}).Warn("The same value was retrieved more than once")
   536  		return false
   537  	}
   538  	if kv.Value == nil {
   539  		s.Log.WithFields(logging.Fields{
   540  			"descriptor": descriptor.Name,
   541  			"key":        kv.Key,
   542  		}).Warn("Descriptor retrieved nil value")
   543  		return false
   544  	}
   545  	if !descriptor.KeySelector(kv.Key) {
   546  		s.Log.WithFields(logging.Fields{
   547  			"descriptor": descriptor.Name,
   548  			"key":        kv.Key,
   549  			"value":      kv.Value,
   550  		}).Warn("Descriptor retrieved value outside of its key space")
   551  		return false
   552  	}
   553  	return true
   554  }
   555  
   556  // validRetrievedDerivedKV verifies validity of a KV-pair derived from a retrieved value.
   557  func (s *Scheduler) validRetrievedDerivedKV(node graph.Node, descriptor *kvs.KVDescriptor, refreshed utils.KeySet) bool {
   558  	descriptorName := "<NONE>"
   559  	if descriptor != nil {
   560  		descriptorName = descriptor.Name
   561  	}
   562  	if node.GetValue() == nil {
   563  		s.Log.WithFields(logging.Fields{
   564  			"descriptor": descriptorName,
   565  			"key":        node.GetKey(),
   566  		}).Warn("Derived nil value")
   567  		return false
   568  	}
   569  	if alreadyRetrieved := refreshed.Has(node.GetKey()); alreadyRetrieved {
   570  		s.Log.WithFields(logging.Fields{
   571  			"descriptor": descriptorName,
   572  			"key":        node.GetKey(),
   573  		}).Warn("The same value was retrieved more than once")
   574  		// return true -> let's overwrite invalidly retrieved derived value
   575  	}
   576  	return true
   577  }