github.com/ddev/ddev@v1.23.2-0.20240519125000-d824ffe36ff3/pkg/amplitude/storages/delayed_transmission_event_storage.go (about) 1 package storages 2 3 import ( 4 "encoding/gob" 5 "os" 6 "sync" 7 "time" 8 9 "github.com/amplitude/analytics-go/amplitude/types" 10 ) 11 12 // NewDelayedTransmissionEventStorage initializes a new EventStorage of type delayedTransmissionEventStorage. 13 func NewDelayedTransmissionEventStorage( 14 logger types.Logger, 15 capacity int, 16 interval time.Duration, 17 fileName string, 18 ) types.EventStorage { 19 return &delayedTransmissionEventStorage{ 20 logger: logger, 21 fileName: fileName, 22 loaded: false, 23 capacity: capacity, 24 interval: interval, 25 } 26 } 27 28 type delayedTransmissionEventStorage struct { 29 logger types.Logger 30 fileName string 31 loaded bool 32 capacity int 33 interval time.Duration 34 cache eventCache 35 36 mu sync.RWMutex 37 } 38 39 // eventCache is the structure used for the cache file. 40 type eventCache struct { 41 LastSubmittedAt time.Time 42 Events []*types.StorageEvent 43 } 44 45 // PushNew writes a new event to the cache. 46 func (s *delayedTransmissionEventStorage) PushNew(event *types.StorageEvent) { 47 s.push(false, event) 48 } 49 50 // ReturnBack is used to return back previously pulled events to the cache. 51 func (s *delayedTransmissionEventStorage) ReturnBack(events ...*types.StorageEvent) { 52 s.push(true, events...) 53 } 54 55 // push prepends or appends events to the cache. 56 func (s *delayedTransmissionEventStorage) push(prepend bool, events ...*types.StorageEvent) { 57 if len(events) == 0 { 58 return 59 } 60 61 s.mu.Lock() 62 defer s.mu.Unlock() 63 64 err := s.readCache() 65 if err != nil { 66 s.logger.Errorf("Error '%s', while reading event cache.", err) 67 } 68 69 prependIndex := 0 70 71 for _, event := range events { 72 s.addNonRetriedEvent(event, prepend, &prependIndex) 73 } 74 75 err = s.writeCache() 76 if err != nil { 77 s.logger.Errorf("Error '%s', while writing event cache.", err) 78 } 79 80 s.logger.Debugf("Pushed %d events to the cache.", len(events)) 81 } 82 83 // Pull returns a chunk of events and removes returned events from the cache. 84 func (s *delayedTransmissionEventStorage) Pull(count int, before time.Time) []*types.StorageEvent { 85 s.mu.Lock() 86 defer s.mu.Unlock() 87 88 err := s.readCache() 89 if err != nil { 90 s.logger.Errorf("Error '%s', while reading event cache.", err) 91 } 92 93 // Early return if capacity and interval hasn't reached. 94 if !s.cache.LastSubmittedAt.Add(s.interval).Before(before) && !(len(s.cache.Events) >= s.capacity) { 95 return make([]*types.StorageEvent, 0) 96 } 97 98 defer func() { 99 err = s.writeCache() 100 if err != nil { 101 s.logger.Errorf("Error '%s', while writing event cache.", err) 102 } 103 }() 104 105 s.cache.LastSubmittedAt = time.Now() 106 107 if len(s.cache.Events) >= count { 108 events := make([]*types.StorageEvent, count) 109 copy(events, s.cache.Events) 110 copy(s.cache.Events, s.cache.Events[count:]) 111 112 // Remove copied items from cache. 113 s.cache.Events = s.cache.Events[:len(s.cache.Events)-count] 114 115 s.logger.Debugf("Pulled %d events from the cache.", len(events)) 116 117 return events 118 } 119 120 events := make([]*types.StorageEvent, len(s.cache.Events), count) 121 copy(events, s.cache.Events) 122 123 // Clear the cache 124 s.cache.Events = s.cache.Events[:0] 125 126 s.logger.Debugf("Pulled %d events from the cache.", len(events)) 127 128 return events 129 } 130 131 // Count returns the number of events ready for transmission. 132 func (s *delayedTransmissionEventStorage) Count(before time.Time) int { 133 s.mu.RLock() 134 defer s.mu.RUnlock() 135 136 err := s.readCache() 137 if err != nil { 138 s.logger.Errorf("Error '%s', while reading event cache.", err) 139 } 140 141 if s.cache.LastSubmittedAt.Add(s.interval).Before(before) || len(s.cache.Events) >= s.capacity { 142 return len(s.cache.Events) 143 } 144 145 return 0 146 } 147 148 // addNonRetriedEvent adds the events to the internal memory cache. 149 func (s *delayedTransmissionEventStorage) addNonRetriedEvent(event *types.StorageEvent, prepend bool, prependIndex *int) { 150 if prepend { 151 s.cache.Events = append(s.cache.Events, nil) 152 copy(s.cache.Events[*prependIndex+1:], s.cache.Events[*prependIndex:]) 153 s.cache.Events[*prependIndex] = event 154 *prependIndex++ 155 } else { 156 s.cache.Events = append(s.cache.Events, event) 157 } 158 } 159 160 // readCache reads the events from the cache file. 161 func (s *delayedTransmissionEventStorage) readCache() error { 162 // The cache is read once per run time, early exit if already loaded. 163 if s.loaded { 164 return nil 165 } 166 167 file, err := os.Open(s.fileName) 168 // If the file does not exist, early exit. 169 if err != nil { 170 s.loaded = true 171 172 return nil 173 } 174 175 defer file.Close() 176 177 stat, err := file.Stat() 178 if err != nil || stat.Size() == 0 { 179 s.loaded = true 180 s.logger.Infof("Event cache is empty") 181 182 return nil 183 } 184 185 decoder := gob.NewDecoder(file) 186 err = decoder.Decode(&s.cache) 187 188 // If the file was properly read, mark the cache as loaded. 189 if err == nil { 190 s.loaded = true 191 192 s.logger.Debugf("Read %d events from the cache.", len(s.cache.Events)) 193 } 194 195 return err 196 } 197 198 // writeCache writes the events to the cache file. 199 func (s *delayedTransmissionEventStorage) writeCache() error { 200 file, err := os.Create(s.fileName) 201 if err != nil { 202 return err 203 } 204 205 defer file.Close() 206 207 encoder := gob.NewEncoder(file) 208 err = encoder.Encode(&s.cache) 209 210 if err == nil { 211 s.logger.Debugf("Wrote %d events to the cache.", len(s.cache.Events)) 212 } 213 214 return err 215 }