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 }