@@ -86,11 +86,13 @@ type Partition struct {
86
86
87
87
// Index's version.
88
88
version int
89
+
90
+ manifestPathFn func () string
89
91
}
90
92
91
93
// NewPartition returns a new instance of Partition.
92
94
func NewPartition (sfile * tsdb.SeriesFile , path string ) * Partition {
93
- return & Partition {
95
+ p := & Partition {
94
96
closing : make (chan struct {}),
95
97
path : path ,
96
98
sfile : sfile ,
@@ -104,6 +106,8 @@ func NewPartition(sfile *tsdb.SeriesFile, path string) *Partition {
104
106
logger : zap .NewNop (),
105
107
version : Version ,
106
108
}
109
+ p .manifestPathFn = p .manifestPath
110
+ return p
107
111
}
108
112
109
113
// bytes estimates the memory footprint of this Partition, in bytes.
@@ -408,27 +412,44 @@ func (p *Partition) nextSequence() int {
408
412
return p .seq
409
413
}
410
414
411
- // ManifestPath returns the path to the index's manifest file.
412
415
func (p * Partition ) ManifestPath () string {
416
+ return p .manifestPathFn ()
417
+ }
418
+
419
+ // ManifestPath returns the path to the index's manifest file.
420
+ func (p * Partition ) manifestPath () string {
413
421
return filepath .Join (p .path , ManifestFileName )
414
422
}
415
423
416
424
// Manifest returns a manifest for the index.
417
425
func (p * Partition ) Manifest () * Manifest {
426
+ return p .manifest (p .fileSet )
427
+ }
428
+
429
+ // manifest returns a manifest for the index, possibly using a
430
+ // new FileSet to account for compaction or log prepending
431
+ func (p * Partition ) manifest (newFileSet * FileSet ) * Manifest {
418
432
m := & Manifest {
419
433
Levels : p .levels ,
420
- Files : make ([]string , len (p . fileSet .files )),
434
+ Files : make ([]string , len (newFileSet .files )),
421
435
Version : p .version ,
422
436
path : p .ManifestPath (),
423
437
}
424
438
425
- for j , f := range p . fileSet .files {
439
+ for j , f := range newFileSet .files {
426
440
m .Files [j ] = filepath .Base (f .Path ())
427
441
}
428
442
429
443
return m
430
444
}
431
445
446
+ // SetManifestPathForTest is only to force a bad path in testing
447
+ func (p * Partition ) SetManifestPathForTest (path string ) {
448
+ p .mu .Lock ()
449
+ defer p .mu .Unlock ()
450
+ p .manifestPathFn = func () string { return path }
451
+ }
452
+
432
453
// WithLogger sets the logger for the index.
433
454
func (p * Partition ) WithLogger (logger * zap.Logger ) {
434
455
p .logger = logger .With (zap .String ("index" , "tsi" ))
@@ -468,28 +489,42 @@ func (p *Partition) retainFileSet() *FileSet {
468
489
}
469
490
470
491
// FileN returns the active files in the file set.
471
- func (p * Partition ) FileN () int { return len (p .fileSet .files ) }
492
+ func (p * Partition ) FileN () int {
493
+ p .mu .RLock ()
494
+ defer p .mu .RUnlock ()
495
+ return len (p .fileSet .files )
496
+ }
472
497
473
498
// prependActiveLogFile adds a new log file so that the current log file can be compacted.
474
- func (p * Partition ) prependActiveLogFile () error {
499
+ func (p * Partition ) prependActiveLogFile () ( rErr error ) {
475
500
// Open file and insert it into the first position.
476
501
f , err := p .openLogFile (filepath .Join (p .path , FormatLogFileName (p .nextSequence ())))
477
502
if err != nil {
478
503
return err
479
504
}
480
- p .activeLogFile = f
505
+ var oldActiveFile * LogFile
506
+ p .activeLogFile , oldActiveFile = f , p .activeLogFile
481
507
482
- // Prepend and generate new fileset.
483
- p .fileSet = p .fileSet .PrependLogFile (f )
508
+ // Prepend and generate new fileset but do not yet update the partition
509
+ newFileSet := p .fileSet .PrependLogFile (f )
510
+
511
+ errors2 .Capture (& rErr , func () error {
512
+ if rErr != nil {
513
+ // close the new file.
514
+ f .Close ()
515
+ p .activeLogFile = oldActiveFile
516
+ }
517
+ return rErr
518
+ })()
484
519
485
520
// Write new manifest.
486
- manifestSize , err := p .Manifest ( ).Write ()
521
+ manifestSize , err := p .manifest ( newFileSet ).Write ()
487
522
if err != nil {
488
- // TODO: Close index if write fails.
489
- p .logger .Error ("manifest write failed, index is potentially damaged" , zap .Error (err ))
490
- return err
523
+ return fmt .Errorf ("manifest write failed for %q: %w" , p .ManifestPath (), err )
491
524
}
492
525
p .manifestSize = manifestSize
526
+ // Store the new FileSet in the partition now that the manifest has been written
527
+ p .fileSet = newFileSet
493
528
return nil
494
529
}
495
530
@@ -1113,20 +1148,28 @@ func (p *Partition) compactToLevel(files []*IndexFile, level int, interrupt <-ch
1113
1148
}
1114
1149
1115
1150
// Obtain lock to swap in index file and write manifest.
1116
- if err := func () error {
1151
+ if err := func () ( rErr error ) {
1117
1152
p .mu .Lock ()
1118
1153
defer p .mu .Unlock ()
1119
1154
1120
1155
// Replace previous files with new index file.
1121
- p . fileSet = p .fileSet .MustReplace (IndexFiles (files ).Files (), file )
1156
+ newFileSet : = p .fileSet .MustReplace (IndexFiles (files ).Files (), file )
1122
1157
1123
1158
// Write new manifest.
1124
- manifestSize , err := p .Manifest ().Write ()
1159
+ manifestSize , err := p .manifest (newFileSet ).Write ()
1160
+ defer errors2 .Capture (& rErr , func () error {
1161
+ if rErr != nil {
1162
+ // Close the new file to avoid leaks.
1163
+ file .Close ()
1164
+ }
1165
+ return rErr
1166
+ })()
1125
1167
if err != nil {
1126
- // TODO: Close index if write fails.
1127
- return err
1168
+ return fmt .Errorf ("manifest file write failed compacting index %q: %w" , p .ManifestPath (), err )
1128
1169
}
1129
1170
p .manifestSize = manifestSize
1171
+ // Store the new FileSet in the partition now that the manifest has been written
1172
+ p .fileSet = newFileSet
1130
1173
return nil
1131
1174
}(); err != nil {
1132
1175
log .Error ("Cannot write manifest" , zap .Error (err ))
@@ -1262,20 +1305,29 @@ func (p *Partition) compactLogFile(logFile *LogFile) {
1262
1305
}
1263
1306
1264
1307
// Obtain lock to swap in index file and write manifest.
1265
- if err := func () error {
1308
+ if err := func () ( rErr error ) {
1266
1309
p .mu .Lock ()
1267
1310
defer p .mu .Unlock ()
1268
1311
1269
1312
// Replace previous log file with index file.
1270
- p .fileSet = p .fileSet .MustReplace ([]File {logFile }, file )
1313
+ newFileSet := p .fileSet .MustReplace ([]File {logFile }, file )
1314
+
1315
+ defer errors2 .Capture (& rErr , func () error {
1316
+ if rErr != nil {
1317
+ // close new file
1318
+ file .Close ()
1319
+ }
1320
+ return rErr
1321
+ })()
1271
1322
1272
1323
// Write new manifest.
1273
- manifestSize , err := p .Manifest ().Write ()
1324
+ manifestSize , err := p .manifest (newFileSet ).Write ()
1325
+
1274
1326
if err != nil {
1275
- // TODO: Close index if write fails.
1276
- return err
1327
+ return fmt .Errorf ("manifest file write failed compacting log file %q: %w" , p .ManifestPath (), err )
1277
1328
}
1278
-
1329
+ // Store the new FileSet in the partition now that the manifest has been written
1330
+ p .fileSet = newFileSet
1279
1331
p .manifestSize = manifestSize
1280
1332
return nil
1281
1333
}(); err != nil {
@@ -1389,6 +1441,7 @@ func (m *Manifest) Validate() error {
1389
1441
// Write writes the manifest file to the provided path, returning the number of
1390
1442
// bytes written and an error, if any.
1391
1443
func (m * Manifest ) Write () (int64 , error ) {
1444
+ var tmp string
1392
1445
buf , err := json .MarshalIndent (m , "" , " " )
1393
1446
if err != nil {
1394
1447
return 0 , fmt .Errorf ("failed marshaling %q: %w" , m .path , err )
@@ -1401,25 +1454,30 @@ func (m *Manifest) Write() (int64, error) {
1401
1454
return 0 , err
1402
1455
}
1403
1456
1404
- tmp := f .Name ()
1405
1457
// In correct operation, Remove() should fail because the file was renamed
1406
1458
defer os .Remove (tmp )
1459
+
1407
1460
err = func () (rErr error ) {
1408
1461
// Close() before rename for Windows
1409
1462
defer errors2 .Capture (& rErr , f .Close )()
1463
+
1464
+ tmp = f .Name ()
1465
+
1466
+ if err = f .Chmod (0666 ); err != nil {
1467
+ return fmt .Errorf ("failed setting permissions on manifest file %q: %w" , tmp , err )
1468
+ }
1410
1469
if _ , err = f .Write (buf ); err != nil {
1411
1470
return fmt .Errorf ("failed writing temporary manifest file %q: %w" , tmp , err )
1412
1471
}
1472
+ if err = f .Sync (); err != nil {
1473
+ return fmt .Errorf ("failed syncing temporary manifest file to disk %q: %w" , tmp , err )
1474
+ }
1413
1475
return nil
1414
1476
}()
1415
1477
if err != nil {
1416
1478
return 0 , err
1417
1479
}
1418
1480
1419
- if err = os .Chmod (tmp , 0666 ); err != nil {
1420
- return 0 , err
1421
- }
1422
-
1423
1481
if err = os .Rename (tmp , m .path ); err != nil {
1424
1482
return 0 , err
1425
1483
}
0 commit comments