|
|
@ -108,11 +108,12 @@ type freezerTable struct { |
|
|
|
|
|
|
|
|
|
|
|
head *os.File // File descriptor for the data head of the table
|
|
|
|
head *os.File // File descriptor for the data head of the table
|
|
|
|
index *os.File // File descriptor for the indexEntry file of the table
|
|
|
|
index *os.File // File descriptor for the indexEntry file of the table
|
|
|
|
meta *os.File // File descriptor for metadata of the table
|
|
|
|
|
|
|
|
files map[uint32]*os.File // open files
|
|
|
|
files map[uint32]*os.File // open files
|
|
|
|
headId uint32 // number of the currently active head file
|
|
|
|
headId uint32 // number of the currently active head file
|
|
|
|
tailId uint32 // number of the earliest file
|
|
|
|
tailId uint32 // number of the earliest file
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
metadata *freezerTableMeta // metadata of the table
|
|
|
|
|
|
|
|
|
|
|
|
headBytes int64 // Number of bytes written to the head file
|
|
|
|
headBytes int64 // Number of bytes written to the head file
|
|
|
|
readMeter metrics.Meter // Meter for measuring the effective amount of data read
|
|
|
|
readMeter metrics.Meter // Meter for measuring the effective amount of data read
|
|
|
|
writeMeter metrics.Meter // Meter for measuring the effective amount of data written
|
|
|
|
writeMeter metrics.Meter // Meter for measuring the effective amount of data written
|
|
|
@ -123,8 +124,8 @@ type freezerTable struct { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// newFreezerTable opens the given path as a freezer table.
|
|
|
|
// newFreezerTable opens the given path as a freezer table.
|
|
|
|
func newFreezerTable(path, name string, disableSnappy, readonly bool) (*freezerTable, error) { |
|
|
|
func newFreezerTable(path, name string, disableSnappy, readonly bool, maxSize uint32) (*freezerTable, error) { |
|
|
|
return newTable(path, name, metrics.NilMeter{}, metrics.NilMeter{}, metrics.NilGauge{}, freezerTableSize, disableSnappy, readonly) |
|
|
|
return newTable(path, name, metrics.NilMeter{}, metrics.NilMeter{}, metrics.NilGauge{}, maxSize, disableSnappy, readonly) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// newTable opens a freezer table, creating the data and index files if they are
|
|
|
|
// newTable opens a freezer table, creating the data and index files if they are
|
|
|
@ -166,10 +167,15 @@ func newTable(path string, name string, readMeter metrics.Meter, writeMeter metr |
|
|
|
return nil, err |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// Load metadata from the file
|
|
|
|
|
|
|
|
metadata, err := newMetadata(meta) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return nil, err |
|
|
|
|
|
|
|
} |
|
|
|
// Create the table and repair any past inconsistency
|
|
|
|
// Create the table and repair any past inconsistency
|
|
|
|
tab := &freezerTable{ |
|
|
|
tab := &freezerTable{ |
|
|
|
index: index, |
|
|
|
index: index, |
|
|
|
meta: meta, |
|
|
|
metadata: metadata, |
|
|
|
files: make(map[uint32]*os.File), |
|
|
|
files: make(map[uint32]*os.File), |
|
|
|
readMeter: readMeter, |
|
|
|
readMeter: readMeter, |
|
|
|
writeMeter: writeMeter, |
|
|
|
writeMeter: writeMeter, |
|
|
@ -222,8 +228,9 @@ func (t *freezerTable) repair() error { |
|
|
|
} // New file can't trigger this path
|
|
|
|
} // New file can't trigger this path
|
|
|
|
} |
|
|
|
} |
|
|
|
// Validate the index file as it might contain some garbage data after the
|
|
|
|
// Validate the index file as it might contain some garbage data after the
|
|
|
|
// power failures.
|
|
|
|
// power failures. The data before the 'IndexFlushOffset' are considered
|
|
|
|
if err := t.repairIndex(); err != nil { |
|
|
|
// as fully flushed and can be skipped for verification.
|
|
|
|
|
|
|
|
if err := t.repairIndex(int64(t.metadata.indexFlushOffset)); err != nil { |
|
|
|
return err |
|
|
|
return err |
|
|
|
} |
|
|
|
} |
|
|
|
// Retrieve the file sizes and prepare for truncation. Note the file size
|
|
|
|
// Retrieve the file sizes and prepare for truncation. Note the file size
|
|
|
@ -253,12 +260,14 @@ func (t *freezerTable) repair() error { |
|
|
|
t.tailId = firstIndex.filenum |
|
|
|
t.tailId = firstIndex.filenum |
|
|
|
t.itemOffset.Store(uint64(firstIndex.offset)) |
|
|
|
t.itemOffset.Store(uint64(firstIndex.offset)) |
|
|
|
|
|
|
|
|
|
|
|
// Load metadata from the file
|
|
|
|
// Adjust the number of hidden items if it is less than the number of items
|
|
|
|
meta, err := loadMetadata(t.meta, t.itemOffset.Load()) |
|
|
|
// being removed.
|
|
|
|
if err != nil { |
|
|
|
if t.itemOffset.Load() > t.metadata.virtualTail { |
|
|
|
return err |
|
|
|
if err := t.metadata.setVirtualTail(t.itemOffset.Load(), true); err != nil { |
|
|
|
|
|
|
|
return err |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
t.itemHidden.Store(meta.VirtualTail) |
|
|
|
t.itemHidden.Store(t.metadata.virtualTail) |
|
|
|
|
|
|
|
|
|
|
|
// Read the last index, use the default value in case the freezer is empty
|
|
|
|
// Read the last index, use the default value in case the freezer is empty
|
|
|
|
if offsetsSize == indexEntrySize { |
|
|
|
if offsetsSize == indexEntrySize { |
|
|
@ -304,6 +313,17 @@ func (t *freezerTable) repair() error { |
|
|
|
// Truncate the index to point within the head file
|
|
|
|
// Truncate the index to point within the head file
|
|
|
|
if contentExp > contentSize { |
|
|
|
if contentExp > contentSize { |
|
|
|
t.logger.Warn("Truncating dangling indexes", "indexes", offsetsSize/indexEntrySize, "indexed", contentExp, "stored", contentSize) |
|
|
|
t.logger.Warn("Truncating dangling indexes", "indexes", offsetsSize/indexEntrySize, "indexed", contentExp, "stored", contentSize) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// If table truncation removes all items after the indexFlushOffset, reset it
|
|
|
|
|
|
|
|
// to zero to force a full validation of the index file on the next restart.
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// This operation should be performed before modifying the index file. In the
|
|
|
|
|
|
|
|
// worst-case scenario, the offset modification could be persisted without any
|
|
|
|
|
|
|
|
// changes to the index file due to an unexpected power failure, resulting in
|
|
|
|
|
|
|
|
// some additional validation workload, which is an acceptable consequence.
|
|
|
|
|
|
|
|
if t.metadata.indexFlushOffset < uint64(offsetsSize-indexEntrySize) { |
|
|
|
|
|
|
|
t.metadata.setIndexFlushOffset(0, true) |
|
|
|
|
|
|
|
} |
|
|
|
if err := truncateFreezerFile(t.index, offsetsSize-indexEntrySize); err != nil { |
|
|
|
if err := truncateFreezerFile(t.index, offsetsSize-indexEntrySize); err != nil { |
|
|
|
return err |
|
|
|
return err |
|
|
|
} |
|
|
|
} |
|
|
@ -345,9 +365,6 @@ func (t *freezerTable) repair() error { |
|
|
|
if err := t.head.Sync(); err != nil { |
|
|
|
if err := t.head.Sync(); err != nil { |
|
|
|
return err |
|
|
|
return err |
|
|
|
} |
|
|
|
} |
|
|
|
if err := t.meta.Sync(); err != nil { |
|
|
|
|
|
|
|
return err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
// Update the item and byte counters and return
|
|
|
|
// Update the item and byte counters and return
|
|
|
|
t.items.Store(t.itemOffset.Load() + uint64(offsetsSize/indexEntrySize-1)) // last indexEntry points to the end of the data file
|
|
|
|
t.items.Store(t.itemOffset.Load() + uint64(offsetsSize/indexEntrySize-1)) // last indexEntry points to the end of the data file
|
|
|
@ -392,7 +409,7 @@ func (t *freezerTable) repair() error { |
|
|
|
// leftover garbage or if all items in the table have zero size is impossible.
|
|
|
|
// leftover garbage or if all items in the table have zero size is impossible.
|
|
|
|
// In such instances, the file will remain unchanged to prevent potential data
|
|
|
|
// In such instances, the file will remain unchanged to prevent potential data
|
|
|
|
// loss or misinterpretation.
|
|
|
|
// loss or misinterpretation.
|
|
|
|
func (t *freezerTable) repairIndex() error { |
|
|
|
func (t *freezerTable) repairIndex(checkOffset int64) error { |
|
|
|
// Retrieve the file sizes and prepare for validation
|
|
|
|
// Retrieve the file sizes and prepare for validation
|
|
|
|
stat, err := t.index.Stat() |
|
|
|
stat, err := t.index.Stat() |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
@ -400,8 +417,19 @@ func (t *freezerTable) repairIndex() error { |
|
|
|
} |
|
|
|
} |
|
|
|
size := stat.Size() |
|
|
|
size := stat.Size() |
|
|
|
|
|
|
|
|
|
|
|
// Move the read cursor to the beginning of the file
|
|
|
|
// Short circuit if the specified start offset is out of range.
|
|
|
|
_, err = t.index.Seek(0, io.SeekStart) |
|
|
|
if checkOffset > size { |
|
|
|
|
|
|
|
return fmt.Errorf("index check offset out of range, offset: %d, size: %d", checkOffset, size) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// Short circuit if the offset points to the end of index file,
|
|
|
|
|
|
|
|
// it could happen when all items in the latest data file are
|
|
|
|
|
|
|
|
// truncated. In this case, all the items in the index file are
|
|
|
|
|
|
|
|
// fully synced and nothing to validate.
|
|
|
|
|
|
|
|
if checkOffset == size { |
|
|
|
|
|
|
|
return nil |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// Move the read cursor to the specified offset
|
|
|
|
|
|
|
|
_, err = t.index.Seek(checkOffset, io.SeekStart) |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
return err |
|
|
|
return err |
|
|
|
} |
|
|
|
} |
|
|
@ -411,7 +439,6 @@ func (t *freezerTable) repairIndex() error { |
|
|
|
start = time.Now() |
|
|
|
start = time.Now() |
|
|
|
buff = make([]byte, indexEntrySize) |
|
|
|
buff = make([]byte, indexEntrySize) |
|
|
|
prev indexEntry |
|
|
|
prev indexEntry |
|
|
|
head indexEntry |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
read = func() (indexEntry, error) { |
|
|
|
read = func() (indexEntry, error) { |
|
|
|
n, err := io.ReadFull(fr, buff) |
|
|
|
n, err := io.ReadFull(fr, buff) |
|
|
@ -436,27 +463,27 @@ func (t *freezerTable) repairIndex() error { |
|
|
|
return nil |
|
|
|
return nil |
|
|
|
} |
|
|
|
} |
|
|
|
) |
|
|
|
) |
|
|
|
for offset := int64(0); offset < size; offset += indexEntrySize { |
|
|
|
for offset := checkOffset; offset < size; offset += indexEntrySize { |
|
|
|
entry, err := read() |
|
|
|
entry, err := read() |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
return err |
|
|
|
return err |
|
|
|
} |
|
|
|
} |
|
|
|
if offset == 0 { |
|
|
|
if offset == checkOffset { |
|
|
|
head = entry |
|
|
|
prev = entry |
|
|
|
continue |
|
|
|
continue |
|
|
|
} |
|
|
|
} |
|
|
|
// Ensure that the first non-head index refers to the earliest file,
|
|
|
|
// Specialize if the index validation starts from zero, in which the "offset"
|
|
|
|
// or the next file if the earliest file has no space to place the
|
|
|
|
// field of the first index entry represents the number of deleted items
|
|
|
|
// first item.
|
|
|
|
// from the tail, rather the offset in the data file. Therefore, skip the
|
|
|
|
|
|
|
|
// offset validation for the first two entries.
|
|
|
|
if offset == indexEntrySize { |
|
|
|
if offset == indexEntrySize { |
|
|
|
if entry.filenum != head.filenum && entry.filenum != head.filenum+1 { |
|
|
|
if entry.filenum != prev.filenum && entry.filenum != prev.filenum+1 { |
|
|
|
log.Error("Corrupted index item detected", "earliest", head.filenum, "filenumber", entry.filenum) |
|
|
|
log.Error("Corrupted index item detected", "earliest", prev.filenum, "next", entry.filenum) |
|
|
|
return truncate(offset) |
|
|
|
return truncate(offset) |
|
|
|
} |
|
|
|
} |
|
|
|
prev = entry |
|
|
|
prev = entry |
|
|
|
continue |
|
|
|
continue |
|
|
|
} |
|
|
|
} |
|
|
|
// ensure two consecutive index items are in order
|
|
|
|
|
|
|
|
if err := t.checkIndexItems(prev, entry); err != nil { |
|
|
|
if err := t.checkIndexItems(prev, entry); err != nil { |
|
|
|
log.Error("Corrupted index item detected", "err", err) |
|
|
|
log.Error("Corrupted index item detected", "err", err) |
|
|
|
return truncate(offset) |
|
|
|
return truncate(offset) |
|
|
@ -470,7 +497,7 @@ func (t *freezerTable) repairIndex() error { |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
return err |
|
|
|
return err |
|
|
|
} |
|
|
|
} |
|
|
|
log.Debug("Verified index file", "items", size/indexEntrySize, "elapsed", common.PrettyDuration(time.Since(start))) |
|
|
|
log.Debug("Verified index file", "items", (size-checkOffset)/indexEntrySize, "elapsed", common.PrettyDuration(time.Since(start))) |
|
|
|
return nil |
|
|
|
return nil |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -550,6 +577,17 @@ func (t *freezerTable) truncateHead(items uint64) error { |
|
|
|
// Truncate the index file first, the tail position is also considered
|
|
|
|
// Truncate the index file first, the tail position is also considered
|
|
|
|
// when calculating the new freezer table length.
|
|
|
|
// when calculating the new freezer table length.
|
|
|
|
length := items - t.itemOffset.Load() |
|
|
|
length := items - t.itemOffset.Load() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// If head truncation removes all items after the indexFlushOffset, reset it
|
|
|
|
|
|
|
|
// to zero to force a full validation of the index file on the next restart.
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// This operation should be performed before modifying the index file. In the
|
|
|
|
|
|
|
|
// worst-case scenario, the offset modification could be persisted without any
|
|
|
|
|
|
|
|
// changes to the index file due to an unexpected power failure, resulting in
|
|
|
|
|
|
|
|
// some additional validation workload, which is an acceptable consequence.
|
|
|
|
|
|
|
|
if t.metadata.indexFlushOffset > (length+1)*indexEntrySize { |
|
|
|
|
|
|
|
t.metadata.setIndexFlushOffset(0, true) |
|
|
|
|
|
|
|
} |
|
|
|
if err := truncateFreezerFile(t.index, int64(length+1)*indexEntrySize); err != nil { |
|
|
|
if err := truncateFreezerFile(t.index, int64(length+1)*indexEntrySize); err != nil { |
|
|
|
return err |
|
|
|
return err |
|
|
|
} |
|
|
|
} |
|
|
@ -652,7 +690,10 @@ func (t *freezerTable) truncateTail(items uint64) error { |
|
|
|
} |
|
|
|
} |
|
|
|
// Update the virtual tail marker and hidden these entries in table.
|
|
|
|
// Update the virtual tail marker and hidden these entries in table.
|
|
|
|
t.itemHidden.Store(items) |
|
|
|
t.itemHidden.Store(items) |
|
|
|
if err := writeMetadata(t.meta, newMetadata(items)); err != nil { |
|
|
|
|
|
|
|
|
|
|
|
// Update the virtual tail without fsync, otherwise it will significantly
|
|
|
|
|
|
|
|
// impact the overall performance.
|
|
|
|
|
|
|
|
if err := t.metadata.setVirtualTail(items, false); err != nil { |
|
|
|
return err |
|
|
|
return err |
|
|
|
} |
|
|
|
} |
|
|
|
// Hidden items still fall in the current tail file, no data file
|
|
|
|
// Hidden items still fall in the current tail file, no data file
|
|
|
@ -681,10 +722,24 @@ func (t *freezerTable) truncateTail(items uint64) error { |
|
|
|
} |
|
|
|
} |
|
|
|
newDeleted = current |
|
|
|
newDeleted = current |
|
|
|
} |
|
|
|
} |
|
|
|
// Commit the changes of metadata file first before manipulating
|
|
|
|
// Move backward the index flush offset due to the index segment deletion.
|
|
|
|
// the indexes file.
|
|
|
|
//
|
|
|
|
if err := t.meta.Sync(); err != nil { |
|
|
|
// This operation should be performed before modifying the index file. In the
|
|
|
|
return err |
|
|
|
// worst-case scenario, the offset modification could be persisted without any
|
|
|
|
|
|
|
|
// changes to the index file due to an unexpected power failure, resulting in
|
|
|
|
|
|
|
|
// some additional validation workload, which is an acceptable consequence.
|
|
|
|
|
|
|
|
shorten := indexEntrySize * (newDeleted - deleted) |
|
|
|
|
|
|
|
if t.metadata.indexFlushOffset <= shorten { |
|
|
|
|
|
|
|
// It's never expected to happen, reset the flush offset to zero just
|
|
|
|
|
|
|
|
// in case.
|
|
|
|
|
|
|
|
t.logger.Error("Reset the index flush offset", "current", t.metadata.indexFlushOffset, "shorten", shorten) |
|
|
|
|
|
|
|
if err := t.metadata.setIndexFlushOffset(0, true); err != nil { |
|
|
|
|
|
|
|
return err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
if err := t.metadata.setIndexFlushOffset(t.metadata.indexFlushOffset-shorten, true); err != nil { |
|
|
|
|
|
|
|
return err |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
// Close the index file before shorten it.
|
|
|
|
// Close the index file before shorten it.
|
|
|
|
if err := t.index.Close(); err != nil { |
|
|
|
if err := t.index.Close(); err != nil { |
|
|
@ -746,7 +801,7 @@ func (t *freezerTable) Close() error { |
|
|
|
// Trying to fsync a file opened in rdonly causes "Access denied"
|
|
|
|
// Trying to fsync a file opened in rdonly causes "Access denied"
|
|
|
|
// error on Windows.
|
|
|
|
// error on Windows.
|
|
|
|
doClose(t.index, true, true) |
|
|
|
doClose(t.index, true, true) |
|
|
|
doClose(t.meta, true, true) |
|
|
|
doClose(t.metadata.file, true, true) |
|
|
|
|
|
|
|
|
|
|
|
// The preopened non-head data-files are all opened in readonly.
|
|
|
|
// The preopened non-head data-files are all opened in readonly.
|
|
|
|
// The head is opened in rw-mode, so we sync it here - but since it's also
|
|
|
|
// The head is opened in rw-mode, so we sync it here - but since it's also
|
|
|
@ -757,7 +812,6 @@ func (t *freezerTable) Close() error { |
|
|
|
doClose(f, false, true) // close but do not sync
|
|
|
|
doClose(f, false, true) // close but do not sync
|
|
|
|
} |
|
|
|
} |
|
|
|
t.index = nil |
|
|
|
t.index = nil |
|
|
|
t.meta = nil |
|
|
|
|
|
|
|
t.head = nil |
|
|
|
t.head = nil |
|
|
|
|
|
|
|
|
|
|
|
if errs != nil { |
|
|
|
if errs != nil { |
|
|
@ -917,7 +971,7 @@ func (t *freezerTable) retrieveItems(start, count, maxBytes uint64) ([]byte, []i |
|
|
|
defer t.lock.RUnlock() |
|
|
|
defer t.lock.RUnlock() |
|
|
|
|
|
|
|
|
|
|
|
// Ensure the table and the item are accessible
|
|
|
|
// Ensure the table and the item are accessible
|
|
|
|
if t.index == nil || t.head == nil || t.meta == nil { |
|
|
|
if t.index == nil || t.head == nil { |
|
|
|
return nil, nil, errClosed |
|
|
|
return nil, nil, errClosed |
|
|
|
} |
|
|
|
} |
|
|
|
var ( |
|
|
|
var ( |
|
|
@ -1042,6 +1096,21 @@ func (t *freezerTable) advanceHead() error { |
|
|
|
t.lock.Lock() |
|
|
|
t.lock.Lock() |
|
|
|
defer t.lock.Unlock() |
|
|
|
defer t.lock.Unlock() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Flush the index file content into disk whenever the head is advanced.
|
|
|
|
|
|
|
|
// It should be performed before updating the index flush offset.
|
|
|
|
|
|
|
|
if err := t.index.Sync(); err != nil { |
|
|
|
|
|
|
|
return err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// Move forward the flush offset to skip unnecessary validation workload
|
|
|
|
|
|
|
|
stat, err := t.index.Stat() |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// Move the index flush offset forward to reduce the workload for index
|
|
|
|
|
|
|
|
// validation.
|
|
|
|
|
|
|
|
if err := t.metadata.setIndexFlushOffset(uint64(stat.Size()), true); err != nil { |
|
|
|
|
|
|
|
return err |
|
|
|
|
|
|
|
} |
|
|
|
// We open the next file in truncated mode -- if this file already
|
|
|
|
// We open the next file in truncated mode -- if this file already
|
|
|
|
// exists, we need to start over from scratch on it.
|
|
|
|
// exists, we need to start over from scratch on it.
|
|
|
|
nextID := t.headId + 1 |
|
|
|
nextID := t.headId + 1 |
|
|
@ -1069,7 +1138,7 @@ func (t *freezerTable) advanceHead() error { |
|
|
|
func (t *freezerTable) Sync() error { |
|
|
|
func (t *freezerTable) Sync() error { |
|
|
|
t.lock.Lock() |
|
|
|
t.lock.Lock() |
|
|
|
defer t.lock.Unlock() |
|
|
|
defer t.lock.Unlock() |
|
|
|
if t.index == nil || t.head == nil || t.meta == nil { |
|
|
|
if t.index == nil || t.head == nil { |
|
|
|
return errClosed |
|
|
|
return errClosed |
|
|
|
} |
|
|
|
} |
|
|
|
var err error |
|
|
|
var err error |
|
|
@ -1078,10 +1147,9 @@ func (t *freezerTable) Sync() error { |
|
|
|
err = e |
|
|
|
err = e |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
trackError(t.index.Sync()) |
|
|
|
trackError(t.index.Sync()) |
|
|
|
trackError(t.meta.Sync()) |
|
|
|
|
|
|
|
trackError(t.head.Sync()) |
|
|
|
trackError(t.head.Sync()) |
|
|
|
|
|
|
|
trackError(t.metadata.file.Sync()) |
|
|
|
return err |
|
|
|
return err |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -1097,13 +1165,8 @@ func (t *freezerTable) dumpIndexString(start, stop int64) string { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (t *freezerTable) dumpIndex(w io.Writer, start, stop int64) { |
|
|
|
func (t *freezerTable) dumpIndex(w io.Writer, start, stop int64) { |
|
|
|
meta, err := readMetadata(t.meta) |
|
|
|
fmt.Fprintf(w, "Version %d count %d, deleted %d, hidden %d\n", |
|
|
|
if err != nil { |
|
|
|
t.metadata.version, t.items.Load(), t.itemOffset.Load(), t.itemHidden.Load()) |
|
|
|
fmt.Fprintf(w, "Failed to decode freezer table %v\n", err) |
|
|
|
|
|
|
|
return |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
fmt.Fprintf(w, "Version %d count %d, deleted %d, hidden %d\n", meta.Version, |
|
|
|
|
|
|
|
t.items.Load(), t.itemOffset.Load(), t.itemHidden.Load()) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
buf := make([]byte, indexEntrySize) |
|
|
|
buf := make([]byte, indexEntrySize) |
|
|
|
|
|
|
|
|
|
|
|