VOL-1174: Keep only latest data and apply changes only when committed.

Change-Id: I2311eb9cf1487b39f23066df50d19b47fd5c7dcc
diff --git a/db/model/backend.go b/db/model/backend.go
index cd92c0b..25d568e 100644
--- a/db/model/backend.go
+++ b/db/model/backend.go
@@ -68,22 +68,24 @@
 
 func (b *Backend) makePath(key string) string {
 	path := fmt.Sprintf("%s/%s", b.PathPrefix, key)
-	log.Debugf("formatting path: %s", path)
 	return path
 }
 func (b *Backend) List(key string) (map[string]*kvstore.KVPair, error) {
 	return b.Client.List(b.makePath(key), b.Timeout)
 }
 func (b *Backend) Get(key string) (*kvstore.KVPair, error) {
+	formattedPath := b.makePath(key)
+	log.Debugf("Get key: %s, path: %s", key, formattedPath)
 	start := time.Now()
-	err, pair := b.Client.Get(b.makePath(key), b.Timeout)
+	err, pair := b.Client.Get(formattedPath, b.Timeout)
 	stop := time.Now()
 	GetProfiling().AddToDatabaseRetrieveTime(stop.Sub(start).Seconds())
 	return err, pair
 }
 func (b *Backend) Put(key string, value interface{}) error {
-	log.Debugf("Put key: %s, value: %+v, path: %s", key, string(value.([]byte)), b.makePath(key))
-	return b.Client.Put(b.makePath(key), value, b.Timeout)
+	formattedPath := b.makePath(key)
+	log.Debugf("Put key: %s, value: %+v, path: %s", key, string(value.([]byte)), formattedPath)
+	return b.Client.Put(formattedPath, value, b.Timeout)
 }
 func (b *Backend) Delete(key string) error {
 	return b.Client.Delete(b.makePath(key), b.Timeout)
diff --git a/db/model/merge.go b/db/model/merge.go
index ef490d8..e041e12 100644
--- a/db/model/merge.go
+++ b/db/model/merge.go
@@ -40,30 +40,30 @@
 }
 
 type changeAnalysis struct {
-	KeyMap1     map[reflect.Value]int
-	KeyMap2     map[reflect.Value]int
-	AddedKeys   map[reflect.Value]struct{}
-	RemovedKeys map[reflect.Value]struct{}
-	ChangedKeys map[reflect.Value]struct{}
+	KeyMap1     map[string]int
+	KeyMap2     map[string]int
+	AddedKeys   map[string]struct{}
+	RemovedKeys map[string]struct{}
+	ChangedKeys map[string]struct{}
 }
 
 func newChangeAnalysis(lst1, lst2 []Revision, keyName string) *changeAnalysis {
 	changes := &changeAnalysis{}
 
-	changes.KeyMap1 = make(map[reflect.Value]int)
-	changes.KeyMap2 = make(map[reflect.Value]int)
+	changes.KeyMap1 = make(map[string]int)
+	changes.KeyMap2 = make(map[string]int)
 
-	changes.AddedKeys = make(map[reflect.Value]struct{})
-	changes.RemovedKeys = make(map[reflect.Value]struct{})
-	changes.ChangedKeys = make(map[reflect.Value]struct{})
+	changes.AddedKeys = make(map[string]struct{})
+	changes.RemovedKeys = make(map[string]struct{})
+	changes.ChangedKeys = make(map[string]struct{})
 
 	for i, rev := range lst1 {
 		_, v := GetAttributeValue(rev.GetData(), keyName, 0)
-		changes.KeyMap1[v] = i
+		changes.KeyMap1[v.String()] = i
 	}
 	for i, rev := range lst2 {
 		_, v := GetAttributeValue(rev.GetData(), keyName, 0)
-		changes.KeyMap2[v] = i
+		changes.KeyMap2[v.String()] = i
 	}
 	for v, _ := range changes.KeyMap2 {
 		if _, ok := changes.KeyMap1[v]; !ok {
@@ -90,6 +90,7 @@
 	dryRun bool) (rev Revision, changes []ChangeTuple) {
 
 	var configChanged bool
+	var revsToDiscard []Revision
 
 	if dstRev.GetConfig() == forkRev.GetConfig() {
 		configChanged = dstRev.GetConfig() != srcRev.GetConfig()
@@ -153,6 +154,7 @@
 				}
 				for key, _ := range src.RemovedKeys {
 					oldRev := forkList[src.KeyMap1[key]]
+					revsToDiscard = append(revsToDiscard, oldRev)
 					changes = append(changes, ChangeTuple{POST_REMOVE, oldRev.GetData()})
 				}
 				for key, _ := range src.ChangedKeys {
@@ -165,7 +167,9 @@
 					}
 				}
 
-				newChildren[fieldName] = newList
+				if !dryRun {
+					newChildren[fieldName] = newList
+				}
 			} else {
 				src := newChangeAnalysis(forkList, srcList, field.Key)
 				dst := newChangeAnalysis(forkList, dstList, field.Key)
@@ -212,9 +216,10 @@
 					if _, changed := dst.ChangedKeys[key]; changed {
 						log.Error("conflict error - revision has changed")
 					}
-					if _, removed := dst.ChangedKeys[key]; !removed {
+					if _, removed := dst.RemovedKeys[key]; !removed {
 						dstIdx := dst.KeyMap2[key]
 						oldRev := newList[dstIdx]
+						revsToDiscard = append(revsToDiscard, oldRev)
 
 						copy(newList[dstIdx:], newList[dstIdx+1:])
 						newList[len(newList)-1] = nil
@@ -224,8 +229,9 @@
 					}
 				}
 
-				newChildren[fieldName] = newList
-
+				if !dryRun {
+					newChildren[fieldName] = newList
+				}
 			}
 		}
 	}
