github.com/inspektor-gadget/inspektor-gadget@v0.28.1/pkg/snapshotcombiner/snapshotcombiner.go (about) 1 // Copyright 2022 The Inspektor Gadget authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package snapshotcombiner 16 17 import ( 18 "sync" 19 "time" 20 ) 21 22 // Stats contains the status of the SnapshotCombiner when calling GetSnapshots() 23 type Stats struct { 24 Epochs int // Number of calls to GetSnapshots() 25 CurrentSnapshots int // Number of wrappedSnapshots that have been updated since the previous call to GetSnapshots() 26 ExpiredSnapshots int // Number of wrappedSnapshots that have a ttl of 0 27 TotalSnapshots int // Number of wrappedSnapshots known 28 } 29 30 type snapshotWrapper[T any] struct { 31 snapshot []*T 32 ttl int 33 count int 34 lastUpdate time.Time 35 } 36 37 type SnapshotCombiner[T any] struct { 38 lock sync.Mutex 39 defaultTTL int 40 wrappedSnapshots map[string]*snapshotWrapper[T] 41 epoch int 42 } 43 44 // NewSnapshotCombiner creates a new wrappedSnapshots combiner that stores structs of type T using a key. Each key is 45 // given a time-to-live (ttl) and only valid for ttl calls of GetSnapshots(). Whenever a key is refreshed using 46 // AddSnapshot(), the ttl will be reset to the initial value. 47 func NewSnapshotCombiner[T any](ttl int) *SnapshotCombiner[T] { 48 return &SnapshotCombiner[T]{ 49 defaultTTL: ttl, 50 wrappedSnapshots: make(map[string]*snapshotWrapper[T]), 51 } 52 } 53 54 // AddSnapshot adds the given snapshot to the given key (e.g. a node name) and set its ttl to the defaultTTL of the 55 // SnapshotCombiner 56 func (sc *SnapshotCombiner[T]) AddSnapshot(key string, snapshot []*T) { 57 now := time.Now() 58 59 sc.lock.Lock() 60 defer sc.lock.Unlock() 61 62 if entry, ok := sc.wrappedSnapshots[key]; ok { 63 entry.snapshot = snapshot 64 entry.ttl = sc.defaultTTL 65 entry.count++ 66 entry.lastUpdate = now 67 return 68 } 69 sc.wrappedSnapshots[key] = &snapshotWrapper[T]{ 70 snapshot: snapshot, 71 ttl: sc.defaultTTL, 72 count: 1, 73 lastUpdate: now, 74 } 75 } 76 77 // GetSnapshots combines all stored wrappedSnapshots from all keys and decreases each keys defaultTTL by one. 78 // If the ttl of an entry is less than zero, it will not be returned anymore. 79 func (sc *SnapshotCombiner[T]) GetSnapshots() ([]*T, Stats) { 80 sc.lock.Lock() 81 defer sc.lock.Unlock() 82 83 // increase epoch 84 sc.epoch++ 85 86 stats := Stats{ 87 Epochs: sc.epoch, 88 } 89 90 result := make([]*T, 0, len(sc.wrappedSnapshots)) 91 for _, wrapper := range sc.wrappedSnapshots { 92 if wrapper.ttl == sc.defaultTTL { 93 stats.CurrentSnapshots++ 94 } 95 if wrapper.ttl > 0 { 96 result = append(result, wrapper.snapshot...) 97 wrapper.ttl-- 98 } else { 99 stats.ExpiredSnapshots++ 100 } 101 } 102 103 stats.TotalSnapshots = len(sc.wrappedSnapshots) 104 105 return result, stats 106 }