blob: 17d9ecebeccca0abdf5f4de15e061a15473eae8a [file] [log] [blame]
/*
* Copyright 2018-present Open Networking Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package model
import (
"sync"
"github.com/opencord/voltha-lib-go/v3/pkg/log"
)
// TODO: implement weak references or something equivalent
// TODO: missing proper logging
// Branch structure is used to classify a collection of transaction based revisions
type Branch struct {
mutex sync.RWMutex
Node *node
Txid string
Origin Revision
Revisions map[string]Revision
LatestLock sync.RWMutex
Latest Revision
}
// NewBranch creates a new instance of the Branch structure
func NewBranch(node *node, txid string, origin Revision, autoPrune bool) *Branch {
b := &Branch{}
b.Node = node
b.Txid = txid
b.Origin = origin
b.Revisions = make(map[string]Revision)
b.Latest = origin
return b
}
// Utility function to extract all children names for a given revision (mostly for debugging purposes)
func (b *Branch) retrieveChildrenNames(revision Revision) []string {
var childrenNames []string
for _, child := range revision.GetChildren("devices") {
childrenNames = append(childrenNames, child.GetName())
}
return childrenNames
}
// Utility function to compare children names and report the missing ones (mostly for debugging purposes)
func (b *Branch) findMissingChildrenNames(previousNames, latestNames []string) []string {
var missingNames []string
for _, previousName := range previousNames {
found := false
if len(latestNames) == 0 {
break
}
for _, latestName := range latestNames {
if previousName == latestName {
found = true
break
}
}
if !found {
missingNames = append(missingNames, previousName)
}
}
return missingNames
}
// SetLatest assigns the latest revision for this branch
func (b *Branch) SetLatest(latest Revision) {
b.mutex.Lock()
defer b.mutex.Unlock()
if b.Latest != nil {
logger.Debugw("updating-latest-revision", log.Fields{"current": b.Latest.GetHash(), "new": latest.GetHash()})
// Go through list of children names in current revision and new revision
// and then compare the resulting outputs to ensure that we have not lost any entries.
if level, _ := log.GetPackageLogLevel(); level == log.DebugLevel {
var previousNames, latestNames, missingNames []string
if previousNames = b.retrieveChildrenNames(b.Latest); len(previousNames) > 0 {
logger.Debugw("children-of-previous-revision", log.Fields{"hash": b.Latest.GetHash(), "names": previousNames})
}
if latestNames = b.retrieveChildrenNames(b.Latest); len(latestNames) > 0 {
logger.Debugw("children-of-latest-revision", log.Fields{"hash": latest.GetHash(), "names": latestNames})
}
if missingNames = b.findMissingChildrenNames(previousNames, latestNames); len(missingNames) > 0 {
logger.Debugw("children-missing-in-latest-revision", log.Fields{"hash": latest.GetHash(), "names": missingNames})
}
}
} else {
logger.Debugw("setting-latest-revision", log.Fields{"new": latest.GetHash()})
}
b.Latest = latest
}
// GetLatest retrieves the latest revision of the branch
func (b *Branch) GetLatest() Revision {
b.mutex.RLock()
defer b.mutex.RUnlock()
return b.Latest
}
// GetOrigin retrieves the original revision of the branch
func (b *Branch) GetOrigin() Revision {
b.mutex.RLock()
defer b.mutex.RUnlock()
return b.Origin
}
// AddRevision inserts a new revision to the branch
func (b *Branch) AddRevision(revision Revision) {
if revision != nil && b.GetRevision(revision.GetHash()) == nil {
b.SetRevision(revision.GetHash(), revision)
}
}
// GetRevision pulls a revision entry at the specified hash
func (b *Branch) GetRevision(hash string) Revision {
b.mutex.RLock()
defer b.mutex.RUnlock()
if revision, ok := b.Revisions[hash]; ok {
return revision
}
return nil
}
// SetRevision updates a revision entry at the specified hash
func (b *Branch) SetRevision(hash string, revision Revision) {
b.mutex.Lock()
defer b.mutex.Unlock()
b.Revisions[hash] = revision
}
// DeleteRevision removes a revision with the specified hash
func (b *Branch) DeleteRevision(hash string) {
b.mutex.Lock()
defer b.mutex.Unlock()
delete(b.Revisions, hash)
}