blob: d2d228f7849b2bc68bfd41194152b24a840fbf3b [file] [log] [blame]
khenaidoobf6e7bb2018-08-14 22:27:29 -04001/*
2 * Copyright 2018-present Open Networking Foundation
3
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7
8 * http://www.apache.org/licenses/LICENSE-2.0
9
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Stephane Barbariedc5022d2018-11-19 15:21:44 -050016
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040017package model
18
19import (
20 "bytes"
21 "compress/gzip"
Stephane Barbarieef6650d2019-07-18 12:15:09 -040022 "context"
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040023 "github.com/golang/protobuf/proto"
Stephane Barbarieef6650d2019-07-18 12:15:09 -040024 "github.com/google/uuid"
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -040025 "github.com/opencord/voltha-go/common/log"
Stephane Barbariee0a4c792019-01-16 11:26:29 -050026 "github.com/opencord/voltha-go/db/kvstore"
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040027 "reflect"
Stephane Barbarie1ab43272018-12-08 21:42:13 -050028 "strings"
Stephane Barbariedc5022d2018-11-19 15:21:44 -050029 "sync"
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040030)
31
Stephane Barbariedc5022d2018-11-19 15:21:44 -050032// PersistedRevision holds information of revision meant to be saved in a persistent storage
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040033type PersistedRevision struct {
Stephane Barbarieec0919b2018-09-05 14:14:29 -040034 Revision
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040035 Compress bool
Stephane Barbariee0a4c792019-01-16 11:26:29 -050036
Stephane Barbarieef6650d2019-07-18 12:15:09 -040037 events chan *kvstore.Event
38 kvStore *Backend
39 mutex sync.RWMutex
40 versionMutex sync.RWMutex
41 Version int64
42 isStored bool
43 isWatched bool
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040044}
45
Stephane Barbarie40fd3b22019-04-23 21:50:47 -040046type watchCache struct {
47 Cache sync.Map
48}
49
50var watchCacheInstance *watchCache
51var watchCacheOne sync.Once
52
53func Watches() *watchCache {
54 watchCacheOne.Do(func() {
55 watchCacheInstance = &watchCache{Cache: sync.Map{}}
56 })
57 return watchCacheInstance
58}
59
Stephane Barbariedc5022d2018-11-19 15:21:44 -050060// NewPersistedRevision creates a new instance of a PersistentRevision structure
Stephane Barbarieec0919b2018-09-05 14:14:29 -040061func NewPersistedRevision(branch *Branch, data interface{}, children map[string][]Revision) Revision {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040062 pr := &PersistedRevision{}
Stephane Barbariedc5022d2018-11-19 15:21:44 -050063 pr.kvStore = branch.Node.GetRoot().KvStore
Stephane Barbarieef6650d2019-07-18 12:15:09 -040064 pr.Version = 1
Stephane Barbariedc5022d2018-11-19 15:21:44 -050065 pr.Revision = NewNonPersistedRevision(nil, branch, data, children)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040066 return pr
67}
68
Stephane Barbarieef6650d2019-07-18 12:15:09 -040069func (pr *PersistedRevision) getVersion() int64 {
70 pr.versionMutex.RLock()
71 defer pr.versionMutex.RUnlock()
72 return pr.Version
73}
74
75func (pr *PersistedRevision) setVersion(version int64) {
76 pr.versionMutex.Lock()
77 defer pr.versionMutex.Unlock()
78 pr.Version = version
79}
80
Stephane Barbariedc5022d2018-11-19 15:21:44 -050081// Finalize is responsible of saving the revision in the persistent storage
Stephane Barbarie1ab43272018-12-08 21:42:13 -050082func (pr *PersistedRevision) Finalize(skipOnExist bool) {
83 pr.store(skipOnExist)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040084}
85
Stephane Barbarie1ab43272018-12-08 21:42:13 -050086func (pr *PersistedRevision) store(skipOnExist bool) {
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -040087 if pr.GetBranch().Txid != "" {
88 return
89 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040090
Stephane Barbarie7512fc82019-05-07 12:25:46 -040091 log.Debugw("ready-to-store-revision", log.Fields{"hash": pr.GetHash(), "name": pr.GetName(), "data": pr.GetData()})
Stephane Barbarie1ab43272018-12-08 21:42:13 -050092
Stephane Barbarieef6650d2019-07-18 12:15:09 -040093 // clone the revision data to avoid any race conditions with processes
94 // accessing the same data
95 cloned := proto.Clone(pr.GetConfig().Data.(proto.Message))
96
97 if blob, err := proto.Marshal(cloned); err != nil {
98 log.Errorw("problem-to-marshal", log.Fields{"error": err, "hash": pr.GetHash(), "name": pr.GetName(), "data": pr.GetData()})
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040099 } else {
100 if pr.Compress {
101 var b bytes.Buffer
102 w := gzip.NewWriter(&b)
103 w.Write(blob)
104 w.Close()
105 blob = b.Bytes()
106 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500107
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400108 GetRevCache().Set(pr.GetName(), pr)
Stephane Barbarief7fc1782019-03-28 22:33:41 -0400109 if err := pr.kvStore.Put(pr.GetName(), blob); err != nil {
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400110 log.Warnw("problem-storing-revision", log.Fields{"error": err, "hash": pr.GetHash(), "name": pr.GetName(), "data": pr.GetConfig().Data})
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500111 } else {
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400112 log.Debugw("storing-revision", log.Fields{"hash": pr.GetHash(), "name": pr.GetName(), "data": pr.GetConfig().Data, "version": pr.getVersion()})
Stephane Barbarie3cb01222019-01-16 17:15:56 -0500113 pr.isStored = true
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500114 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400115 }
116}
117
Stephane Barbariee0a4c792019-01-16 11:26:29 -0500118func (pr *PersistedRevision) SetupWatch(key string) {
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400119 if key == "" {
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400120 log.Debugw("ignoring-watch", log.Fields{"key": key, "revision-hash": pr.GetHash()})
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400121 return
122 }
123
124 if _, exists := Watches().Cache.LoadOrStore(key+"-"+pr.GetHash(), struct{}{}); exists {
125 return
126 }
127
Stephane Barbariee0a4c792019-01-16 11:26:29 -0500128 if pr.events == nil {
129 pr.events = make(chan *kvstore.Event)
130
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400131 log.Debugw("setting-watch-channel", log.Fields{"key": key, "revision-hash": pr.GetHash()})
Stephane Barbariee0a4c792019-01-16 11:26:29 -0500132
Stephane Barbarief7fc1782019-03-28 22:33:41 -0400133 pr.SetName(key)
Stephane Barbariee0a4c792019-01-16 11:26:29 -0500134 pr.events = pr.kvStore.CreateWatch(key)
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400135 }
Stephane Barbariee0a4c792019-01-16 11:26:29 -0500136
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400137 if !pr.isWatched {
Stephane Barbarie3cb01222019-01-16 17:15:56 -0500138 pr.isWatched = true
139
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400140 log.Debugw("setting-watch-routine", log.Fields{"key": key, "revision-hash": pr.GetHash()})
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400141
Stephane Barbariee0a4c792019-01-16 11:26:29 -0500142 // Start watching
143 go pr.startWatching()
144 }
145}
146
147func (pr *PersistedRevision) startWatching() {
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400148 log.Debugw("starting-watch", log.Fields{"key": pr.GetHash(), "watch": pr.GetName()})
Stephane Barbariee0a4c792019-01-16 11:26:29 -0500149
150StopWatchLoop:
151 for {
Stephane Barbariec92d1072019-06-07 16:21:49 -0400152 latestRev := pr.GetBranch().GetLatest()
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400153
Stephane Barbariee0a4c792019-01-16 11:26:29 -0500154 select {
155 case event, ok := <-pr.events:
156 if !ok {
Stephane Barbariec92d1072019-06-07 16:21:49 -0400157 log.Errorw("event-channel-failure: stopping watch loop", log.Fields{"key": latestRev.GetHash(), "watch": latestRev.GetName()})
Stephane Barbariee0a4c792019-01-16 11:26:29 -0500158 break StopWatchLoop
159 }
Stephane Barbariec92d1072019-06-07 16:21:49 -0400160 log.Debugw("received-event", log.Fields{"type": event.EventType, "watch": latestRev.GetName()})
Stephane Barbariee0a4c792019-01-16 11:26:29 -0500161
162 switch event.EventType {
163 case kvstore.DELETE:
Stephane Barbariec92d1072019-06-07 16:21:49 -0400164 log.Debugw("delete-from-memory", log.Fields{"key": latestRev.GetHash(), "watch": latestRev.GetName()})
Stephane Barbariee0a4c792019-01-16 11:26:29 -0500165 pr.Revision.Drop("", true)
166 break StopWatchLoop
167
168 case kvstore.PUT:
Stephane Barbariec92d1072019-06-07 16:21:49 -0400169 log.Debugw("update-in-memory", log.Fields{"key": latestRev.GetHash(), "watch": latestRev.GetName()})
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400170 if latestRev.getVersion() >= event.Version {
171 log.Debugw("skipping-matching-or-older-revision", log.Fields{
172 "watch": latestRev.GetName(),
173 "watch-version": event.Version,
174 "latest-version": latestRev.getVersion(),
175 })
176 continue
177 } else {
178 log.Debugw("watch-revision-is-newer", log.Fields{
179 "watch": latestRev.GetName(),
180 "watch-version": event.Version,
181 "latest-version": latestRev.getVersion(),
182 })
183 }
Stephane Barbariee0a4c792019-01-16 11:26:29 -0500184
Stephane Barbariec92d1072019-06-07 16:21:49 -0400185 data := reflect.New(reflect.TypeOf(latestRev.GetData()).Elem())
Stephane Barbariedf5479f2019-01-29 22:13:00 -0500186
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400187 if err := proto.Unmarshal(event.Value.([]byte), data.Interface().(proto.Message)); err != nil {
Stephane Barbariec92d1072019-06-07 16:21:49 -0400188 log.Errorw("failed-to-unmarshal-watch-data", log.Fields{"key": latestRev.GetHash(), "watch": latestRev.GetName(), "error": err})
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400189 } else {
Stephane Barbariec92d1072019-06-07 16:21:49 -0400190 log.Debugw("un-marshaled-watch-data", log.Fields{"key": latestRev.GetHash(), "watch": latestRev.GetName(), "data": data.Interface()})
Stephane Barbarie802aca42019-05-21 12:19:28 -0400191
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400192 var pathLock string
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400193 var blobs map[string]*kvstore.KVPair
194
195 // The watch reported new persistence data.
196 // Construct an object that will be used to update the memory
197 blobs = make(map[string]*kvstore.KVPair)
198 key, _ := kvstore.ToString(event.Key)
199 blobs[key] = &kvstore.KVPair{
200 Key: key,
201 Value: event.Value,
202 Session: "",
203 Lease: 0,
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400204 Version: event.Version,
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400205 }
206
Stephane Barbariec92d1072019-06-07 16:21:49 -0400207 if latestRev.GetNode().GetProxy() != nil {
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400208 //
209 // If a proxy exists for this revision, use it to lock access to the path
210 // and prevent simultaneous updates to the object in memory
211 //
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400212
213 //If the proxy already has a request in progress, then there is no need to process the watch
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400214 if latestRev.GetNode().GetProxy().GetOperation() != PROXY_NONE {
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400215 log.Debugw("operation-in-progress", log.Fields{
Stephane Barbariec92d1072019-06-07 16:21:49 -0400216 "key": latestRev.GetHash(),
217 "path": latestRev.GetNode().GetProxy().getFullPath(),
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400218 "operation": latestRev.GetNode().GetProxy().operation.String(),
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400219 })
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400220 continue
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400221 }
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400222
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400223 pathLock, _ = latestRev.GetNode().GetProxy().parseForControlledPath(latestRev.GetNode().GetProxy().getFullPath())
224
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400225 // Reserve the path to prevent others to modify while we reload from persistence
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400226 latestRev.GetNode().GetProxy().GetRoot().KvStore.Client.Reserve(pathLock+"_", uuid.New().String(), ReservationTTL)
227 latestRev.GetNode().GetProxy().SetOperation(PROXY_WATCH)
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400228
229 // Load changes and apply to memory
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400230 latestRev.LoadFromPersistence(context.Background(), latestRev.GetName(), "", blobs)
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400231
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400232 // Release path
233 latestRev.GetNode().GetProxy().GetRoot().KvStore.Client.ReleaseReservation(pathLock + "_")
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400234
235 } else {
Stephane Barbarie802aca42019-05-21 12:19:28 -0400236 // This block should be reached only if coming from a non-proxied request
Stephane Barbariec92d1072019-06-07 16:21:49 -0400237 log.Debugw("revision-with-no-proxy", log.Fields{"key": latestRev.GetHash(), "watch": latestRev.GetName()})
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400238
239 // Load changes and apply to memory
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400240 latestRev.LoadFromPersistence(context.Background(), latestRev.GetName(), "", blobs)
Stephane Barbariedf5479f2019-01-29 22:13:00 -0500241 }
Stephane Barbariee0a4c792019-01-16 11:26:29 -0500242 }
243
244 default:
Stephane Barbariec92d1072019-06-07 16:21:49 -0400245 log.Debugw("unhandled-event", log.Fields{"key": latestRev.GetHash(), "watch": latestRev.GetName(), "type": event.EventType})
Stephane Barbariee0a4c792019-01-16 11:26:29 -0500246 }
247 }
248 }
249
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400250 Watches().Cache.Delete(pr.GetName() + "-" + pr.GetHash())
Stephane Barbariee0a4c792019-01-16 11:26:29 -0500251
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400252 log.Debugw("exiting-watch", log.Fields{"key": pr.GetHash(), "watch": pr.GetName()})
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400253}
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400254
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500255// UpdateData modifies the information in the data model and saves it in the persistent storage
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400256func (pr *PersistedRevision) UpdateData(ctx context.Context, data interface{}, branch *Branch) Revision {
Stephane Barbariee0a4c792019-01-16 11:26:29 -0500257 log.Debugw("updating-persisted-data", log.Fields{"hash": pr.GetHash()})
258
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400259 newNPR := pr.Revision.UpdateData(ctx, data, branch)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400260
261 newPR := &PersistedRevision{
Stephane Barbariec92d1072019-06-07 16:21:49 -0400262 Revision: newNPR,
263 Compress: pr.Compress,
264 kvStore: pr.kvStore,
265 events: pr.events,
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400266 Version: pr.getVersion(),
Stephane Barbariec92d1072019-06-07 16:21:49 -0400267 isWatched: pr.isWatched,
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400268 }
269
270 if newPR.GetHash() != pr.GetHash() {
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400271 newPR.isStored = false
272 pr.Drop(branch.Txid, false)
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400273 pr.Drop(branch.Txid, false)
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400274 } else {
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400275 newPR.isStored = true
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400276 }
277
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400278 return newPR
279}
280
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500281// UpdateChildren modifies the children of a revision and of a specific component and saves it in the persistent storage
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400282func (pr *PersistedRevision) UpdateChildren(ctx context.Context, name string, children []Revision, branch *Branch) Revision {
Stephane Barbariee0a4c792019-01-16 11:26:29 -0500283 log.Debugw("updating-persisted-children", log.Fields{"hash": pr.GetHash()})
284
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400285 newNPR := pr.Revision.UpdateChildren(ctx, name, children, branch)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400286
287 newPR := &PersistedRevision{
Stephane Barbariec92d1072019-06-07 16:21:49 -0400288 Revision: newNPR,
289 Compress: pr.Compress,
290 kvStore: pr.kvStore,
291 events: pr.events,
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400292 Version: pr.getVersion(),
Stephane Barbariec92d1072019-06-07 16:21:49 -0400293 isWatched: pr.isWatched,
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400294 }
295
296 if newPR.GetHash() != pr.GetHash() {
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400297 newPR.isStored = false
298 pr.Drop(branch.Txid, false)
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400299 } else {
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400300 newPR.isStored = true
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400301 }
302
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400303 return newPR
304}
305
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500306// UpdateAllChildren modifies the children for all components of a revision and saves it in the peristent storage
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400307func (pr *PersistedRevision) UpdateAllChildren(children map[string][]Revision, branch *Branch) Revision {
Stephane Barbariee0a4c792019-01-16 11:26:29 -0500308 log.Debugw("updating-all-persisted-children", log.Fields{"hash": pr.GetHash()})
309
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400310 newNPR := pr.Revision.UpdateAllChildren(children, branch)
311
312 newPR := &PersistedRevision{
Stephane Barbariec92d1072019-06-07 16:21:49 -0400313 Revision: newNPR,
314 Compress: pr.Compress,
315 kvStore: pr.kvStore,
316 events: pr.events,
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400317 Version: pr.getVersion(),
Stephane Barbariec92d1072019-06-07 16:21:49 -0400318 isWatched: pr.isWatched,
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400319 }
320
321 if newPR.GetHash() != pr.GetHash() {
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400322 newPR.isStored = false
323 pr.Drop(branch.Txid, false)
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400324 } else {
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400325 newPR.isStored = true
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400326 }
327
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400328 return newPR
329}
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400330
331// Drop takes care of eliminating a revision hash that is no longer needed
332// and its associated config when required
333func (pr *PersistedRevision) Drop(txid string, includeConfig bool) {
Stephane Barbarief7fc1782019-03-28 22:33:41 -0400334 pr.Revision.Drop(txid, includeConfig)
335}
336
337// Drop takes care of eliminating a revision hash that is no longer needed
338// and its associated config when required
339func (pr *PersistedRevision) StorageDrop(txid string, includeConfig bool) {
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400340 log.Debugw("dropping-revision", log.Fields{"txid": txid, "hash": pr.GetHash(), "config-hash": pr.GetConfig().Hash})
Stephane Barbariee0a4c792019-01-16 11:26:29 -0500341
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500342 pr.mutex.Lock()
343 defer pr.mutex.Unlock()
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400344 if pr.kvStore != nil && txid == "" {
Stephane Barbarie3cb01222019-01-16 17:15:56 -0500345 if pr.isStored {
Stephane Barbarief7fc1782019-03-28 22:33:41 -0400346 if pr.isWatched {
347 pr.kvStore.DeleteWatch(pr.GetName(), pr.events)
348 pr.isWatched = false
Stephane Barbarie3cb01222019-01-16 17:15:56 -0500349 }
350
Stephane Barbarief7fc1782019-03-28 22:33:41 -0400351 if err := pr.kvStore.Delete(pr.GetName()); err != nil {
Stephane Barbarie3cb01222019-01-16 17:15:56 -0500352 log.Errorw("failed-to-remove-revision", log.Fields{"hash": pr.GetHash(), "error": err.Error()})
353 } else {
354 pr.isStored = false
355 }
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400356 }
357
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400358 } else {
359 if includeConfig {
Stephane Barbariee0a4c792019-01-16 11:26:29 -0500360 log.Debugw("attempted-to-remove-transacted-revision-config", log.Fields{"hash": pr.GetConfig().Hash, "txid": txid})
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400361 }
Stephane Barbariee0a4c792019-01-16 11:26:29 -0500362 log.Debugw("attempted-to-remove-transacted-revision", log.Fields{"hash": pr.GetHash(), "txid": txid})
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400363 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500364
365 pr.Revision.Drop(txid, includeConfig)
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400366}
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400367
368// verifyPersistedEntry validates if the provided data is available or not in memory and applies updates as required
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400369func (pr *PersistedRevision) verifyPersistedEntry(ctx context.Context, data interface{}, typeName string, keyName string,
370 keyValue string, txid string, version int64) (response Revision) {
Stephane Barbarie802aca42019-05-21 12:19:28 -0400371 // Parent which holds the current node entry
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400372 parent := pr.GetBranch().Node.GetRoot()
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400373
Stephane Barbarie802aca42019-05-21 12:19:28 -0400374 // Get a copy of the parent's children
375 children := make([]Revision, len(parent.GetBranch(NONE).Latest.GetChildren(typeName)))
376 copy(children, parent.GetBranch(NONE).Latest.GetChildren(typeName))
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400377
Stephane Barbarie802aca42019-05-21 12:19:28 -0400378 // Verify if a child with the provided key value can be found
379 if childIdx, childRev := pr.GetNode().findRevByKey(children, keyName, keyValue); childRev != nil {
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400380 // A child matching the provided key exists in memory
Stephane Barbarie802aca42019-05-21 12:19:28 -0400381 // Verify if the data differs from what was retrieved from persistence
Stephane Barbariec92d1072019-06-07 16:21:49 -0400382 // Also check if we are treating a newer revision of the data or not
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400383 if childRev.GetData().(proto.Message).String() != data.(proto.Message).String() && childRev.getVersion() < version {
Stephane Barbarie802aca42019-05-21 12:19:28 -0400384 log.Debugw("revision-data-is-different", log.Fields{
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400385 "key": childRev.GetHash(),
386 "name": childRev.GetName(),
387 "data": childRev.GetData(),
388 "version": childRev.getVersion(),
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400389 })
390
Stephane Barbarie802aca42019-05-21 12:19:28 -0400391 //
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400392 // Data has changed; replace the child entry and update the parent revision
Stephane Barbarie802aca42019-05-21 12:19:28 -0400393 //
394
395 // BEGIN Lock child -- prevent any incoming changes
396 childRev.GetBranch().LatestLock.Lock()
397
398 // Update child
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400399 updatedChildRev := childRev.UpdateData(ctx, data, childRev.GetBranch())
Stephane Barbarie802aca42019-05-21 12:19:28 -0400400
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400401 updatedChildRev.GetNode().SetProxy(childRev.GetNode().GetProxy())
402 updatedChildRev.SetupWatch(updatedChildRev.GetName())
Stephane Barbarie802aca42019-05-21 12:19:28 -0400403 updatedChildRev.SetLastUpdate()
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400404 updatedChildRev.(*PersistedRevision).setVersion(version)
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400405
Stephane Barbarie802aca42019-05-21 12:19:28 -0400406 // Update cache
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400407 GetRevCache().Set(updatedChildRev.GetName(), updatedChildRev)
Stephane Barbariec92d1072019-06-07 16:21:49 -0400408 childRev.Drop(txid, false)
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400409
Stephane Barbarie802aca42019-05-21 12:19:28 -0400410 childRev.GetBranch().LatestLock.Unlock()
411 // END lock child
412
413 // Update child entry
414 children[childIdx] = updatedChildRev
415
416 // BEGIN lock parent -- Update parent
417 parent.GetBranch(NONE).LatestLock.Lock()
418
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400419 updatedRev := parent.GetBranch(NONE).GetLatest().UpdateChildren(ctx, typeName, children, parent.GetBranch(NONE))
Stephane Barbarie802aca42019-05-21 12:19:28 -0400420 parent.GetBranch(NONE).Node.makeLatest(parent.GetBranch(NONE), updatedRev, nil)
421
422 parent.GetBranch(NONE).LatestLock.Unlock()
423 // END lock parent
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400424
425 // Drop the previous child revision
Stephane Barbarie802aca42019-05-21 12:19:28 -0400426 parent.GetBranch(NONE).Latest.ChildDrop(typeName, childRev.GetHash())
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400427
428 if updatedChildRev != nil {
429 log.Debugw("verify-persisted-entry--adding-child", log.Fields{
430 "key": updatedChildRev.GetHash(),
431 "name": updatedChildRev.GetName(),
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400432 "data": updatedChildRev.GetData(),
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400433 })
434 response = updatedChildRev
435 }
436 } else {
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400437 if childRev != nil {
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400438 log.Debugw("keeping-revision-data", log.Fields{
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400439 "key": childRev.GetHash(),
440 "name": childRev.GetName(),
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400441 "data": childRev.GetData(),
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400442 })
Stephane Barbarie802aca42019-05-21 12:19:28 -0400443
444 // Update timestamp to reflect when it was last read and to reset tracked timeout
445 childRev.SetLastUpdate()
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400446 if childRev.getVersion() < version {
447 childRev.(*PersistedRevision).setVersion(version)
448 }
449 GetRevCache().Set(childRev.GetName(), childRev)
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400450 response = childRev
451 }
452 }
Stephane Barbariec92d1072019-06-07 16:21:49 -0400453
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400454 } else {
455 // There is no available child with that key value.
456 // Create a new child and update the parent revision.
Stephane Barbarie802aca42019-05-21 12:19:28 -0400457 log.Debugw("no-such-revision-entry", log.Fields{
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400458 "key": keyValue,
459 "name": typeName,
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400460 "data": data,
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400461 })
462
Stephane Barbarie802aca42019-05-21 12:19:28 -0400463 // BEGIN child lock
464 pr.GetBranch().LatestLock.Lock()
465
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400466 // Construct a new child node with the retrieved persistence data
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400467 childRev = pr.GetBranch().Node.MakeNode(data, txid).Latest(txid)
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400468
469 // We need to start watching this entry for future changes
470 childRev.SetName(typeName + "/" + keyValue)
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400471 childRev.SetupWatch(childRev.GetName())
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400472 childRev.(*PersistedRevision).setVersion(version)
473
474 // Add entry to cache
475 GetRevCache().Set(childRev.GetName(), childRev)
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400476
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400477 pr.GetBranch().LatestLock.Unlock()
Stephane Barbarie802aca42019-05-21 12:19:28 -0400478 // END child lock
479
480 //
481 // Add the child to the parent revision
482 //
483
484 // BEGIN parent lock
485 parent.GetBranch(NONE).LatestLock.Lock()
486 children = append(children, childRev)
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400487 updatedRev := parent.GetBranch(NONE).GetLatest().UpdateChildren(ctx, typeName, children, parent.GetBranch(NONE))
Stephane Barbarie802aca42019-05-21 12:19:28 -0400488 updatedRev.GetNode().SetProxy(parent.GetBranch(NONE).Node.GetProxy())
Stephane Barbarie802aca42019-05-21 12:19:28 -0400489 parent.GetBranch(NONE).Node.makeLatest(parent.GetBranch(NONE), updatedRev, nil)
490 parent.GetBranch(NONE).LatestLock.Unlock()
491 // END parent lock
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400492
493 // Child entry is valid and can be included in the response object
494 if childRev != nil {
Stephane Barbarie802aca42019-05-21 12:19:28 -0400495 log.Debugw("adding-revision-to-response", log.Fields{
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400496 "key": childRev.GetHash(),
497 "name": childRev.GetName(),
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400498 "data": childRev.GetData(),
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400499 })
500 response = childRev
501 }
502 }
503
504 return response
505}
506
507// LoadFromPersistence retrieves data from kv store at the specified location and refreshes the memory
508// by adding missing entries, updating changed entries and ignoring unchanged ones
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400509func (pr *PersistedRevision) LoadFromPersistence(ctx context.Context, path string, txid string, blobs map[string]*kvstore.KVPair) []Revision {
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400510 pr.mutex.Lock()
511 defer pr.mutex.Unlock()
512
513 log.Debugw("loading-from-persistence", log.Fields{"path": path, "txid": txid})
514
515 var response []Revision
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400516
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400517 for strings.HasPrefix(path, "/") {
518 path = path[1:]
519 }
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400520
521 if pr.kvStore != nil && path != "" {
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400522 if blobs == nil || len(blobs) == 0 {
523 log.Debugw("retrieve-from-kv", log.Fields{"path": path, "txid": txid})
524 blobs, _ = pr.kvStore.List(path)
525 }
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400526
527 partition := strings.SplitN(path, "/", 2)
528 name := partition[0]
529
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400530 var nodeType interface{}
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400531 if len(partition) < 2 {
532 path = ""
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400533 nodeType = pr.GetBranch().Node.Type
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400534 } else {
535 path = partition[1]
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400536 nodeType = pr.GetBranch().Node.GetRoot().Type
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400537 }
538
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400539 field := ChildrenFields(nodeType)[name]
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400540
541 if field != nil && field.IsContainer {
Stephane Barbarie802aca42019-05-21 12:19:28 -0400542 log.Debugw("parsing-data-blobs", log.Fields{
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400543 "path": path,
544 "name": name,
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400545 "size": len(blobs),
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400546 })
547
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400548 for _, blob := range blobs {
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400549 output := blob.Value.([]byte)
550
551 data := reflect.New(field.ClassType.Elem())
552
553 if err := proto.Unmarshal(output, data.Interface().(proto.Message)); err != nil {
Stephane Barbarie802aca42019-05-21 12:19:28 -0400554 log.Errorw("failed-to-unmarshal", log.Fields{
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400555 "path": path,
556 "txid": txid,
557 "error": err,
558 })
559 } else if path == "" {
560 if field.Key != "" {
Stephane Barbarie802aca42019-05-21 12:19:28 -0400561 log.Debugw("no-path-with-container-key", log.Fields{
562 "path": path,
563 "txid": txid,
564 "data": data.Interface(),
565 })
566
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400567 // Retrieve the key identifier value from the data structure
568 // based on the field's key attribute
569 _, key := GetAttributeValue(data.Interface(), field.Key, 0)
570
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400571 if entry := pr.verifyPersistedEntry(ctx, data.Interface(), name, field.Key, key.String(), txid, blob.Version); entry != nil {
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400572 response = append(response, entry)
573 }
Stephane Barbarie802aca42019-05-21 12:19:28 -0400574 } else {
575 log.Debugw("path-with-no-container-key", log.Fields{
576 "path": path,
577 "txid": txid,
578 "data": data.Interface(),
579 })
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400580 }
581
582 } else if field.Key != "" {
Stephane Barbarie802aca42019-05-21 12:19:28 -0400583 log.Debugw("path-with-container-key", log.Fields{
584 "path": path,
585 "txid": txid,
586 "data": data.Interface(),
587 })
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400588 // The request is for a specific entry/id
589 partition := strings.SplitN(path, "/", 2)
590 key := partition[0]
591 if len(partition) < 2 {
592 path = ""
593 } else {
594 path = partition[1]
595 }
596 keyValue := field.KeyFromStr(key)
597
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400598 if entry := pr.verifyPersistedEntry(ctx, data.Interface(), name, field.Key, keyValue.(string), txid, blob.Version); entry != nil {
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400599 response = append(response, entry)
600 }
601 }
602 }
603
Stephane Barbarie802aca42019-05-21 12:19:28 -0400604 log.Debugw("no-more-data-blobs", log.Fields{"path": path, "name": name})
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400605 } else {
Stephane Barbarie802aca42019-05-21 12:19:28 -0400606 log.Debugw("cannot-process-field", log.Fields{
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400607 "type": pr.GetBranch().Node.Type,
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400608 "name": name,
609 })
610 }
611 }
612
613 return response
Kent Hagerman0ab4cb22019-04-24 13:13:35 -0400614}