5
5
package eventstorage
6
6
7
7
import (
8
+ "errors"
8
9
"os"
9
10
"path/filepath"
10
11
"sync"
@@ -34,9 +35,14 @@ type StorageManager struct {
34
35
storage * Storage
35
36
rw * ShardedReadWriter
36
37
38
+ // mu guards db, storage, and rw swaps.
39
+ mu sync.RWMutex
37
40
// subscriberPosMu protects the subscriber file from concurrent RW.
38
41
subscriberPosMu sync.Mutex
39
42
43
+ // dropLoopCh acts as a mutex to ensure that there is only 1 active RunDropLoop per StorageManager,
44
+ // as it is possible that 2 separate RunDropLoop are created by 2 TBS processors during a hot reload.
45
+ dropLoopCh chan struct {}
40
46
// gcLoopCh acts as a mutex to ensure only 1 gc loop is running per StorageManager.
41
47
// as it is possible that 2 separate RunGCLoop are created by 2 TBS processors during a hot reload.
42
48
gcLoopCh chan struct {}
@@ -46,6 +52,7 @@ type StorageManager struct {
46
52
func NewStorageManager (storageDir string ) (* StorageManager , error ) {
47
53
sm := & StorageManager {
48
54
storageDir : storageDir ,
55
+ dropLoopCh : make (chan struct {}, 1 ),
49
56
gcLoopCh : make (chan struct {}, 1 ),
50
57
logger : logp .NewLogger (logs .Sampling ),
51
58
}
@@ -107,16 +114,131 @@ func (s *StorageManager) runValueLogGC(discardRatio float64) error {
107
114
return s .db .RunValueLogGC (discardRatio )
108
115
}
109
116
117
+ // RunDropLoop runs a loop that detects if storage limit has been exceeded for at least ttl.
118
+ // If so, it drops and recreates the underlying badger DB.
119
+ // The loop stops when it receives from stopping.
120
+ func (s * StorageManager ) RunDropLoop (stopping <- chan struct {}, ttl time.Duration , storageLimitInBytes uint64 ) error {
121
+ select {
122
+ case <- stopping :
123
+ return nil
124
+ case s .dropLoopCh <- struct {}{}:
125
+ }
126
+ defer func () {
127
+ <- s .dropLoopCh
128
+ }()
129
+
130
+ if storageLimitInBytes == 0 {
131
+ <- stopping
132
+ return nil
133
+ }
134
+
135
+ timer := time .NewTicker (min (time .Minute , ttl )) // Eval db size every minute as badger reports them with 1m lag, but use min to facilitate testing
136
+ defer timer .Stop ()
137
+ var firstExceeded time.Time
138
+ for {
139
+ select {
140
+ case <- stopping :
141
+ return nil
142
+ case <- timer .C :
143
+ lsm , vlog := s .Size ()
144
+ if uint64 (lsm + vlog ) >= storageLimitInBytes { //FIXME: Add a bit of buffer? Is s.storage.pendingSize reliable enough?
145
+ now := time .Now ()
146
+ if firstExceeded .IsZero () {
147
+ firstExceeded = now
148
+ }
149
+ if now .Sub (firstExceeded ) >= ttl {
150
+ s .logger .Warnf ("badger db size has exceeded storage limit for over TTL, please consider increasing sampling.tail.storage_size; dropping and recreating badger db to recover" )
151
+ s .DropAndRecreate ()
152
+ s .logger .Info ("badger db dropped and recreated" )
153
+ }
154
+ } else {
155
+ firstExceeded = time.Time {}
156
+ }
157
+ }
158
+ }
159
+ }
160
+
110
161
func (s * StorageManager ) Close () error {
162
+ s .mu .RLock ()
163
+ defer s .mu .RUnlock ()
111
164
s .rw .Close ()
112
165
return s .db .Close ()
113
166
}
114
167
168
+ // Reset initializes db, storage, and rw.
169
+ func (s * StorageManager ) Reset () error {
170
+ db , err := OpenBadger (s .storageDir , - 1 )
171
+ if err != nil {
172
+ return err
173
+ }
174
+ s .db = db
175
+ s .storage = New (db , ProtobufCodec {})
176
+ s .rw = s .storage .NewShardedReadWriter ()
177
+ return nil
178
+ }
179
+
115
180
// Size returns the db size
181
+ //
182
+ // Caller should either be main Run loop or should be holding RLock already
116
183
func (s * StorageManager ) Size () (lsm , vlog int64 ) {
117
184
return s .db .Size ()
118
185
}
119
186
187
+ func (s * StorageManager ) runValueLogGC (discardRatio float64 ) error {
188
+ s .mu .RLock ()
189
+ defer s .mu .RUnlock ()
190
+ return s .db .RunValueLogGC (discardRatio )
191
+ }
192
+
193
+ // DropAndRecreate deletes the underlying badger DB at a file system level, and replaces it with a new badger DB.
194
+ func (s * StorageManager ) DropAndRecreate () {
195
+ s .mu .Lock ()
196
+ s .rw .Close ()
197
+ err := s .db .Close ()
198
+ if err != nil {
199
+ s .logger .With (logp .Error (err )).Error ("error closing badger db during drop and recreate" )
200
+ }
201
+
202
+ s .subscriberPosMu .Lock ()
203
+ backupPath := filepath .Join (filepath .Dir (s .storageDir ), filepath .Base (s .storageDir )+ ".old" )
204
+ // FIXME: what if backupPath already exists?
205
+ err = os .Rename (s .storageDir , backupPath )
206
+ if err != nil {
207
+ s .logger .With (logp .Error (err )).Error ("error renaming old badger db during drop and recreate" )
208
+ }
209
+
210
+ // Since subscriber position file lives in the same tail sampling directory as badger DB,
211
+ // Create tail sampling dir, move back subscriber position file, as it is not a part of the DB.
212
+ var mode os.FileMode
213
+ stat , err := os .Stat (backupPath )
214
+ if err != nil {
215
+ mode = 0700
216
+ s .logger .With (logp .Error (err )).Error ("error stat backup path during drop and recreate" )
217
+ } else {
218
+ mode = stat .Mode ()
219
+ }
220
+ err = os .Mkdir (s .storageDir , mode )
221
+ if err != nil {
222
+ s .logger .With (logp .Error (err )).Error ("error mkdir storage dir during drop and recreate" )
223
+ }
224
+ err = os .Rename (filepath .Join (backupPath , subscriberPositionFile ), filepath .Join (s .storageDir , subscriberPositionFile ))
225
+ if err != nil && ! errors .Is (err , os .ErrNotExist ) {
226
+ s .logger .With (logp .Error (err )).Error ("error copying subscriber position file during drop and recreate" )
227
+ }
228
+
229
+ err = s .Reset () //FIXME: this is likely fatal. Return error to crash the processor
230
+ if err != nil {
231
+ s .logger .With (logp .Error (err )).Error ("error creating new badger db during drop and recreate" )
232
+ }
233
+ s .subscriberPosMu .Unlock ()
234
+ s .mu .Unlock ()
235
+
236
+ err = os .RemoveAll (backupPath )
237
+ if err != nil {
238
+ s .logger .With (logp .Error (err )).Error ("error removing old badger db during drop and recreate" )
239
+ }
240
+ }
241
+
120
242
func (s * StorageManager ) ReadSubscriberPosition () ([]byte , error ) {
121
243
s .subscriberPosMu .Lock ()
122
244
defer s .subscriberPosMu .Unlock ()
@@ -142,26 +264,38 @@ type ManagedReadWriter struct {
142
264
}
143
265
144
266
func (s * ManagedReadWriter ) ReadTraceEvents (traceID string , out * modelpb.Batch ) error {
267
+ s .sm .mu .RLock ()
268
+ defer s .sm .mu .RUnlock ()
145
269
return s .sm .rw .ReadTraceEvents (traceID , out )
146
270
}
147
271
148
272
func (s * ManagedReadWriter ) WriteTraceEvent (traceID , id string , event * modelpb.APMEvent , opts WriterOpts ) error {
273
+ s .sm .mu .RLock ()
274
+ defer s .sm .mu .RUnlock ()
149
275
return s .sm .rw .WriteTraceEvent (traceID , id , event , opts )
150
276
}
151
277
152
278
func (s * ManagedReadWriter ) WriteTraceSampled (traceID string , sampled bool , opts WriterOpts ) error {
279
+ s .sm .mu .RLock ()
280
+ defer s .sm .mu .RUnlock ()
153
281
return s .sm .rw .WriteTraceSampled (traceID , sampled , opts )
154
282
}
155
283
156
284
func (s * ManagedReadWriter ) IsTraceSampled (traceID string ) (bool , error ) {
285
+ s .sm .mu .RLock ()
286
+ defer s .sm .mu .RUnlock ()
157
287
return s .sm .rw .IsTraceSampled (traceID )
158
288
}
159
289
160
290
func (s * ManagedReadWriter ) DeleteTraceEvent (traceID , id string ) error {
291
+ s .sm .mu .RLock ()
292
+ defer s .sm .mu .RUnlock ()
161
293
return s .sm .rw .DeleteTraceEvent (traceID , id )
162
294
}
163
295
164
296
func (s * ManagedReadWriter ) Flush () error {
297
+ s .sm .mu .RLock ()
298
+ defer s .sm .mu .RUnlock ()
165
299
return s .sm .rw .Flush ()
166
300
}
167
301
0 commit comments