@@ -237,6 +243,11 @@
 			rev = dstRev
 		}
 
+		for _, discarded := range revsToDiscard {
+			discarded.Drop("", true)
+		}
+
+		dstRev.GetBranch().Latest.Drop("", configChanged)
 		rev = rev.UpdateAllChildren(newChildren, dstRev.GetBranch())
 
 		if configChanged {
diff --git a/db/model/node.go b/db/model/node.go
index 0a77fff..fd58cf8 100644
--- a/db/model/node.go
+++ b/db/model/node.go
@@ -89,22 +89,27 @@
 	if changeAnnouncement != nil && branch.Txid == "" {
 		if n.Proxy != nil {
 			for _, change := range changeAnnouncement {
-				// TODO: Invoke callback
 				fmt.Printf("invoking callback - changeType: %+v, data:%+v\n", change.Type, change.Data)
 				n.root.addCallback(n.Proxy.InvokeCallbacks, change.Type, change.Data, true)
 			}
 		}
 
 		for _, change := range changeAnnouncement {
-			// TODO: send notifications
 			fmt.Printf("sending notification - changeType: %+v, data:%+v\n", change.Type, change.Data)
 			n.root.addNotificationCallback(n.makeEventBus().Advertise, change.Type, change.Data, revision.GetHash())
 		}
 	}
 }
 
-func (n *Node) Latest() Revision {
-	if branch, exists := n.Branches[NONE]; exists {
+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
+		}
+	} else if branch, exists = n.Branches[NONE]; exists {
 		return branch.Latest
 	}
 	return nil
@@ -127,7 +132,9 @@
 
 					for i := 0; i < fieldValue.Len(); i++ {
 						v := fieldValue.Index(i)
-						rev := n.MakeNode(v.Interface(), txid).Latest()
+
+						rev := n.MakeNode(v.Interface(), txid).Latest(txid)
+
 						_, key := GetAttributeValue(v.Interface(), field.Key, 0)
 						for _, k := range keysSeen {
 							if k == key.String() {
@@ -353,6 +360,7 @@
 			}
 			children[idx] = newChildRev
 			rev = rev.UpdateChildren(name, children, branch)
+			branch.Latest.Drop(txid, false)
 			n.root.MakeLatest(branch, rev, nil)
 			return rev
 		} else {
@@ -363,6 +371,7 @@
 		childNode := childRev.GetNode()
 		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)
 		return rev
 	}
@@ -393,8 +402,8 @@
 			fmt.Println("checking access violations")
 		}
 		rev := branch.Latest.UpdateData(data, branch)
