github.com/braveheart12/just@v0.8.7/ledger/recentstorage/recentstorage_concrete.go (about) 1 /* 2 * Copyright 2019 Insolar 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 */ 16 17 package recentstorage 18 19 import ( 20 "context" 21 "sync" 22 23 "github.com/insolar/insolar/core" 24 "github.com/insolar/insolar/instrumentation/inslogger" 25 "github.com/insolar/insolar/instrumentation/insmetrics" 26 "go.opencensus.io/stats" 27 ) 28 29 // RecentStorageProvider provides a recent storage for jet 30 type RecentStorageProvider struct { //nolint: golint 31 indexStorages map[core.RecordID]*RecentIndexStorageConcrete 32 pendingStorages map[core.RecordID]*PendingStorageConcrete 33 34 indexLock sync.Mutex 35 pendingLock sync.Mutex 36 37 DefaultTTL int 38 } 39 40 // NewRecentStorageProvider creates new provider 41 func NewRecentStorageProvider(defaultTTL int) *RecentStorageProvider { 42 return &RecentStorageProvider{ 43 DefaultTTL: defaultTTL, 44 indexStorages: map[core.RecordID]*RecentIndexStorageConcrete{}, 45 pendingStorages: map[core.RecordID]*PendingStorageConcrete{}, 46 } 47 } 48 49 // GetIndexStorage returns a recent indexes for a specific jet 50 func (p *RecentStorageProvider) GetIndexStorage(ctx context.Context, jetID core.RecordID) RecentIndexStorage { 51 p.indexLock.Lock() 52 defer p.indexLock.Unlock() 53 54 storage, ok := p.indexStorages[jetID] 55 if !ok { 56 storage = NewRecentIndexStorage(jetID, p.DefaultTTL) 57 p.indexStorages[jetID] = storage 58 } 59 return storage 60 } 61 62 // GetPendingStorage returns pendings for a specific jet 63 func (p *RecentStorageProvider) GetPendingStorage(ctx context.Context, jetID core.RecordID) PendingStorage { 64 p.pendingLock.Lock() 65 defer p.pendingLock.Unlock() 66 67 storage, ok := p.pendingStorages[jetID] 68 if !ok { 69 storage = NewPendingStorage(jetID) 70 p.pendingStorages[jetID] = storage 71 } 72 return storage 73 } 74 75 // Count returns count of pendings in all storages 76 func (p *RecentStorageProvider) Count() int { 77 p.pendingLock.Lock() 78 defer p.pendingLock.Unlock() 79 80 count := 0 81 for _, storage := range p.pendingStorages { 82 count += len(storage.requests) 83 } 84 85 return count 86 } 87 88 // CloneIndexStorage clones indexes from one jet to another one 89 func (p *RecentStorageProvider) CloneIndexStorage(ctx context.Context, fromJetID, toJetID core.RecordID) { 90 p.indexLock.Lock() 91 defer p.indexLock.Unlock() 92 93 fromStorage, ok := p.indexStorages[fromJetID] 94 if !ok { 95 return 96 } 97 toStorage := &RecentIndexStorageConcrete{ 98 jetID: toJetID, 99 indexes: map[core.RecordID]recentObjectMeta{}, 100 DefaultTTL: p.DefaultTTL, 101 } 102 for k, v := range fromStorage.indexes { 103 clone := v 104 toStorage.indexes[k] = clone 105 } 106 p.indexStorages[toJetID] = toStorage 107 } 108 109 // ClonePendingStorage clones pending requests from one jet to another one 110 func (p *RecentStorageProvider) ClonePendingStorage(ctx context.Context, fromJetID, toJetID core.RecordID) { 111 p.pendingLock.Lock() 112 fromStorage, ok := p.pendingStorages[fromJetID] 113 p.pendingLock.Unlock() 114 115 if !ok { 116 return 117 } 118 119 fromStorage.lock.RLock() 120 toStorage := &PendingStorageConcrete{ 121 jetID: toJetID, 122 requests: map[core.RecordID]*lockedPendingObjectContext{}, 123 } 124 for objID, pendingContext := range fromStorage.requests { 125 if len(pendingContext.Context.Requests) == 0 { 126 continue 127 } 128 129 pendingContext.lock.Lock() 130 131 clone := PendingObjectContext{ 132 Active: pendingContext.Context.Active, 133 Requests: []core.RecordID{}, 134 } 135 136 clone.Requests = append(clone.Requests, pendingContext.Context.Requests...) 137 toStorage.requests[objID] = &lockedPendingObjectContext{Context: &clone} 138 139 pendingContext.lock.Unlock() 140 } 141 fromStorage.lock.RUnlock() 142 143 p.pendingLock.Lock() 144 p.pendingStorages[toJetID] = toStorage 145 p.pendingLock.Unlock() 146 } 147 148 // DecreaseIndexesTTL decrease ttl of all indexes in all storages 149 // If storage contains indexes with zero ttl, they are removed from storage and returned to a caller 150 // If there are no indexes with ttl more then 0, storage is removed 151 func (p *RecentStorageProvider) DecreaseIndexesTTL(ctx context.Context) map[core.RecordID][]core.RecordID { 152 p.indexLock.Lock() 153 defer p.indexLock.Unlock() 154 155 resMapLock := sync.Mutex{} 156 resMap := map[core.RecordID][]core.RecordID{} 157 wg := sync.WaitGroup{} 158 wg.Add(len(p.indexStorages)) 159 160 for jetID, storage := range p.indexStorages { 161 go func(jetID core.RecordID, s *RecentIndexStorageConcrete) { 162 res := s.DecreaseIndexTTL(ctx) 163 164 if len(res) > 0 { 165 resMapLock.Lock() 166 resMap[jetID] = res 167 resMapLock.Unlock() 168 } 169 170 wg.Done() 171 }(jetID, storage) 172 } 173 174 wg.Wait() 175 176 for jetID, storage := range p.indexStorages { 177 if len(storage.indexes) == 0 { 178 delete(p.indexStorages, jetID) 179 } 180 } 181 182 return resMap 183 } 184 185 // RemovePendingStorage removes pending requests for a specific jet from provider 186 // If there is a reference to RecentIndexStorage somewhere, it won't be affected 187 func (p *RecentStorageProvider) RemovePendingStorage(ctx context.Context, id core.RecordID) { 188 p.pendingLock.Lock() 189 defer p.pendingLock.Unlock() 190 191 if storage, ok := p.pendingStorages[id]; ok { 192 193 ctx = insmetrics.InsertTag(ctx, tagJet, storage.jetID.DebugString()) 194 stats.Record(ctx, 195 statRecentStoragePendingsRemoved.M(int64(len(storage.requests))), 196 ) 197 198 delete(p.pendingStorages, id) 199 } 200 } 201 202 // RecentIndexStorageConcrete is an implementation of RecentIndexStorage interface 203 // This is a in-memory cache for indexes` ids 204 type RecentIndexStorageConcrete struct { 205 jetID core.RecordID 206 indexes map[core.RecordID]recentObjectMeta 207 lock sync.Mutex 208 DefaultTTL int 209 } 210 211 type recentObjectMeta struct { 212 ttl int 213 } 214 215 // NewRecentIndexStorage creates new *RecentIndexStorage 216 func NewRecentIndexStorage(jetID core.RecordID, defaultTTL int) *RecentIndexStorageConcrete { 217 return &RecentIndexStorageConcrete{jetID: jetID, DefaultTTL: defaultTTL, indexes: map[core.RecordID]recentObjectMeta{}} 218 } 219 220 // AddObject adds index's id to an in-memory cache and sets DefaultTTL for it 221 func (r *RecentIndexStorageConcrete) AddObject(ctx context.Context, id core.RecordID) { 222 r.AddObjectWithTLL(ctx, id, r.DefaultTTL) 223 } 224 225 // AddObjectWithTLL adds index's id to an in-memory cache with provided ttl 226 func (r *RecentIndexStorageConcrete) AddObjectWithTLL(ctx context.Context, id core.RecordID, ttl int) { 227 r.lock.Lock() 228 defer r.lock.Unlock() 229 230 if ttl < 0 { 231 inslogger.FromContext(ctx).Error("below zero ttl happened") 232 panic("below zero ttl happened") 233 } 234 235 r.indexes[id] = recentObjectMeta{ttl: ttl} 236 237 ctx = insmetrics.InsertTag(ctx, tagJet, r.jetID.DebugString()) 238 stats.Record(ctx, statRecentStorageObjectsAdded.M(1)) 239 } 240 241 // GetObjects returns deep copy of indexes' ids 242 func (r *RecentIndexStorageConcrete) GetObjects() map[core.RecordID]int { 243 r.lock.Lock() 244 defer r.lock.Unlock() 245 246 targetMap := make(map[core.RecordID]int, len(r.indexes)) 247 for key, value := range r.indexes { 248 targetMap[key] = value.ttl 249 } 250 251 return targetMap 252 } 253 254 // FilterNotExistWithLock filters candidates from params 255 // found indexes, which aren't removed from cache 256 // and the passes it to lockedFn 257 func (r *RecentIndexStorageConcrete) FilterNotExistWithLock( 258 ctx context.Context, 259 candidates []core.RecordID, 260 lockedFn func(filtered []core.RecordID), 261 ) { 262 r.lock.Lock() 263 markedCandidates := r.markForDelete(candidates) 264 lockedFn(markedCandidates) 265 r.lock.Unlock() 266 } 267 268 func (r *RecentIndexStorageConcrete) markForDelete(candidates []core.RecordID) []core.RecordID { 269 result := make([]core.RecordID, 0, len(candidates)) 270 271 for _, c := range candidates { 272 _, exists := r.indexes[c] 273 if !exists { 274 result = append(result, c) 275 } 276 } 277 return result 278 } 279 280 // DecreaseIndexTTL decreases ttls and remove indexes with 0 ttl 281 // Removed indexes will be returned as a functon's result 282 func (r *RecentIndexStorageConcrete) DecreaseIndexTTL(ctx context.Context) []core.RecordID { 283 r.lock.Lock() 284 defer r.lock.Unlock() 285 286 var clearedObjects []core.RecordID 287 for key, value := range r.indexes { 288 value.ttl-- 289 if value.ttl <= 0 { 290 clearedObjects = append(clearedObjects, key) 291 delete(r.indexes, key) 292 continue 293 } 294 r.indexes[key] = value 295 } 296 return clearedObjects 297 } 298 299 // PendingStorageConcrete contains indexes of unclosed requests (pendings) for a specific object id 300 type PendingStorageConcrete struct { 301 lock sync.RWMutex 302 303 jetID core.RecordID 304 305 requests map[core.RecordID]*lockedPendingObjectContext 306 } 307 308 // PendingObjectContext contains a list of requests for an object 309 // Also it contains a boolean-flag for determination object's status 310 // If It's false, current LME, when it gets a hot-data, needs to send 311 // notifications about forgotten requests 312 type PendingObjectContext struct { 313 Active bool 314 Requests []core.RecordID 315 } 316 317 type lockedPendingObjectContext struct { 318 Context *PendingObjectContext 319 lock sync.RWMutex 320 } 321 322 // NewPendingStorage creates *PendingStorage 323 func NewPendingStorage(jetID core.RecordID) *PendingStorageConcrete { 324 return &PendingStorageConcrete{ 325 jetID: jetID, 326 requests: map[core.RecordID]*lockedPendingObjectContext{}, 327 } 328 } 329 330 // AddPendingRequest adds an id of pending request to memory 331 // The id stores in a collection ids of a specific object 332 func (r *PendingStorageConcrete) AddPendingRequest(ctx context.Context, obj, req core.RecordID) { 333 r.lock.Lock() 334 defer r.lock.Unlock() 335 336 var objectContext *lockedPendingObjectContext 337 var ok bool 338 if objectContext, ok = r.requests[obj]; !ok { 339 objectContext = &lockedPendingObjectContext{ 340 lock: sync.RWMutex{}, 341 Context: &PendingObjectContext{ 342 Active: true, 343 Requests: []core.RecordID{}, 344 }, 345 } 346 r.requests[obj] = objectContext 347 } 348 349 objectContext.lock.Lock() 350 defer objectContext.lock.Unlock() 351 352 objectContext.Context.Requests = append(objectContext.Context.Requests, req) 353 354 ctx = insmetrics.InsertTag(ctx, tagJet, r.jetID.DebugString()) 355 stats.Record(ctx, statRecentStoragePendingsAdded.M(1)) 356 } 357 358 // SetContextToObject add a context to a provided object 359 func (r *PendingStorageConcrete) SetContextToObject(ctx context.Context, obj core.RecordID, objContext PendingObjectContext) { 360 r.lock.Lock() 361 defer r.lock.Unlock() 362 363 r.requests[obj] = &lockedPendingObjectContext{ 364 Context: &objContext, 365 } 366 ctx = insmetrics.InsertTag(ctx, tagJet, r.jetID.DebugString()) 367 stats.Record(ctx, statRecentStoragePendingsAdded.M(int64(len(objContext.Requests)))) 368 } 369 370 // GetRequests returns a deep-copy of requests collections 371 func (r *PendingStorageConcrete) GetRequests() map[core.RecordID]PendingObjectContext { 372 r.lock.RLock() 373 defer r.lock.RUnlock() 374 375 requestsClone := map[core.RecordID]PendingObjectContext{} 376 for objID, objContext := range r.requests { 377 objContext.lock.RLock() 378 379 objectClone := PendingObjectContext{ 380 Active: objContext.Context.Active, 381 Requests: []core.RecordID{}, 382 } 383 objectClone.Requests = append(objectClone.Requests, objContext.Context.Requests...) 384 requestsClone[objID] = objectClone 385 386 objContext.lock.RUnlock() 387 } 388 389 return requestsClone 390 } 391 392 // GetRequestsForObject returns a deep-copy of requests collection for a specific object 393 func (r *PendingStorageConcrete) GetRequestsForObject(obj core.RecordID) []core.RecordID { 394 r.lock.RLock() 395 defer r.lock.RUnlock() 396 397 forObject, ok := r.requests[obj] 398 if !ok { 399 return nil 400 } 401 402 forObject.lock.RLock() 403 defer forObject.lock.RUnlock() 404 405 results := make([]core.RecordID, 0, len(forObject.Context.Requests)) 406 results = append(results, forObject.Context.Requests...) 407 408 return results 409 } 410 411 // RemovePendingRequest removes a request on object from cache 412 func (r *PendingStorageConcrete) RemovePendingRequest(ctx context.Context, obj, req core.RecordID) { 413 r.lock.RLock() 414 defer r.lock.RUnlock() 415 416 objContext, ok := r.requests[obj] 417 if !ok { 418 return 419 } 420 421 objContext.lock.Lock() 422 defer objContext.lock.Unlock() 423 424 if len(objContext.Context.Requests) == 0 { 425 return 426 } 427 428 firstRequest := objContext.Context.Requests[0] 429 if firstRequest.Pulse() == req.Pulse() { 430 objContext.Context.Active = true 431 } 432 433 index := -1 434 for internalIndex, objReq := range objContext.Context.Requests { 435 if objReq == req { 436 index = internalIndex 437 break 438 } 439 } 440 441 if index == -1 { 442 return 443 } 444 445 if len(objContext.Context.Requests) == 1 { 446 objContext.Context.Requests = []core.RecordID{} 447 return 448 } 449 450 objContext.Context.Requests = append(objContext.Context.Requests[:index], objContext.Context.Requests[index+1:]...) 451 452 ctx = insmetrics.InsertTag(ctx, tagJet, r.jetID.DebugString()) 453 stats.Record(ctx, statRecentStoragePendingsRemoved.M(1)) 454 }