-
Notifications
You must be signed in to change notification settings - Fork 179
refactor block size calculation #637
Changes from 7 commits
64101d9
321002e
9fcf803
73547a6
b9e94c5
f83fc26
af26017
deb9b07
c7e140f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -151,12 +151,6 @@ type Appendable interface { | |
Appender() Appender | ||
} | ||
|
||
// SizeReader returns the size of the object in bytes. | ||
type SizeReader interface { | ||
// Size returns the size in bytes. | ||
Size() int64 | ||
} | ||
|
||
// BlockMeta provides meta information about a block. | ||
type BlockMeta struct { | ||
// Unique identifier for the block and its contents. Changes on compaction. | ||
|
@@ -183,7 +177,6 @@ type BlockStats struct { | |
NumSeries uint64 `json:"numSeries,omitempty"` | ||
NumChunks uint64 `json:"numChunks,omitempty"` | ||
NumTombstones uint64 `json:"numTombstones,omitempty"` | ||
NumBytes int64 `json:"numBytes,omitempty"` | ||
} | ||
|
||
// BlockDesc describes a block by ULID and time range. | ||
|
@@ -214,24 +207,24 @@ const metaFilename = "meta.json" | |
|
||
func chunkDir(dir string) string { return filepath.Join(dir, "chunks") } | ||
|
||
func readMetaFile(dir string) (*BlockMeta, error) { | ||
func readMetaFile(dir string) (*BlockMeta, int64, error) { | ||
b, err := ioutil.ReadFile(filepath.Join(dir, metaFilename)) | ||
if err != nil { | ||
return nil, err | ||
return nil, 0, err | ||
} | ||
var m BlockMeta | ||
|
||
if err := json.Unmarshal(b, &m); err != nil { | ||
return nil, err | ||
return nil, 0, err | ||
} | ||
if m.Version != 1 { | ||
return nil, errors.Errorf("unexpected meta file version %d", m.Version) | ||
return nil, 0, errors.Errorf("unexpected meta file version %d", m.Version) | ||
} | ||
|
||
return &m, nil | ||
return &m, int64(len(b)), nil | ||
} | ||
|
||
func writeMetaFile(logger log.Logger, dir string, meta *BlockMeta) error { | ||
func writeMetaFile(logger log.Logger, dir string, meta *BlockMeta) (int64, error) { | ||
meta.Version = 1 | ||
|
||
// Make any changes to the file appear atomic. | ||
|
@@ -245,26 +238,32 @@ func writeMetaFile(logger log.Logger, dir string, meta *BlockMeta) error { | |
|
||
f, err := os.Create(tmp) | ||
if err != nil { | ||
return err | ||
return 0, err | ||
} | ||
|
||
enc := json.NewEncoder(f) | ||
enc.SetIndent("", "\t") | ||
jsonMeta, err := json.MarshalIndent(meta, "", "\t") | ||
if err != nil { | ||
return 0, err | ||
} | ||
|
||
var merr tsdb_errors.MultiError | ||
if merr.Add(enc.Encode(meta)); merr.Err() != nil { | ||
n, err := f.Write(jsonMeta) | ||
if err != nil { | ||
krasi-georgiev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
merr.Add(err) | ||
merr.Add(f.Close()) | ||
return merr.Err() | ||
return 0, merr.Err() | ||
} | ||
|
||
// Force the kernel to persist the file on disk to avoid data loss if the host crashes. | ||
if merr.Add(f.Sync()); merr.Err() != nil { | ||
if err := f.Sync(); err != nil { | ||
merr.Add(err) | ||
krasi-georgiev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
merr.Add(f.Close()) | ||
return merr.Err() | ||
return 0, merr.Err() | ||
} | ||
if err := f.Close(); err != nil { | ||
return err | ||
return 0, err | ||
} | ||
return fileutil.Replace(tmp, path) | ||
return int64(n), fileutil.Replace(tmp, path) | ||
} | ||
|
||
// Block represents a directory of time series data covering a continuous time range. | ||
|
@@ -285,6 +284,11 @@ type Block struct { | |
tombstones TombstoneReader | ||
|
||
logger log.Logger | ||
|
||
numBytesChunks int64 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. all Size,Write interfaces I have seen so far use int or int64 so I prefer to keep it as is. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, fine by me then There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for explaining |
||
numBytesIndex int64 | ||
numBytesTombstone int64 | ||
numBytesMeta int64 | ||
} | ||
|
||
// OpenBlock opens the block in the directory. It can be passed a chunk pool, which is used | ||
|
@@ -302,7 +306,7 @@ func OpenBlock(logger log.Logger, dir string, pool chunkenc.Pool) (pb *Block, er | |
err = merr.Err() | ||
} | ||
}() | ||
meta, err := readMetaFile(dir) | ||
meta, sizeMeta, err := readMetaFile(dir) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
@@ -319,43 +323,28 @@ func OpenBlock(logger log.Logger, dir string, pool chunkenc.Pool) (pb *Block, er | |
} | ||
closers = append(closers, ir) | ||
|
||
tr, tsr, err := readTombstones(dir) | ||
tr, sizeTomb, err := readTombstones(dir) | ||
if err != nil { | ||
return nil, err | ||
} | ||
closers = append(closers, tr) | ||
|
||
// TODO refactor to set this at block creation time as | ||
// that would be the logical place for a block size to be calculated. | ||
bs := blockSize(cr, ir, tsr) | ||
meta.Stats.NumBytes = bs | ||
err = writeMetaFile(logger, dir, meta) | ||
if err != nil { | ||
level.Warn(logger).Log("msg", "couldn't write the meta file for the block size", "block", dir, "err", err) | ||
} | ||
|
||
pb = &Block{ | ||
dir: dir, | ||
meta: *meta, | ||
chunkr: cr, | ||
indexr: ir, | ||
tombstones: tr, | ||
symbolTableSize: ir.SymbolTableSize(), | ||
logger: logger, | ||
dir: dir, | ||
meta: *meta, | ||
chunkr: cr, | ||
indexr: ir, | ||
tombstones: tr, | ||
symbolTableSize: ir.SymbolTableSize(), | ||
logger: logger, | ||
numBytesChunks: cr.Size(), | ||
numBytesIndex: ir.Size(), | ||
numBytesTombstone: sizeTomb, | ||
numBytesMeta: sizeMeta, | ||
} | ||
return pb, nil | ||
} | ||
|
||
func blockSize(rr ...SizeReader) int64 { | ||
var total int64 | ||
for _, r := range rr { | ||
if r != nil { | ||
total += r.Size() | ||
} | ||
} | ||
return total | ||
} | ||
|
||
// Close closes the on-disk block. It blocks as long as there are readers reading from the block. | ||
func (pb *Block) Close() error { | ||
pb.mtx.Lock() | ||
|
@@ -390,7 +379,9 @@ func (pb *Block) MinTime() int64 { return pb.meta.MinTime } | |
func (pb *Block) MaxTime() int64 { return pb.meta.MaxTime } | ||
|
||
// Size returns the number of bytes that the block takes up. | ||
func (pb *Block) Size() int64 { return pb.meta.Stats.NumBytes } | ||
func (pb *Block) Size() int64 { | ||
return pb.numBytesChunks + pb.numBytesIndex + pb.numBytesTombstone + pb.numBytesMeta | ||
} | ||
|
||
// ErrClosing is returned when a block is in the process of being closed. | ||
var ErrClosing = errors.New("block is closing") | ||
|
@@ -437,7 +428,9 @@ func (pb *Block) GetSymbolTableSize() uint64 { | |
|
||
func (pb *Block) setCompactionFailed() error { | ||
pb.meta.Compaction.Failed = true | ||
return writeMetaFile(pb.logger, pb.dir, &pb.meta) | ||
n, err := writeMetaFile(pb.logger, pb.dir, &pb.meta) | ||
pb.numBytesMeta = n | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Slight inconsistency with below code. We update size only on no error, here we do it no matter what. Can we do it on no error here as well? (: this will avoid potential confusion. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. updated |
||
return err | ||
} | ||
|
||
type blockIndexReader struct { | ||
|
@@ -561,10 +554,17 @@ Outer: | |
pb.tombstones = stones | ||
pb.meta.Stats.NumTombstones = pb.tombstones.Total() | ||
|
||
if err := writeTombstoneFile(pb.logger, pb.dir, pb.tombstones); err != nil { | ||
n, err := writeTombstoneFile(pb.logger, pb.dir, pb.tombstones) | ||
krasi-georgiev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if err != nil { | ||
return err | ||
} | ||
pb.numBytesTombstone = n | ||
n, err = writeMetaFile(pb.logger, pb.dir, &pb.meta) | ||
if err != nil { | ||
return err | ||
} | ||
return writeMetaFile(pb.logger, pb.dir, &pb.meta) | ||
pb.numBytesMeta = n | ||
return nil | ||
} | ||
|
||
// CleanTombstones will remove the tombstones and rewrite the block (only if there are any tombstones). | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -178,7 +178,7 @@ func (c *LeveledCompactor) Plan(dir string) ([]string, error) { | |
|
||
var dms []dirMeta | ||
for _, dir := range dirs { | ||
meta, err := readMetaFile(dir) | ||
meta, _, err := readMetaFile(dir) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
@@ -380,7 +380,7 @@ func (c *LeveledCompactor) Compact(dest string, dirs []string, open []*Block) (u | |
start := time.Now() | ||
|
||
for _, d := range dirs { | ||
meta, err := readMetaFile(d) | ||
meta, _, err := readMetaFile(d) | ||
if err != nil { | ||
return uid, err | ||
} | ||
|
@@ -420,12 +420,14 @@ func (c *LeveledCompactor) Compact(dest string, dirs []string, open []*Block) (u | |
if meta.Stats.NumSamples == 0 { | ||
for _, b := range bs { | ||
b.meta.Compaction.Deletable = true | ||
if err = writeMetaFile(c.logger, b.dir, &b.meta); err != nil { | ||
n, err := writeMetaFile(c.logger, b.dir, &b.meta) | ||
if err != nil { | ||
level.Error(c.logger).Log( | ||
"msg", "Failed to write 'Deletable' to meta file after compaction", | ||
"ulid", b.meta.ULID, | ||
) | ||
} | ||
b.numBytesMeta = n | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why we set numBytes to new block, but we ignore the other sizes later on? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. actually this is setting the numBytesMeta for the existing blocks. Existing blocks are already open so need to set the size information when it changes. Later on it is creating new blocks and these would have their size calculated when they are open at There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point, LGTM. |
||
} | ||
uid = ulid.ULID{} | ||
level.Info(c.logger).Log( | ||
|
@@ -600,12 +602,12 @@ func (c *LeveledCompactor) write(dest string, meta *BlockMeta, blocks ...BlockRe | |
return nil | ||
} | ||
|
||
if err = writeMetaFile(c.logger, tmp, meta); err != nil { | ||
if _, err = writeMetaFile(c.logger, tmp, meta); err != nil { | ||
return errors.Wrap(err, "write merged meta") | ||
} | ||
|
||
// Create an empty tombstones file. | ||
if err := writeTombstoneFile(c.logger, tmp, newMemTombstones()); err != nil { | ||
if _, err := writeTombstoneFile(c.logger, tmp, newMemTombstones()); err != nil { | ||
return errors.Wrap(err, "write new tombstones file") | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the size is recalculated NOT just when opening a block, but also when calling block.Delete so I prefer to keep this a bit more generic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fair. Can we have then first fix
(not precise)
and thenThis is now dynamically calculated and kept in memory.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what do you mean by `(not precise)'? If that is the missing meta size that it can be something like:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
perfect!