-		n.root.MakeLatest(branch, rev, nil) // TODO -> changeAnnouncement needs to be a tuple (CallbackType.
-		// POST_UPDATE, rev.data)
+		branch.Latest.Drop(branch.Txid, true)
+		n.root.MakeLatest(branch, rev, []ChangeTuple{{POST_UPDATE, rev.GetData()}})
 		return rev
 	} else {
 		return branch.Latest
@@ -453,11 +462,11 @@
 					fmt.Errorf("duplicate key found: %s", key.String())
 				}
 
-				childRev := n.MakeNode(data, "").Latest()
+				childRev := n.MakeNode(data, txid).Latest(txid)
 				children = append(children, childRev)
 				rev := rev.UpdateChildren(name, children, branch)
-				n.root.MakeLatest(branch, rev, nil) // TODO -> changeAnnouncement needs to be a tuple (CallbackType.
-				// POST_ADD, rev.data)
+				branch.Latest.Drop(txid, false)
+				n.root.MakeLatest(branch, rev, []ChangeTuple{{POST_ADD, rev.GetData()}})
 				return rev
 			} else {
 				fmt.Errorf("cannot add to non-keyed container\n")
@@ -477,6 +486,7 @@
 			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
 		} else {
@@ -543,6 +553,7 @@
 				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 {
@@ -558,8 +569,10 @@
 				} else {
 					postAnnouncement = append(postAnnouncement, ChangeTuple{POST_REMOVE, childRev.GetData()})
 				}
+				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)
 				return rev
 			}
diff --git a/db/model/non_persisted_revision.go b/db/model/non_persisted_revision.go
index 06bb53d..8b81a48 100644
--- a/db/model/non_persisted_revision.go
+++ b/db/model/non_persisted_revision.go
@@ -143,14 +143,32 @@
 			if field.IsContainer {
 				for _, rev := range npr.Children[fieldName] {
 					childData := rev.Get(depth - 1)
-					childDataHolder = reflect.Append(childDataHolder, reflect.ValueOf(childData))
-					//fmt.Printf("data:%+v, dataHolder:%+v\n", childData, childDataHolder)
+					foundEntry := false
+					for i := 0; i < childDataHolder.Len(); i++ {
+						if reflect.DeepEqual(childDataHolder.Index(i).Interface(), childData) {
+							foundEntry = true
+							break
+						}
+					}
+					if !foundEntry {
+						// avoid duplicates by adding if the child was not found in the holder
+						childDataHolder = reflect.Append(childDataHolder, reflect.ValueOf(childData))
+					}
 				}
 			} else {
 				rev := npr.Children[fieldName][0]
 				childData := rev.Get(depth - 1)
-				childDataHolder = reflect.Append(childDataHolder, reflect.ValueOf(childData))
-				//fmt.Printf("data:%+v, dataHolder:%+v\n", childData, childDataHolder)
+				foundEntry := false
+				for i := 0; i < childDataHolder.Len(); i++ {
+					if reflect.DeepEqual(childDataHolder.Index(i).Interface(), childData) {
+						foundEntry = true
+						break
+					}
+				}
+				if !foundEntry {
+					// avoid duplicates by adding if the child was not found in the holder
+					childDataHolder = reflect.Append(childDataHolder, reflect.ValueOf(childData))
+				}
 			}
 			// Merge child data with cloned object
 			reflect.ValueOf(data).Elem().FieldByName(childDataName).Set(childDataHolder)
@@ -160,6 +178,9 @@
 }
 
 func (npr *NonPersistedRevision) UpdateData(data interface{}, branch *Branch) Revision {
+	// TODO: Need to keep the hash for the old revision.
+	// TODO: This will allow us to get rid of the unnecessary data
+
 	newRev := reflect.ValueOf(npr).Elem().Interface().(NonPersistedRevision)
 	newRev.SetBranch(branch)
 	log.Debugf("newRev config : %+v, npr: %+v", newRev.GetConfig(), npr)
@@ -195,3 +216,7 @@
 
 	return &newRev
 }
+
+func (npr *NonPersistedRevision) Drop(txid string, includeConfig bool) {
+	//npr.SetConfig(nil)
+}
diff --git a/db/model/persisted_revision.go b/db/model/persisted_revision.go
index ece0bb0..05c4090 100644
--- a/db/model/persisted_revision.go
+++ b/db/model/persisted_revision.go
@@ -21,6 +21,7 @@
 	"encoding/json"
 	"fmt"
 	"github.com/golang/protobuf/proto"
