@@ -12,25 +12,22 @@ import (
12
12
"os"
13
13
"path/filepath"
14
14
"regexp"
15
- "sort"
16
15
"strings"
17
16
"sync"
18
17
"sync/atomic"
19
18
"time"
20
19
21
20
"github.com/grafana/metrictank/conf"
22
21
"github.com/grafana/metrictank/logger"
23
- "github.com/grafana/metrictank/mdata"
24
- "github.com/grafana/metrictank/mdata/chunk"
22
+ "github.com/grafana/metrictank/mdata/importer"
25
23
"github.com/kisielk/whisper-go/whisper"
26
- "github.com/raintank/schema"
27
24
log "github.com/sirupsen/logrus"
28
25
)
29
26
30
27
var (
31
28
httpEndpoint = flag .String (
32
29
"http-endpoint" ,
33
- "http://127.0.0.1:8080/chunks " ,
30
+ "http://127.0.0.1:8080/metrics/import " ,
34
31
"The http endpoint to send the data to" ,
35
32
)
36
33
namePrefix = flag .String (
48
45
false ,
49
46
"Defines if chunks that have not completed their chunk span should be written" ,
50
47
)
51
- orgId = flag .Int (
52
- "orgid" ,
53
- 1 ,
54
- "Organization ID the data belongs to " ,
55
- )
56
48
insecureSSL = flag .Bool (
57
49
"insecure-ssl" ,
58
50
false ,
@@ -78,15 +70,15 @@ var (
78
70
"" ,
79
71
"A regex pattern to be applied to all metric names, only matching ones will be imported" ,
80
72
)
81
- importUpTo = flag .Uint (
82
- "import-up-to " ,
73
+ importUntil = flag .Uint (
74
+ "import-until " ,
83
75
math .MaxUint32 ,
84
- "Only import up to the specified timestamp" ,
76
+ "Only import up to, but not including, the specified timestamp" ,
85
77
)
86
- importAfter = flag .Uint (
87
- "import-after " ,
78
+ importFrom = flag .Uint (
79
+ "import-from " ,
88
80
0 ,
89
- "Only import after the specified timestamp" ,
81
+ "Only import starting from the specified timestamp" ,
90
82
)
91
83
positionFile = flag .String (
92
84
"position-file" ,
@@ -167,7 +159,7 @@ func processFromChan(pos *posTracker, files chan string, wg *sync.WaitGroup) {
167
159
168
160
name := getMetricName (file )
169
161
log .Debugf ("Processing file %s (%s)" , file , name )
170
- data , err := getMetric (w , file , name )
162
+ data , err := importer . NewArchiveRequest (w , schemas , file , name , uint32 ( * importFrom ), uint32 ( * importUntil ), * writeUnfinishedChunks )
171
163
if err != nil {
172
164
log .Errorf ("Failed to get metric: %q" , err .Error ())
173
165
continue
@@ -246,152 +238,6 @@ func getMetricName(file string) string {
246
238
return * namePrefix + strings .Replace (strings .TrimSuffix (file , ".wsp" ), "/" , "." , - 1 )
247
239
}
248
240
249
- // pointSorter sorts points by timestamp
250
- type pointSorter []whisper.Point
251
-
252
- func (a pointSorter ) Len () int { return len (a ) }
253
- func (a pointSorter ) Swap (i , j int ) { a [i ], a [j ] = a [j ], a [i ] }
254
- func (a pointSorter ) Less (i , j int ) bool { return a [i ].Timestamp < a [j ].Timestamp }
255
-
256
- // the whisper archives are organized like a ringbuffer. since we need to
257
- // insert the points into the chunks in order we first need to sort them
258
- func sortPoints (points pointSorter ) pointSorter {
259
- sort .Sort (points )
260
- return points
261
- }
262
-
263
- func convertWhisperMethod (whisperMethod whisper.AggregationMethod ) (schema.Method , error ) {
264
- switch whisperMethod {
265
- case whisper .AggregationAverage :
266
- return schema .Avg , nil
267
- case whisper .AggregationSum :
268
- return schema .Sum , nil
269
- case whisper .AggregationLast :
270
- return schema .Lst , nil
271
- case whisper .AggregationMax :
272
- return schema .Max , nil
273
- case whisper .AggregationMin :
274
- return schema .Min , nil
275
- default :
276
- return 0 , fmt .Errorf ("Unknown whisper method: %d" , whisperMethod )
277
- }
278
- }
279
-
280
- func getMetric (w * whisper.Whisper , file , name string ) (* mdata.ArchiveRequest , error ) {
281
- if len (w .Header .Archives ) == 0 {
282
- return nil , fmt .Errorf ("Whisper file contains no archives: %q" , file )
283
- }
284
-
285
- method , err := convertWhisperMethod (w .Header .Metadata .AggregationMethod )
286
- if err != nil {
287
- return nil , err
288
- }
289
-
290
- points := make (map [int ][]whisper.Point )
291
- for i := range w .Header .Archives {
292
- p , err := w .DumpArchive (i )
293
- if err != nil {
294
- return nil , fmt .Errorf ("Failed to dump archive %d from whisper file %s" , i , file )
295
- }
296
- points [i ] = p
297
- }
298
-
299
- res := & mdata.ArchiveRequest {
300
- MetricData : schema.MetricData {
301
- Name : name ,
302
- Value : 0 ,
303
- Interval : int (w .Header .Archives [0 ].SecondsPerPoint ),
304
- Unit : "unknown" ,
305
- Time : 0 ,
306
- Mtype : "gauge" ,
307
- Tags : []string {},
308
- OrgId : * orgId ,
309
- },
310
- }
311
- res .MetricData .SetId ()
312
- mkey , err := schema .MKeyFromString (res .MetricData .Id )
313
- if err != nil {
314
- panic (err )
315
- }
316
-
317
- _ , selectedSchema := schemas .Match (res .MetricData .Name , int (w .Header .Archives [0 ].SecondsPerPoint ))
318
- conversion := newConversion (w .Header .Archives , points , method )
319
- for retIdx , retention := range selectedSchema .Retentions {
320
- convertedPoints := conversion .getPoints (retIdx , uint32 (retention .SecondsPerPoint ), uint32 (retention .NumberOfPoints ))
321
- for m , p := range convertedPoints {
322
- if len (p ) == 0 {
323
- continue
324
- }
325
-
326
- var amkey schema.AMKey
327
- if retIdx == 0 {
328
- amkey = schema.AMKey {MKey : mkey }
329
- } else {
330
- amkey = schema .GetAMKey (mkey , m , retention .ChunkSpan )
331
- }
332
-
333
- encodedChunks := encodedChunksFromPoints (p , uint32 (retention .SecondsPerPoint ), retention .ChunkSpan )
334
- for _ , chunk := range encodedChunks {
335
- res .ChunkWriteRequests = append (res .ChunkWriteRequests , mdata .NewChunkWriteRequest (
336
- nil ,
337
- amkey ,
338
- uint32 (retention .MaxRetention ()),
339
- chunk .Series .T0 ,
340
- chunk .Encode (retention .ChunkSpan ),
341
- time .Now (),
342
- ))
343
- }
344
-
345
- if res .MetricData .Time < int64 (p [len (p )- 1 ].Timestamp ) {
346
- res .MetricData .Time = int64 (p [len (p )- 1 ].Timestamp )
347
- }
348
- }
349
- }
350
-
351
- return res , nil
352
- }
353
-
354
- func encodedChunksFromPoints (points []whisper.Point , intervalIn , chunkSpan uint32 ) []* chunk.Chunk {
355
- var point whisper.Point
356
- var t0 , prevT0 uint32
357
- var c * chunk.Chunk
358
- var encodedChunks []* chunk.Chunk
359
-
360
- for _ , point = range points {
361
- // this shouldn't happen, but if it would we better catch it here because Metrictank wouldn't handle it well:
362
- // https://github.com/grafana/metrictank/blob/f1868cccfb92fc82cd853914af958f6d187c5f74/mdata/aggmetric.go#L378
363
- if point .Timestamp == 0 {
364
- continue
365
- }
366
-
367
- t0 = point .Timestamp - (point .Timestamp % chunkSpan )
368
- if prevT0 == 0 {
369
- c = chunk .New (t0 )
370
- prevT0 = t0
371
- } else if prevT0 != t0 {
372
- c .Finish ()
373
- encodedChunks = append (encodedChunks , c )
374
-
375
- c = chunk .New (t0 )
376
- prevT0 = t0
377
- }
378
-
379
- err := c .Push (point .Timestamp , point .Value )
380
- if err != nil {
381
- panic (fmt .Sprintf ("ERROR: Failed to push value into chunk at t0 %d: %q" , t0 , err ))
382
- }
383
- }
384
-
385
- // if the last written point was also the last one of the current chunk,
386
- // or if writeUnfinishedChunks is on, we close the chunk and push it
387
- if point .Timestamp == t0 + chunkSpan - intervalIn || * writeUnfinishedChunks {
388
- c .Finish ()
389
- encodedChunks = append (encodedChunks , c )
390
- }
391
-
392
- return encodedChunks
393
- }
394
-
395
241
// scan a directory and feed the list of whisper files relative to base into the given channel
396
242
func getFileListIntoChan (pos * posTracker , fileChan chan string ) {
397
243
filepath .Walk (
0 commit comments