VOL-1334 : Fixed concurrency issues
- Semaphores were added at the different layers of the model
- Made the proxy interfaces more robust
- Eliminated problems while retrieving latest data in concurrent mode
Change-Id: I7854105d7effa10e5cb704f5d9917569ab184f84
diff --git a/db/model/node.go b/db/model/node.go
index a3e6ea7..7ea41ce 100644
--- a/db/model/node.go
+++ b/db/model/node.go
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package model
// TODO: proper error handling
@@ -23,13 +24,17 @@
"github.com/golang/protobuf/proto"
"github.com/opencord/voltha-go/common/log"
"reflect"
+ "runtime/debug"
"strings"
+ "sync"
)
+// When a branch has no transaction id, everything gets stored in NONE
const (
NONE string = "none"
)
+// Node interface is an abstraction of the node data structure
type Node interface {
MakeLatest(branch *Branch, revision Revision, changeAnnouncement []ChangeTuple)
@@ -47,14 +52,12 @@
DeleteTxBranch(txid string)
FoldTxBranch(txid string)
- GetProxy(path string, exclusive bool) *Proxy
-
- ExecuteCallbacks()
- AddCallback(callback CallbackFunction, args ...interface{})
- AddNotificationCallback(callback CallbackFunction, args ...interface{})
+ CreateProxy(path string, exclusive bool) *Proxy
+ GetProxy() *Proxy
}
type node struct {
+ sync.RWMutex
Root *root
Type interface{}
Branches map[string]*Branch
@@ -64,12 +67,14 @@
AutoPrune bool
}
+// ChangeTuple holds details of modifications made to a revision
type ChangeTuple struct {
Type CallbackType
PreviousData interface{}
LatestData interface{}
}
+// NewNode creates a new instance of the node data structure
func NewNode(root *root, initialData interface{}, autoPrune bool, txid string) *node {
n := &node{}
@@ -85,6 +90,9 @@
dataCopy := proto.Clone(initialData.(proto.Message))
n.initialize(dataCopy, txid)
} else if reflect.ValueOf(initialData).IsValid() {
+ // FIXME: this block does not reflect the original implementation
+ // it should be checking if the provided initial_data is already a type!??!
+ // it should be checked before IsProtoMessage
n.Type = reflect.ValueOf(initialData).Interface()
} else {
// not implemented error
@@ -94,39 +102,33 @@
return n
}
+// MakeNode creates a new node in the tree
func (n *node) MakeNode(data interface{}, txid string) *node {
return NewNode(n.Root, data, true, txid)
}
+// MakeRevision create a new revision of the node in the tree
func (n *node) MakeRevision(branch *Branch, data interface{}, children map[string][]Revision) Revision {
- if n.Root.RevisionClass.(reflect.Type) == reflect.TypeOf(PersistedRevision{}) {
- return NewPersistedRevision(branch, data, children)
- }
-
- return NewNonPersistedRevision(branch, data, children)
+ return n.GetRoot().MakeRevision(branch, data, children)
}
-func (n *node) MakeLatest(branch *Branch, revision Revision, changeAnnouncement []ChangeTuple) {
- n.makeLatest(branch, revision, changeAnnouncement)
-}
+// makeLatest will mark the revision of a node as being the latest
func (n *node) makeLatest(branch *Branch, revision Revision, changeAnnouncement []ChangeTuple) {
- if _, ok := branch.Revisions[revision.GetHash()]; !ok {
- branch.Revisions[revision.GetHash()] = revision
- }
+ branch.AddRevision(revision)
- if branch.Latest == nil || revision.GetHash() != branch.Latest.GetHash() {
- branch.Latest = revision
+ if branch.GetLatest() == nil || revision.GetHash() != branch.GetLatest().GetHash() {
+ branch.SetLatest(revision)
}
if changeAnnouncement != nil && branch.Txid == "" {
- if n.Proxy != nil {
+ if n.GetProxy() != nil {
for _, change := range changeAnnouncement {
log.Debugf("invoking callback - changeType: %+v, previous:%+v, latest: %+v",
change.Type,
change.PreviousData,
change.LatestData)
- n.Root.AddCallback(
- n.Proxy.InvokeCallbacks,
+ n.GetRoot().AddCallback(
+ n.GetProxy().InvokeCallbacks,
change.Type,
true,
change.PreviousData,
@@ -139,7 +141,7 @@
change.Type,
change.PreviousData,
change.LatestData)
- n.Root.AddNotificationCallback(
+ n.GetRoot().AddNotificationCallback(
n.makeEventBus().Advertise,
change.Type,
revision.GetHash(),
@@ -149,27 +151,24 @@
}
}
+// Latest returns the latest revision of node with or without the transaction id
func (n *node) Latest(txid ...string) Revision {
var branch *Branch
- var exists bool
if len(txid) > 0 && txid[0] != "" {
- if branch, exists = n.Branches[txid[0]]; exists {
- return branch.Latest
+ if branch = n.GetBranch(txid[0]); branch != nil {
+ return branch.GetLatest()
}
- } else if branch, exists = n.Branches[NONE]; exists {
- return branch.Latest
+ } else if branch = n.GetBranch(NONE); branch != nil {
+ return branch.GetLatest()
}
return nil
}
-func (n *node) GetHash(hash string) Revision {
- return n.Branches[NONE].Revisions[hash]
-}
-
+// initialize prepares the content of a node along with its possible ramifications
func (n *node) initialize(data interface{}, txid string) {
- var children map[string][]Revision
- children = make(map[string][]Revision)
+ n.Lock()
+ children := make(map[string][]Revision)
for fieldName, field := range ChildrenFields(n.Type) {
_, fieldValue := GetAttributeValue(data, fieldName, 0)
@@ -181,7 +180,9 @@
for i := 0; i < fieldValue.Len(); i++ {
v := fieldValue.Index(i)
- rev := n.MakeNode(v.Interface(), txid).Latest(txid)
+ if rev := n.MakeNode(v.Interface(), txid).Latest(txid); rev != nil {
+ children[fieldName] = append(children[fieldName], rev)
+ }
_, key := GetAttributeValue(v.Interface(), field.Key, 0)
for _, k := range keysSeen {
@@ -189,59 +190,60 @@
log.Errorf("duplicate key - %s", k)
}
}
- children[fieldName] = append(children[fieldName], rev)
keysSeen = append(keysSeen, key.String())
}
} else {
for i := 0; i < fieldValue.Len(); i++ {
v := fieldValue.Index(i)
- children[fieldName] = append(children[fieldName], n.MakeNode(v.Interface(), txid).Latest())
+ if newNodeRev := n.MakeNode(v.Interface(), txid).Latest(); newNodeRev != nil {
+ children[fieldName] = append(children[fieldName], newNodeRev)
+ }
}
}
} else {
- children[fieldName] = append(children[fieldName], n.MakeNode(fieldValue.Interface(), txid).Latest())
+ if newNodeRev := n.MakeNode(fieldValue.Interface(), txid).Latest(); newNodeRev != nil {
+ children[fieldName] = append(children[fieldName], newNodeRev)
+ }
}
} else {
log.Errorf("field is invalid - %+v", fieldValue)
}
}
- // FIXME: ClearField??? No such method in go protos. Reset?
- //data.ClearField(field_name)
+ n.Unlock()
+
branch := NewBranch(n, "", nil, n.AutoPrune)
rev := n.MakeRevision(branch, data, children)
n.makeLatest(branch, rev, nil)
if txid == "" {
- n.Branches[NONE] = branch
+ n.SetBranch(NONE, branch)
} else {
- n.Branches[txid] = branch
+ n.SetBranch(txid, branch)
}
}
+// findRevByKey retrieves a specific revision from a node tree
func (n *node) findRevByKey(revs []Revision, keyName string, value interface{}) (int, Revision) {
+ n.Lock()
+ defer n.Unlock()
for i, rev := range revs {
dataValue := reflect.ValueOf(rev.GetData())
dataStruct := GetAttributeStructure(rev.GetData(), keyName, 0)
fieldValue := dataValue.Elem().FieldByName(dataStruct.Name)
- //log.Debugf("fieldValue: %+v, type: %+v, value: %+v", fieldValue.Interface(), fieldValue.Type(), value)
a := fmt.Sprintf("%s", fieldValue.Interface())
b := fmt.Sprintf("%s", value)
if a == b {
- return i, rev
+ return i, revs[i]
}
}
- log.Errorf("key %s=%s not found", keyName, value)
-
return -1, nil
}
-//
-// Get operation
-//
+// Get retrieves the data from a node tree that resides at the specified path
func (n *node) Get(path string, hash string, depth int, deep bool, txid string) interface{} {
if deep {
depth = -1
@@ -254,20 +256,20 @@
var branch *Branch
var rev Revision
- // FIXME: should empty txid be cleaned up?
- if branch = n.Branches[txid]; txid == "" || branch == nil {
- branch = n.Branches[NONE]
+ if branch = n.GetBranch(txid); txid == "" || branch == nil {
+ branch = n.GetBranch(NONE)
}
if hash != "" {
- rev = branch.Revisions[hash]
+ rev = branch.GetRevision(hash)
} else {
- rev = branch.Latest
+ rev = branch.GetLatest()
}
return n.getPath(rev, path, depth)
}
+// getPath traverses the specified path and retrieves the data associated to it
func (n *node) getPath(rev Revision, path string, depth int) interface{} {
if path == "" {
return n.getData(rev, depth)
@@ -286,8 +288,10 @@
field := names[name]
if field.IsContainer {
+ children := make([]Revision, len(rev.GetChildren()[name]))
+ copy(children, rev.GetChildren()[name])
+
if field.Key != "" {
- children := rev.GetChildren()[name]
if path != "" {
partition = strings.SplitN(path, "/", 2)
key := partition[0]
@@ -314,60 +318,57 @@
// TODO: raise error
return response
}
- for _, childRev := range rev.GetChildren()[name] {
+ for _, childRev := range children {
childNode := childRev.GetNode()
value := childNode.getData(childRev, depth)
response = append(response, value)
}
return response
}
- } else {
- childRev := rev.GetChildren()[name][0]
- childNode := childRev.GetNode()
- return childNode.getPath(childRev, path, depth)
}
- return nil
+
+ childRev := rev.GetChildren()[name][0]
+ childNode := childRev.GetNode()
+ return childNode.getPath(childRev, path, depth)
}
+// getData retrieves the data from a node revision
func (n *node) getData(rev Revision, depth int) interface{} {
- msg := rev.Get(depth)
+ msg := rev.GetBranch().GetLatest().Get(depth)
var modifiedMsg interface{}
- if n.Proxy != nil {
- log.Debug("invoking proxy GET Callbacks")
- if modifiedMsg = n.Proxy.InvokeCallbacks(GET, false, msg); modifiedMsg != nil {
+ if n.GetProxy() != nil {
+ log.Debug("invoking proxy GET Callbacks : %+v", msg)
+ if modifiedMsg = n.GetProxy().InvokeCallbacks(GET, false, msg); modifiedMsg != nil {
msg = modifiedMsg
}
}
+
return msg
}
-//
-// Update operation
-//
+// Update changes the content of a node at the specified path with the provided data
func (n *node) Update(path string, data interface{}, strict bool, txid string, makeBranch MakeBranchFunction) Revision {
- // FIXME: is this required ... a bit overkill to take out a "/"
for strings.HasPrefix(path, "/") {
path = path[1:]
}
var branch *Branch
- var ok bool
if txid == "" {
- branch = n.Branches[NONE]
- } else if branch, ok = n.Branches[txid]; !ok {
+ branch = n.GetBranch(NONE)
+ } else if branch = n.GetBranch(txid); &branch == nil {
branch = makeBranch(n)
}
- log.Debugf("Branch data : %+v, Passed data: %+v", branch.Latest.GetData(), data)
-
+ if branch.GetLatest() != nil {
+ log.Debugf("Branch data : %+v, Passed data: %+v", branch.GetLatest().GetData(), data)
+ }
if path == "" {
return n.doUpdate(branch, data, strict)
}
- // TODO missing some code here...
- rev := branch.Latest
+ rev := branch.GetLatest()
partition := strings.SplitN(path, "/", 2)
name := partition[0]
@@ -393,14 +394,12 @@
path = partition[1]
}
keyValue := field.KeyFromStr(key)
- // TODO. Est-ce que le copy ne fonctionne pas? dois-je plutôt faire un clone de chaque item?
- for _, v := range rev.GetChildren()[name] {
- revCopy := reflect.ValueOf(v).Interface().(Revision)
- children = append(children, revCopy)
- }
+
+ children = make([]Revision, len(rev.GetChildren()[name]))
+ copy(children, rev.GetChildren()[name])
+
idx, childRev := n.findRevByKey(children, field.Key, keyValue)
childNode := childRev.GetNode()
- childNode.Proxy = n.Proxy
newChildRev := childNode.Update(path, data, strict, txid, makeBranch)
@@ -409,39 +408,45 @@
log.Debug("clear-hash - %s %+v", newChildRev.GetHash(), newChildRev)
newChildRev.ClearHash()
}
- return branch.Latest
+ return branch.GetLatest()
}
_, newKey := GetAttributeValue(newChildRev.GetData(), field.Key, 0)
- log.Debugf("newKey is %s", newKey.Interface())
+
_newKeyType := fmt.Sprintf("%s", newKey)
_keyValueType := fmt.Sprintf("%s", keyValue)
+
if _newKeyType != _keyValueType {
log.Errorf("cannot change key field")
}
+
children[idx] = newChildRev
- rev = rev.UpdateChildren(name, children, branch)
- branch.Latest.Drop(txid, false)
- n.Root.MakeLatest(branch, rev, nil)
+
+ updatedRev := rev.UpdateChildren(name, children, branch)
+ branch.GetLatest().Drop(txid, false)
+ n.makeLatest(branch, updatedRev, nil)
+
return newChildRev
+
} else {
log.Errorf("cannot index into container with no keys")
}
} else {
childRev := rev.GetChildren()[name][0]
childNode := childRev.GetNode()
- childNode.Proxy = n.Proxy
newChildRev := childNode.Update(path, data, strict, txid, makeBranch)
- rev = rev.UpdateChildren(name, []Revision{newChildRev}, branch)
- branch.Latest.Drop(txid, false)
- n.Root.MakeLatest(branch, rev, nil)
+ updatedRev := rev.UpdateChildren(name, []Revision{newChildRev}, branch)
+ rev.Drop(txid, false)
+ n.makeLatest(branch, updatedRev, nil)
return newChildRev
}
return nil
}
func (n *node) doUpdate(branch *Branch, data interface{}, strict bool) Revision {
- log.Debugf("Comparing types - expected: %+v, actual: %+v", reflect.ValueOf(n.Type).Type(), reflect.TypeOf(data))
+ log.Debugf("Comparing types - expected: %+v, actual: %+v &&&&&& %s", reflect.ValueOf(n.Type).Type(),
+ reflect.TypeOf(data),
+ string(debug.Stack()))
if reflect.TypeOf(data) != reflect.ValueOf(n.Type).Type() {
// TODO raise error
@@ -454,33 +459,33 @@
// return nil
//}
- if n.Proxy != nil {
+ if n.GetProxy() != nil {
log.Debug("invoking proxy PRE_UPDATE Callbacks")
- n.Proxy.InvokeCallbacks(PRE_UPDATE, false, branch.Latest.GetData(), data)
+ n.GetProxy().InvokeCallbacks(PRE_UPDATE, false, branch.GetLatest(), data)
}
- if !reflect.DeepEqual(branch.Latest.GetData(), data) {
+
+ if branch.GetLatest().GetData().(proto.Message).String() != data.(proto.Message).String() {
if strict {
// TODO: checkAccessViolations(data, Branch.GetLatest.data)
log.Debugf("checking access violations")
}
- rev := branch.Latest.UpdateData(data, branch)
- changes := []ChangeTuple{{POST_UPDATE, branch.Latest.GetData(), rev.GetData()}}
+
+ rev := branch.GetLatest().UpdateData(data, branch)
+ changes := []ChangeTuple{{POST_UPDATE, branch.GetLatest().GetData(), rev.GetData()}}
// FIXME VOL-1293: the following statement corrupts the kv when using a subproxy (commenting for now)
// FIXME VOL-1293 cont'd: need to figure out the required conditions otherwise we are not cleaning up entries
- //branch.Latest.Drop(branch.Txid, true)
+ //branch.GetLatest().Drop(branch.Txid, true)
- n.Root.Proxy = n.Proxy
- n.Root.MakeLatest(branch, rev, changes)
+ n.makeLatest(branch, rev, changes)
+
return rev
- } else {
- return branch.Latest
}
+
+ return branch.GetLatest()
}
-//
-// Add operation
-//
+// Add inserts a new node at the specified path with the provided data
func (n *node) Add(path string, data interface{}, txid string, makeBranch MakeBranchFunction) Revision {
for strings.HasPrefix(path, "/") {
path = path[1:]
@@ -491,14 +496,13 @@
}
var branch *Branch
- var ok bool
if txid == "" {
- branch = n.Branches[NONE]
- } else if branch, ok = n.Branches[txid]; !ok {
+ branch = n.GetBranch(NONE)
+ } else if branch = n.GetBranch(txid); branch == nil {
branch = makeBranch(n)
}
- rev := branch.Latest
+ rev := branch.GetLatest()
partition := strings.SplitN(path, "/", 2)
name := partition[0]
@@ -510,37 +514,39 @@
}
field := ChildrenFields(n.Type)[name]
+
var children []Revision
if field.IsContainer {
if path == "" {
if field.Key != "" {
- if n.Proxy != nil {
+ if n.GetProxy() != nil {
log.Debug("invoking proxy PRE_ADD Callbacks")
- n.Proxy.InvokeCallbacks(PRE_ADD, false, data)
+ n.GetProxy().InvokeCallbacks(PRE_ADD, false, data)
}
- for _, v := range rev.GetChildren()[name] {
- revCopy := reflect.ValueOf(v).Interface().(Revision)
- children = append(children, revCopy)
- }
+ children = make([]Revision, len(rev.GetChildren()[name]))
+ copy(children, rev.GetChildren()[name])
+
_, key := GetAttributeValue(data, field.Key, 0)
+
if _, exists := n.findRevByKey(children, field.Key, key.String()); exists != nil {
// TODO raise error
log.Errorf("duplicate key found: %s", key.String())
- } else {
- childRev := n.MakeNode(data, txid).Latest(txid)
- children = append(children, childRev)
- rev := rev.UpdateChildren(name, children, branch)
- changes := []ChangeTuple{{POST_ADD, nil, rev.GetData()}}
- branch.Latest.Drop(txid, false)
- n.Root.MakeLatest(branch, rev, changes)
- return rev
+ return exists
}
+ childRev := n.MakeNode(data, txid).Latest(txid)
+ children = append(children, childRev)
+ rev = rev.UpdateChildren(name, children, branch)
+ changes := []ChangeTuple{{POST_ADD, nil, childRev.GetData()}}
- } else {
- log.Errorf("cannot add to non-keyed container")
+ rev.Drop(txid, false)
+ n.makeLatest(branch, rev, changes)
+
+ return childRev
}
+ log.Errorf("cannot add to non-keyed container")
+
} else if field.Key != "" {
partition := strings.SplitN(path, "/", 2)
key := partition[0]
@@ -550,27 +556,33 @@
path = partition[1]
}
keyValue := field.KeyFromStr(key)
+
+ children = make([]Revision, len(rev.GetChildren()[name]))
copy(children, rev.GetChildren()[name])
+
idx, childRev := n.findRevByKey(children, field.Key, keyValue)
+
childNode := childRev.GetNode()
newChildRev := childNode.Add(path, data, txid, makeBranch)
+
children[idx] = newChildRev
- rev := rev.UpdateChildren(name, children, branch)
- branch.Latest.Drop(txid, false)
- n.Root.MakeLatest(branch, rev, nil)
- return rev
+
+ rev = rev.UpdateChildren(name, branch.GetLatest().GetChildren()[name], branch)
+ rev.Drop(txid, false)
+ n.makeLatest(branch, rev.GetBranch().GetLatest(), nil)
+
+ return newChildRev
} else {
log.Errorf("cannot add to non-keyed container")
}
} else {
log.Errorf("cannot add to non-container field")
}
+
return nil
}
-//
-// Remove operation
-//
+// Remove eliminates a node at the specified path
func (n *node) Remove(path string, txid string, makeBranch MakeBranchFunction) Revision {
for strings.HasPrefix(path, "/") {
path = path[1:]
@@ -580,14 +592,13 @@
log.Errorf("cannot remove for non-container mode")
}
var branch *Branch
- var ok bool
if txid == "" {
- branch = n.Branches[NONE]
- } else if branch, ok = n.Branches[txid]; !ok {
+ branch = n.GetBranch(NONE)
+ } else if branch = n.GetBranch(txid); branch == nil {
branch = makeBranch(n)
}
- rev := branch.Latest
+ rev := branch.GetLatest()
partition := strings.SplitN(path, "/", 2)
name := partition[0]
@@ -613,42 +624,36 @@
path = partition[1]
}
keyValue := field.KeyFromStr(key)
+ children = make([]Revision, len(rev.GetChildren()[name]))
+ copy(children, rev.GetChildren()[name])
if path != "" {
- for _, v := range rev.GetChildren()[name] {
- newV := reflect.ValueOf(v).Interface().(Revision)
- children = append(children, newV)
- }
idx, childRev := n.findRevByKey(children, field.Key, keyValue)
childNode := childRev.GetNode()
newChildRev := childNode.Remove(path, txid, makeBranch)
children[idx] = newChildRev
rev := rev.UpdateChildren(name, children, branch)
- branch.Latest.Drop(txid, false)
- n.Root.MakeLatest(branch, rev, nil)
- return rev
- } else {
- for _, v := range rev.GetChildren()[name] {
- newV := reflect.ValueOf(v).Interface().(Revision)
- children = append(children, newV)
- }
- idx, childRev := n.findRevByKey(children, field.Key, keyValue)
- if n.Proxy != nil {
- data := childRev.GetData()
- n.Proxy.InvokeCallbacks(PRE_REMOVE, false, data)
- postAnnouncement = append(postAnnouncement, ChangeTuple{POST_REMOVE, data, nil})
- } else {
- postAnnouncement = append(postAnnouncement, ChangeTuple{POST_REMOVE, childRev.GetData(), nil})
- }
- childRev.Drop(txid, true)
- children = append(children[:idx], children[idx+1:]...)
- rev := rev.UpdateChildren(name, children, branch)
- branch.Latest.Drop(txid, false)
- n.Root.MakeLatest(branch, rev, postAnnouncement)
+ branch.GetLatest().Drop(txid, false)
+ n.makeLatest(branch, rev, nil)
return rev
}
- } else {
- log.Errorf("cannot add to non-keyed container")
+ idx, childRev := n.findRevByKey(children, field.Key, keyValue)
+ if n.GetProxy() != nil {
+ data := childRev.GetData()
+ n.GetProxy().InvokeCallbacks(PRE_REMOVE, false, data)
+ postAnnouncement = append(postAnnouncement, ChangeTuple{POST_REMOVE, data, nil})
+ } else {
+ postAnnouncement = append(postAnnouncement, ChangeTuple{POST_REMOVE, childRev.GetData(), nil})
+ }
+ childRev.Drop(txid, true)
+ children = append(children[:idx], children[idx+1:]...)
+ rev := rev.UpdateChildren(name, children, branch)
+ branch.GetLatest().Drop(txid, false)
+ n.makeLatest(branch, rev, postAnnouncement)
+ return rev
+
}
+ log.Errorf("cannot add to non-keyed container")
+
} else {
log.Errorf("cannot add to non-container field")
}
@@ -658,16 +663,21 @@
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Branching ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// MakeBranchFunction is a type for function references intented to create a branch
type MakeBranchFunction func(*node) *Branch
+// MakeBranch creates a new branch for the provided transaction id
func (n *node) MakeBranch(txid string) *Branch {
- branchPoint := n.Branches[NONE].Latest
+ branchPoint := n.GetBranch(NONE).GetLatest()
branch := NewBranch(n, txid, branchPoint, true)
- n.Branches[txid] = branch
+ n.SetBranch(txid, branch)
return branch
}
+// DeleteBranch removes a branch with the specified id
func (n *node) DeleteBranch(txid string) {
+ n.Lock()
+ defer n.Unlock()
delete(n.Branches, txid)
}
@@ -684,19 +694,20 @@
return f
}
+// MergeBranch will integrate the contents of a transaction branch within the latest branch of a given node
func (n *node) MergeBranch(txid string, dryRun bool) (Revision, error) {
- srcBranch := n.Branches[txid]
- dstBranch := n.Branches[NONE]
+ srcBranch := n.GetBranch(txid)
+ dstBranch := n.GetBranch(NONE)
forkRev := srcBranch.Origin
- srcRev := srcBranch.Latest
- dstRev := dstBranch.Latest
+ srcRev := srcBranch.GetLatest()
+ dstRev := dstBranch.GetLatest()
rev, changes := Merge3Way(forkRev, srcRev, dstRev, n.mergeChild(txid, dryRun), dryRun)
if !dryRun {
- n.Root.MakeLatest(dstBranch, rev, changes)
- delete(n.Branches, txid)
+ n.makeLatest(dstBranch, rev, changes)
+ n.DeleteBranch(txid)
}
// TODO: return proper error when one occurs
@@ -707,8 +718,8 @@
//func (n *node) diff(hash1, hash2, txid string) {
// branch := n.Branches[txid]
-// rev1 := branch.get(hash1)
-// rev2 := branch.get(hash2)
+// rev1 := branch.GetHash(hash1)
+// rev2 := branch.GetHash(hash2)
//
// if rev1.GetHash() == rev2.GetHash() {
// // empty patch
@@ -740,14 +751,12 @@
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ node Proxy ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-func (n *node) GetProxy(path string, exclusive bool) *Proxy {
- //r := NewRoot(n.Type, n.KvStore)
- //r.node = n
- //r.KvStore = n.KvStore
-
- return n.getProxy(path, path, exclusive)
+// CreateProxy returns a reference to a sub-tree of the data model
+func (n *node) CreateProxy(path string, exclusive bool) *Proxy {
+ return n.createProxy(path, path, exclusive)
}
-func (n *node) getProxy(path string, fullPath string, exclusive bool) *Proxy {
+
+func (n *node) createProxy(path string, fullPath string, exclusive bool) *Proxy {
for strings.HasPrefix(path, "/") {
path = path[1:]
}
@@ -755,7 +764,7 @@
return n.makeProxy(path, fullPath, exclusive)
}
- rev := n.Branches[NONE].Latest
+ rev := n.GetBranch(NONE).GetLatest()
partition := strings.SplitN(path, "/", 2)
name := partition[0]
if len(partition) < 2 {
@@ -765,7 +774,7 @@
}
field := ChildrenFields(n.Type)[name]
- if field != nil && field.IsContainer {
+ if field.IsContainer {
if path == "" {
log.Error("cannot proxy a container field")
} else if field.Key != "" {
@@ -778,31 +787,31 @@
}
keyValue := field.KeyFromStr(key)
var children []Revision
- for _, v := range rev.GetChildren()[name] {
- newV := reflect.ValueOf(v).Interface().(Revision)
- children = append(children, newV)
- }
+ children = make([]Revision, len(rev.GetChildren()[name]))
+ copy(children, rev.GetChildren()[name])
_, childRev := n.findRevByKey(children, field.Key, keyValue)
childNode := childRev.GetNode()
- return childNode.getProxy(path, fullPath, exclusive)
+ return childNode.createProxy(path, fullPath, exclusive)
} else {
log.Error("cannot index into container with no keys")
}
} else {
childRev := rev.GetChildren()[name][0]
childNode := childRev.GetNode()
- return childNode.getProxy(path, fullPath, exclusive)
+ return childNode.createProxy(path, fullPath, exclusive)
}
return nil
}
func (n *node) makeProxy(path string, fullPath string, exclusive bool) *Proxy {
+ n.Lock()
+ defer n.Unlock()
r := &root{
node: n,
- Callbacks: n.Root.Callbacks,
- NotificationCallbacks: n.Root.NotificationCallbacks,
+ Callbacks: n.Root.GetCallbacks(),
+ NotificationCallbacks: n.Root.GetNotificationCallbacks(),
DirtyNodes: n.Root.DirtyNodes,
KvStore: n.Root.KvStore,
Loading: n.Root.Loading,
@@ -810,16 +819,19 @@
}
if n.Proxy == nil {
- n.Proxy = NewProxy(r, path, fullPath, exclusive)
+ n.Proxy = NewProxy(r, n, path, fullPath, exclusive)
} else {
if n.Proxy.Exclusive {
log.Error("node is already owned exclusively")
}
}
+
return n.Proxy
}
func (n *node) makeEventBus() *EventBus {
+ n.Lock()
+ defer n.Unlock()
if n.EventBus == nil {
n.EventBus = NewEventBus()
}
@@ -828,14 +840,44 @@
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Persistence Loading ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// LoadLatest accesses the persistent storage to construct the data model based on the stored information
func (n *node) LoadLatest(hash string) {
branch := NewBranch(n, "", nil, n.AutoPrune)
pr := &PersistedRevision{}
- rev := pr.Load(branch, n.Root.KvStore, n.Type, hash)
+ rev := pr.Load(branch, n.GetRoot().KvStore, n.Type, hash)
n.makeLatest(branch, rev, nil)
- n.Branches[NONE] = branch
+ n.SetBranch(NONE, branch)
}
-func (n *node) ExecuteCallbacks() {
- n.Root.ExecuteCallbacks()
+func (n *node) SetProxy(proxy *Proxy) {
+ n.Lock()
+ defer n.Unlock()
+ n.Proxy = proxy
+}
+
+func (n *node) GetProxy() *Proxy {
+ n.Lock()
+ defer n.Unlock()
+ return n.Proxy
+}
+
+func (n *node) GetBranch(key string) *Branch {
+ n.Lock()
+ defer n.Unlock()
+ if branch, exists := n.Branches[key]; exists {
+ return branch
+ }
+ return nil
+}
+
+func (n *node) SetBranch(key string, branch *Branch) {
+ n.Lock()
+ defer n.Unlock()
+ n.Branches[key] = branch
+}
+
+func (n *node) GetRoot() *root {
+ n.Lock()
+ defer n.Unlock()
+ return n.Root
}