+	"github.com/opencord/voltha-go/common/log"
 	"io/ioutil"
 	"reflect"
 	"time"
@@ -51,6 +52,9 @@
 }
 
 func (pr *PersistedRevision) store() {
+	if pr.GetBranch().Txid != "" {
+		return
+	}
 	if ok, _ := pr.kvStore.Get(pr.Revision.GetHash()); ok != nil {
 		return
 	}
@@ -221,3 +225,30 @@
 
 	return newPR
 }
+
+// Drop takes care of eliminating a revision hash that is no longer needed
+// and its associated config when required
+func (pr *PersistedRevision) Drop(txid string, includeConfig bool) {
+	if pr.kvStore != nil && txid == "" {
+		if includeConfig {
+			log.Debugf("removing rev config - hash: %s", pr.GetConfig().Hash)
+			if err := pr.kvStore.Delete(pr.GetConfig().Hash); err != nil {
+				log.Errorf(
+					"failed to remove rev config - hash: %s, err: %s",
+					pr.GetConfig().Hash,
+					err.Error(),
+				)
+			}
+		}
+
+		log.Debugf("removing rev - hash: %s", pr.GetHash())
+		if err := pr.kvStore.Delete(pr.GetHash()); err != nil {
+			log.Errorf("failed to remove rev - hash: %s, err: %s", pr.GetHash(), err.Error())
+		}
+	} else {
+		if includeConfig {
+			log.Debugf("Attempted to remove revision config:%s linked to transaction:%s", pr.GetConfig().Hash, txid)
+		}
+		log.Debugf("Attempted to remove revision:%s linked to transaction:%s", pr.GetHash(), txid)
+	}
+}
diff --git a/db/model/proxy.go b/db/model/proxy.go
index 4aae7f4..3e33849 100644
--- a/db/model/proxy.go
+++ b/db/model/proxy.go
@@ -113,7 +113,7 @@
 	return p.Root.Remove(fullPath, txid, nil)
 }
 
-func (p *Proxy) openTransaction() *Transaction {
+func (p *Proxy) OpenTransaction() *Transaction {
 	txid := p.Root.MakeTxBranch()
 	return NewTransaction(p, txid)
 }
diff --git a/db/model/proxy_test.go b/db/model/proxy_test.go
index e7b644c..bf80bb7 100644
--- a/db/model/proxy_test.go
+++ b/db/model/proxy_test.go
@@ -21,6 +21,7 @@
 	"fmt"
 	"github.com/google/uuid"
 	"github.com/opencord/voltha-go/common/log"
+	"github.com/opencord/voltha-go/protos/common"
 	"github.com/opencord/voltha-go/protos/voltha"
 	"reflect"
 	"strconv"
@@ -52,8 +53,7 @@
 )
 
 func init() {
-
-	log.AddPackage(log.JSON, log.ErrorLevel, nil)
+	log.AddPackage(log.JSON, log.DebugLevel, nil)
 	log.UpdateAllLoggers(log.Fields{"instanceId": "proxy_test"})
 
 	defer log.CleanUp()
@@ -61,7 +61,7 @@
 	pt.Backend = NewBackend(pt.DbType, pt.DbHost, pt.DbPort, pt.DbTimeout, pt.DbPrefix)
 
 	msgClass := &voltha.Voltha{}
-	root := NewRoot(msgClass, pt.Backend, nil)
+	root := NewRoot(msgClass, pt.Backend)
 	pt.Root = root.Load(msgClass)
 
 	GetProfiling().Report()
@@ -69,18 +69,6 @@
 	pt.Proxy = pt.Root.Node.GetProxy("/", false)
 }
 
-//func Test_Proxy_0_GetRootProxy(t *testing.T) {
-//	pt.Backend = NewBackend(pt.DbType, pt.DbHost, pt.DbPort, pt.DbTimeout, pt.DbPrefix)
-//
-//	msgClass := &voltha.Voltha{}
-//	root := NewRoot(msgClass, pt.Backend, nil)
-//	pt.Root = root.Load(msgClass)
-//
-//	GetProfiling().Report()
-//
-//	pt.Proxy = pt.Root.Node.GetProxy("/", false)
-//}
-
 func Test_Proxy_1_GetDevices(t *testing.T) {
 	devices := pt.Proxy.Get("/devices", 1, false, "")
 
@@ -93,86 +81,28 @@
 	}
 }
 
