go.ligato.io/vpp-agent/v3@v3.5.0/plugins/kvscheduler/internal/graph/graph_write.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  	"time"
    19  
    20  	"go.ligato.io/cn-infra/v2/idxmap"
    21  
    22  	"go.ligato.io/vpp-agent/v3/plugins/kvscheduler/internal/utils"
    23  )
    24  
    25  // graphRW implements RWAccess.
    26  type graphRW struct {
    27  	*graphR
    28  
    29  	record   bool
    30  	wInPlace bool
    31  
    32  	newRevs map[string]bool // key -> data-updated? (for Release)
    33  }
    34  
    35  // newGraphRW creates a new instance of grapRW, which extends an existing
    36  // graph with write-operations.
    37  func newGraphRW(graph *graphR, wInPlace, recordChanges bool) *graphRW {
    38  	var gR *graphR
    39  	if wInPlace {
    40  		gR = graph
    41  	} else {
    42  		gR = graph.copyNodesOnly()
    43  	}
    44  	gR.unsaved = utils.NewSliceBasedKeySet()
    45  	return &graphRW{
    46  		graphR:   gR,
    47  		wInPlace: wInPlace,
    48  		record:   recordChanges,
    49  		newRevs:  make(map[string]bool),
    50  	}
    51  }
    52  
    53  // RegisterMetadataMap registers new metadata map for value-label->metadata
    54  // associations of selected node.
    55  func (graph *graphRW) RegisterMetadataMap(mapName string, mapping idxmap.NamedMappingRW) {
    56  	if graph.mappings == nil {
    57  		graph.mappings = make(map[string]idxmap.NamedMappingRW)
    58  	}
    59  	graph.mappings[mapName] = mapping
    60  }
    61  
    62  // SetNode creates new node or returns read-write handle to an existing node.
    63  // The changes are propagated to the graph only after Save() is called.
    64  // If <newRev> is true, the changes will recorded as a new revision of the
    65  // node for the history.
    66  func (graph *graphRW) SetNode(key string) NodeRW {
    67  	if graph.parent.methodTracker != nil {
    68  		defer graph.parent.methodTracker("SetNode")()
    69  	}
    70  
    71  	graph.unsaved.Add(key)
    72  	node, has := graph.nodes[key]
    73  	if has {
    74  		return node
    75  	}
    76  
    77  	// new node
    78  	node = newNode(nil)
    79  	node.graph = graph.graphR
    80  	node.key = key
    81  	graph.nodes[key] = node
    82  
    83  	// add edges going into this new node
    84  	graph.edgeLookup.addNodeKey(key)
    85  	graph.edgeLookup.iterSources(key, func(sourceNodeKey, relation, label string) {
    86  		sourceNode := graph.nodes[sourceNodeKey]
    87  		target, targetIdx := sourceNode.targets.GetTargetForLabel(relation, label)
    88  		keySelector := sourceNode.targetsDef[targetIdx].Selector.KeySelector
    89  		if keySelector != nil {
    90  			if !keySelector(key) {
    91  				return
    92  			}
    93  		}
    94  		sourceNode.addToTargets(node, target)
    95  	})
    96  
    97  	return node
    98  }
    99  
   100  // DeleteNode deletes node with the given key.
   101  // Returns true if the node really existed before the operation.
   102  func (graph *graphRW) DeleteNode(key string) bool {
   103  	if graph.parent.methodTracker != nil {
   104  		defer graph.parent.methodTracker("DeleteNode")()
   105  	}
   106  
   107  	node, has := graph.nodes[key]
   108  	if !has {
   109  		return false
   110  	}
   111  
   112  	// remove outgoing edges (not the most effective approach...)
   113  	node.SetTargets(nil)
   114  
   115  	// remove incoming edges
   116  	graph.edgeLookup.iterSources(key, func(sourceNodeKey, relation, label string) {
   117  		sourceNode := graph.nodes[sourceNodeKey]
   118  		sourceNode.removeFromTarget(key, relation, label)
   119  	})
   120  	graph.edgeLookup.delNodeKey(key)
   121  
   122  	// delete from graph
   123  	delete(graph.nodes, key)
   124  
   125  	// unset metadata
   126  	if graph.wInPlace && node.metadataAdded {
   127  		if mapping, hasMapping := graph.mappings[node.metadataMap]; hasMapping {
   128  			mapping.Delete(node.label)
   129  		}
   130  	}
   131  
   132  	graph.unsaved.Add(key)
   133  	return true
   134  }
   135  
   136  // Save propagates all changes to the graph.
   137  func (graph *graphRW) Save() {
   138  	if graph.parent.methodTracker != nil {
   139  		defer graph.parent.methodTracker("Save")()
   140  	}
   141  
   142  	if !graph.wInPlace {
   143  		graph.parent.rwLock.Lock()
   144  		defer graph.parent.rwLock.Unlock()
   145  	}
   146  
   147  	destGraph := graph.parent.graph
   148  
   149  	if !graph.wInPlace {
   150  		// propagate newly registered mappings
   151  		for mapName, mapping := range graph.mappings {
   152  			if _, alreadyReg := destGraph.mappings[mapName]; !alreadyReg {
   153  				destGraph.mappings[mapName] = mapping
   154  			}
   155  		}
   156  
   157  		// save updated edgeLookup
   158  		graph.edgeLookup.saveOverlay()
   159  	}
   160  
   161  	for _, key := range graph.unsaved.Iterate() {
   162  		node, found := graph.nodes[key]
   163  		deleted := !found
   164  		if !deleted && !node.dataUpdated && !node.targetsUpdated && !node.sourcesUpdated {
   165  			continue
   166  		}
   167  
   168  		if deleted {
   169  			// node was deleted:
   170  
   171  			if !graph.wInPlace {
   172  				if node, has := destGraph.nodes[key]; has {
   173  					// remove metadata
   174  					if node.metadataAdded {
   175  						if mapping, hasMapping := destGraph.mappings[node.metadataMap]; hasMapping {
   176  							mapping.Delete(node.label)
   177  						}
   178  					}
   179  					// remove node from graph
   180  					delete(destGraph.nodes, key)
   181  				}
   182  			}
   183  			graph.newRevs[key] = true
   184  			continue
   185  		}
   186  
   187  		// node was created or updated:
   188  
   189  		// mark node for recording during RW-handle release
   190  		// (ignore if only sources have been updated)
   191  		if node.dataUpdated || node.targetsUpdated {
   192  			if _, newRev := graph.newRevs[key]; !newRev {
   193  				graph.newRevs[key] = false
   194  			}
   195  			graph.newRevs[key] = graph.newRevs[key] || node.dataUpdated
   196  		}
   197  
   198  		if !graph.wInPlace {
   199  			// copy changed node to the actual graph
   200  			nodeCopyR := node.copy()
   201  			nodeCopyR.graph = destGraph
   202  			nodeCopy := newNode(nodeCopyR)
   203  			destGraph.nodes[key] = nodeCopy
   204  
   205  			// sync metadata
   206  			nodeCopy.metaInSync = node.metaInSync
   207  			nodeCopy.syncMetadata()
   208  			node.metadataAdded = nodeCopy.metadataAdded
   209  
   210  			// use copy-on-write targets+sources for the write-handle
   211  			cowTargets := nodeCopy.targets
   212  			nodeCopy.targets = node.targets
   213  			node.targets = cowTargets
   214  			cowSources := nodeCopy.sources
   215  			nodeCopy.sources = node.sources
   216  			node.sources = cowSources
   217  		}
   218  
   219  		// working copy is now in-sync
   220  		node.dataUpdated = false
   221  		node.targetsUpdated = false
   222  		node.sourcesUpdated = false
   223  		node.metaInSync = true
   224  	}
   225  
   226  	graph.unsaved = utils.NewSliceBasedKeySet()
   227  }
   228  
   229  // Release records changes if requested.
   230  func (graph *graphRW) Release() {
   231  	if graph.parent.methodTracker != nil {
   232  		defer graph.parent.methodTracker("Release")()
   233  	}
   234  
   235  	if graph.wInPlace {
   236  		// update unsaved & newRevs
   237  		graph.Save()
   238  		defer graph.parent.rwLock.Unlock()
   239  	}
   240  
   241  	if graph.record && graph.parent.recordOldRevs {
   242  		if !graph.wInPlace {
   243  			graph.parent.rwLock.Lock()
   244  			defer graph.parent.rwLock.Unlock()
   245  		}
   246  
   247  		destGraph := graph.parent.graph
   248  		for key, dataUpdated := range graph.newRevs {
   249  			node, exists := destGraph.nodes[key]
   250  			if _, hasTimeline := destGraph.timeline[key]; !hasTimeline {
   251  				if !exists {
   252  					// deleted, but never recorded => skip
   253  					continue
   254  				}
   255  				destGraph.timeline[key] = []*RecordedNode{}
   256  			}
   257  			records := destGraph.timeline[key]
   258  			if len(records) > 0 {
   259  				lastRecord := records[len(records)-1]
   260  				if lastRecord.Until.IsZero() {
   261  					lastRecord.Until = time.Now()
   262  				}
   263  			}
   264  			if exists {
   265  				destGraph.timeline[key] = append(records,
   266  					destGraph.recordNode(node, !dataUpdated))
   267  			}
   268  		}
   269  
   270  		// remove past revisions from the log which are too old to keep
   271  		now := time.Now()
   272  		sinceLastTrimming := now.Sub(graph.parent.lastRevTrimming)
   273  		if sinceLastTrimming >= oldRevsTrimmingPeriod {
   274  			for key, records := range destGraph.timeline {
   275  				var i, j int // i = first after init period, j = first after init period to keep
   276  				for i = 0; i < len(records); i++ {
   277  					sinceStart := records[i].Since.Sub(graph.parent.startTime)
   278  					if sinceStart > graph.parent.permanentInitPeriod {
   279  						break
   280  					}
   281  				}
   282  				for j = i; j < len(records); j++ {
   283  					if records[j].Until.IsZero() {
   284  						break
   285  					}
   286  					elapsed := now.Sub(records[j].Until)
   287  					if elapsed <= graph.parent.recordAgeLimit {
   288  						break
   289  					}
   290  				}
   291  				if j > i {
   292  					copy(records[i:], records[j:])
   293  					newLen := len(records) - (j - i)
   294  					for k := newLen; k < len(records); k++ {
   295  						records[k] = nil
   296  					}
   297  					destGraph.timeline[key] = records[:newLen]
   298  				}
   299  				if len(destGraph.timeline[key]) == 0 {
   300  					delete(destGraph.timeline, key)
   301  				}
   302  			}
   303  			graph.parent.lastRevTrimming = now
   304  		}
   305  	}
   306  }