-func Test_Proxy_2_GetDevice(t *testing.T) {
-	basePath := "/devices/" + targetDeviceId
-	device1 := pt.Proxy.Get(basePath+"/ports", 1, false, "")
-	t.Logf("retrieved device with ports: %+v", device1)
-
-	device2 := pt.Proxy.Get(basePath, 0, false, "")
-
-	t.Logf("retrieved device: %+v", device2)
-}
-
-//func Test_Proxy_3_AddDevice(t *testing.T) {
-//	//ports := []*voltha.Port{
-//	//	{
-//	//		PortNo:     123,
-//	//		Label:      "test-port-0",
-//	//		Type:       voltha.Port_PON_OLT,
-//	//		AdminState: common.AdminState_ENABLED,
-//	//		OperStatus: common.OperStatus_ACTIVE,
-//	//		DeviceId:   "etcd_port-0-device-id",
-//	//		Peers:      []*voltha.Port_PeerPort{},
-//	//	},
-//	//}
-//	devIdBin, _ := uuid.New().MarshalBinary()
-//	devId := hex.EncodeToString(devIdBin)[:12]
-//
-//	device := &voltha.Device{
-//		Id:                  devId,
-//		Type:                "simulated_olt",
-//		//Root:                true,
-//		//ParentId:            "",
-//		//ParentPortNo:        0,
-//		//Vendor:              "voltha-test",
-//		//Model:               "latest-voltha-simulated-olt",
-//		//HardwareVersion:     "1.0.0",
-//		//FirmwareVersion:     "1.0.0",
-//		//Images:              &voltha.Images{},
-//		//SerialNumber:        "abcdef-123456",
-//		//VendorId:            "DEADBEEF-INC",
-//		//Adapter:             "simulated_olt",
-//		//Vlan:                1234,
-//		Address:             &voltha.Device_HostAndPort{HostAndPort: "1.2.3.4:5555"},
-//		//ExtraArgs:           "",
-//		//ProxyAddress:        &voltha.Device_ProxyAddress{},
-//		AdminState:          voltha.AdminState_PREPROVISIONED,
-//		//OperStatus:          common.OperStatus_ACTIVE,
-//		//Reason:              "",
-//		//ConnectStatus:       common.ConnectStatus_REACHABLE,
-//		//Custom:              &any.Any{},
-//		//Ports:               ports,
-//		//Flows:               &openflow_13.Flows{},
-//		//FlowGroups:          &openflow_13.FlowGroups{},
-//		//PmConfigs:           &voltha.PmConfigs{},
-//		//ImageDownloads:      []*voltha.ImageDownload{},
-//	}
-//
-//	//if retrieved := pt.Proxy.Get("/devices/00019b09a90bbe17", 0, false, ""); retrieved == nil {
-//	//	t.Error("Failed to get device")
-//	//} else {
-//	//	devIdBin, _ := uuid.New().MarshalBinary()
-//	//	devId = "0001" + hex.EncodeToString(devIdBin)[:12]
-//	//	newDevice := Clone(de\).(*voltha.Device)
-//	//	newDevice.Id = devId
-//
-//		if added := pt.Proxy.Add("/devices", device, ""); added == nil {
-//			t.Error("Failed to add device")
-//		} else {
-//			t.Logf("Added device : %+v", added)
-//		}
-//	//}
-//
-//}
-func Test_Proxy_3_AddDevice(t *testing.T) {
+func Test_Proxy_2_AddDevice(t *testing.T) {
 	devIdBin, _ := uuid.New().MarshalBinary()
 	devId = "0001" + hex.EncodeToString(devIdBin)[:12]
 
+	ports := []*voltha.Port{
+		{
+			PortNo:     123,
+			Label:      "test-port-0",
+			Type:       voltha.Port_PON_OLT,
+			AdminState: common.AdminState_ENABLED,
+			OperStatus: common.OperStatus_ACTIVE,
+			DeviceId:   "etcd_port-0-device-id",
+			Peers:      []*voltha.Port_PeerPort{},
+		},
+	}
+
 	device := &voltha.Device{
 		Id:         devId,
 		Type:       "simulated_olt",
 		Address:    &voltha.Device_HostAndPort{HostAndPort: "1.2.3.4:5555"},
 		AdminState: voltha.AdminState_PREPROVISIONED,
+		Ports:      ports,
 	}
 
 	if added := pt.Proxy.Add("/devices", device, ""); added == nil {
@@ -182,7 +112,7 @@
 	}
 }
 
-func Test_Proxy_4_CheckAddedDevice(t *testing.T) {
+func Test_Proxy_3_GetDevice_PostAdd(t *testing.T) {
 	if d := pt.Proxy.Get("/devices/"+devId, 0, false, ""); !reflect.ValueOf(d).IsValid() {
 		t.Error("Failed to find added device")
 	} else {
@@ -192,7 +122,7 @@
 	}
 }
 
-func Test_Proxy_5_UpdateDevice(t *testing.T) {
+func Test_Proxy_4_UpdateDevice(t *testing.T) {
 	if retrieved := pt.Proxy.Get("/devices/"+targetDeviceId, 1, false, ""); retrieved == nil {
 		t.Error("Failed to get device")
 	} else {
@@ -216,13 +146,13 @@
 	}
 }
 
-func Test_Proxy_6_CheckUpdatedDevice(t *testing.T) {
+func Test_Proxy_5_GetDevice_PostUpdate(t *testing.T) {
 	device := pt.Proxy.Get("/devices/"+targetDeviceId, 0, false, "")
 
 	t.Logf("content of updated device: %+v", device)
 }
 
-func Test_Proxy_7_RemoveDevice(t *testing.T) {
+func Test_Proxy_6_RemoveDevice(t *testing.T) {
 	if removed := pt.Proxy.Remove("/devices/"+devId, ""); removed == nil {
 		t.Error("Failed to remove device")
 	} else {
@@ -230,7 +160,7 @@
 	}
 }
 
-func Test_Proxy_8_CheckRemovedDevice(t *testing.T) {
+func Test_Proxy_7_GetDevice_PostRemove(t *testing.T) {
 	if d := pt.Proxy.Get("/devices/"+devId, 0, false, ""); reflect.ValueOf(d).IsValid() {
 		djson, _ := json.Marshal(d)
 		t.Errorf("Device was not removed - %s", djson)
@@ -253,7 +183,8 @@
 	name := args[0].(map[string]string)
 	id := args[1]
 	fmt.Printf("Running second callback - name: %s, id: %f\n", name["name"], id)
-	panic("Generating a panic in second callback")
+	// FIXME: the panic call seem to interfere with the logging mechanism
+	//panic("Generating a panic in second callback")
 	return nil
 }
 func thirdCallback(args ...interface{}) interface{} {
diff --git a/db/model/revision.go b/db/model/revision.go
index 44f97b2..a7b0c39 100644
--- a/db/model/revision.go
+++ b/db/model/revision.go
@@ -19,6 +19,7 @@
 	Finalize()
 	SetConfig(revision *DataRevision)
 	GetConfig() *DataRevision
+	Drop(txid string, includeConfig bool)
 	SetChildren(children map[string][]Revision)
 	GetChildren() map[string][]Revision
 	SetHash(hash string)
diff --git a/db/model/root.go b/db/model/root.go
index 1c14f9a..3e3cc43 100644
--- a/db/model/root.go
+++ b/db/model/root.go
@@ -35,15 +35,16 @@
 	NotificationCallbacks []CallbackTuple
 }
 
-func NewRoot(initialData interface{}, kvStore *Backend, revisionClass interface{}) *Root {
+func NewRoot(initialData interface{}, kvStore *Backend) *Root {
 	root := &Root{}
 	root.KvStore = kvStore
 	root.DirtyNodes = make(map[string][]*Node)
 	root.Loading = false
-	if kvStore != nil /*&& TODO: RevisionClass is not a subclass of PersistedRevision ??? */ {
-		revisionClass = reflect.TypeOf(PersistedRevision{})
+	if kvStore != nil {
+		root.RevisionClass = reflect.TypeOf(PersistedRevision{})
+	} else {
+		root.RevisionClass = reflect.TypeOf(NonPersistedRevision{})
 	}
-	root.RevisionClass = revisionClass
 	root.Callbacks = []CallbackTuple{}
 	root.NotificationCallbacks = []CallbackTuple{}
 
@@ -188,8 +189,8 @@
 
 func (r *Root) Load(rootClass interface{}) *Root {
 	//fakeKvStore := &Backend{}
-	//root := NewRoot(rootClass, fakeKvStore, PersistedRevision{})
-	//r.KvStore = KvStore
+	//root := NewRoot(rootClass, nil)
+	//root.KvStore = r.KvStore
 	r.loadFromPersistence(rootClass)
 	return r
 }
diff --git a/db/model/root_test.go b/db/model/root_test.go
index 1887a0a..9c57730 100644
--- a/db/model/root_test.go
+++ b/db/model/root_test.go
@@ -24,21 +24,21 @@
 )
 
 var (
-	backend *Backend
-	//rootPrefix = "service/voltha/data/core/0001"
+	backend    *Backend
+	rootPrefix = "service/voltha/data/core/0001"
 
-	basePrefix  = "service/voltha/service/vcores/data/devices"
-	deviceId    = "00016f13befaedcc"
-	rootPrefix  = basePrefix + "/" + deviceId
+	//basePrefix  = "service/voltha/service/vcores/data/devices"
+	deviceId = "00016f13befaedcc"
+	//rootPrefix  = basePrefix + "/" + deviceId
 	deviceProxy = "/devices/" + deviceId
 )
 
 func Test_NewRoot(t *testing.T) {
 	backend = NewBackend(ETCD_KV, etcd_host, etcd_port, timeout, rootPrefix)
 
-	//var msgClass *voltha.VolthaInstance
-	var msgClass *voltha.DeviceInstance
-	root := NewRoot(msgClass, backend, nil)
+	var msgClass *voltha.Voltha
+	//var msgClass *voltha.DeviceInstance
+	root := NewRoot(msgClass, backend)
 
 	start := time.Now()
 
diff --git a/db/model/transaction_test.go b/db/model/transaction_test.go
index 130a992..3b1b5bf 100644
--- a/db/model/transaction_test.go
+++ b/db/model/transaction_test.go
@@ -19,6 +19,7 @@
 	"encoding/hex"
 	"github.com/google/uuid"
 	"github.com/opencord/voltha-go/common/log"
+	"github.com/opencord/voltha-go/protos/common"
 	"github.com/opencord/voltha-go/protos/voltha"
 	"reflect"
 	"strconv"
@@ -50,8 +51,7 @@
 )
 
 func init() {
-
-	log.AddPackage(log.JSON, log.ErrorLevel, nil)
+	log.AddPackage(log.JSON, log.DebugLevel, nil)
 	log.UpdateAllLoggers(log.Fields{"instanceId": "transaction_test"})
 
 	defer log.CleanUp()
@@ -59,17 +59,21 @@
 	tx.Backend = NewBackend(tx.DbType, tx.DbHost, tx.DbPort, tx.DbTimeout, tx.DbPrefix)
 
 	msgClass := &voltha.Voltha{}
-	root := NewRoot(msgClass, tx.Backend, nil)
-	tx.Root = root.Load(msgClass)
+	root := NewRoot(msgClass, tx.Backend)
+
+	if tx.Backend != nil {
+		tx.Root = root.Load(msgClass)
+	} else {
+		tx.Root = root
+	}
 
 	GetProfiling().Report()
 
 	tx.Proxy = tx.Root.Node.GetProxy("/", false)
-
 }
 
 func Test_Transaction_1_GetDevices(t *testing.T) {
-	getTx := tx.Proxy.openTransaction()
+	getTx := tx.Proxy.OpenTransaction()
 
 	devices := getTx.Get("/devices", 1, false)
 
@@ -81,47 +85,61 @@
 		t.Logf("retrieved devices: %+v", devices)
 	}
 
-	tx.Proxy.commitTransaction(getTx.txid)
+	getTx.Commit()
 }
 
-func Test_Transaction_2_GetDevice(t *testing.T) {
-
-	basePath := "/devices/" + txTargetDevId
-
-	getDevWithPortsTx := tx.Proxy.openTransaction()
-	device1 := getDevWithPortsTx.Get(basePath+"/ports", 1, false)
-	t.Logf("retrieved device with ports: %+v", device1)
-	tx.Proxy.commitTransaction(getDevWithPortsTx.txid)
-
-	getDevTx := tx.Proxy.openTransaction()
-	device2 := getDevTx.Get(basePath, 0, false)
-	t.Logf("retrieved device: %+v", device2)
-	tx.Proxy.commitTransaction(getDevTx.txid)
-}
-
-func Test_Transaction_3_AddDevice(t *testing.T) {
+func Test_Transaction_2_AddDevice(t *testing.T) {
 	devIdBin, _ := uuid.New().MarshalBinary()
 	txDevId = "0001" + hex.EncodeToString(devIdBin)[:12]
 
+	ports := []*voltha.Port{
+		{
+			PortNo:     123,
+			Label:      "test-port-0",
+			Type:       voltha.Port_PON_OLT,
+			AdminState: common.AdminState_ENABLED,
+			OperStatus: common.OperStatus_ACTIVE,
+			DeviceId:   "etcd_port-0-device-id",
+			Peers:      []*voltha.Port_PeerPort{},
+		},
+	}
+
 	device := &voltha.Device{
 		Id:         txDevId,
 		Type:       "simulated_olt",
 		Address:    &voltha.Device_HostAndPort{HostAndPort: "1.2.3.4:5555"},
 		AdminState: voltha.AdminState_PREPROVISIONED,
+		Ports:      ports,
 	}
 
-	addTx := tx.Proxy.openTransaction()
+	addTx := tx.Proxy.OpenTransaction()
 
 	if added := addTx.Add("/devices", device); added == nil {
 		t.Error("Failed to add device")
 	} else {
 		t.Logf("Added device : %+v", added)
 	}
-	tx.Proxy.commitTransaction(addTx.txid)
+	addTx.Commit()
+}
+
+func Test_Transaction_3_GetDevice_PostAdd(t *testing.T) {
+
+	basePath := "/devices/" + txDevId
+
+	getDevWithPortsTx := tx.Proxy.OpenTransaction()
+	device1 := getDevWithPortsTx.Get(basePath+"/ports", 1, false)
+	t.Logf("retrieved device with ports: %+v", device1)
+	getDevWithPortsTx.Commit()
+
+	getDevTx := tx.Proxy.OpenTransaction()
+	device2 := getDevTx.Get(basePath, 0, false)
+	t.Logf("retrieved device: %+v", device2)
+
+	getDevTx.Commit()
 }
 
 func Test_Transaction_4_UpdateDevice(t *testing.T) {
-	updateTx := tx.Proxy.openTransaction()
+	updateTx := tx.Proxy.OpenTransaction()
 	if retrieved := updateTx.Get("/devices/"+txTargetDevId, 1, false); retrieved == nil {
 		t.Error("Failed to get device")
 	} else {
@@ -144,15 +162,43 @@
 			t.Logf("Updated device : %+v", afterUpdate.(Revision).GetData())
 		}
 	}
-	tx.Proxy.commitTransaction(updateTx.txid)
+	updateTx.Commit()
 }
 
-func Test_Transaction_5_RemoveDevice(t *testing.T) {
-	removeTx := tx.Proxy.openTransaction()
+func Test_Transaction_5_GetDevice_PostUpdate(t *testing.T) {
+
+	basePath := "/devices/" + txDevId
+
+	getDevWithPortsTx := tx.Proxy.OpenTransaction()
+	device1 := getDevWithPortsTx.Get(basePath+"/ports", 1, false)
+	t.Logf("retrieved device with ports: %+v", device1)
+	getDevWithPortsTx.Commit()
+
+	getDevTx := tx.Proxy.OpenTransaction()
+	device2 := getDevTx.Get(basePath, 0, false)
+	t.Logf("retrieved device: %+v", device2)
+
+	getDevTx.Commit()
+}
+
+
+func Test_Transaction_6_RemoveDevice(t *testing.T) {
+	removeTx := tx.Proxy.OpenTransaction()
 	if removed := removeTx.Remove("/devices/" + txDevId); removed == nil {
 		t.Error("Failed to remove device")
 	} else {
 		t.Logf("Removed device : %+v", removed)
 	}
-	tx.Proxy.commitTransaction(removeTx.txid)
+	removeTx.Commit()
+}
+
+func Test_Transaction_7_GetDevice_PostRemove(t *testing.T) {
+
+	basePath := "/devices/" + txDevId
+
+	getDevTx := tx.Proxy.OpenTransaction()
+	device := tx.Proxy.Get(basePath, 0, false, "")
+	t.Logf("retrieved device: %+v", device)
+
+	getDevTx.Commit()
 }