2019-05-22 21:16:55 +00:00
|
|
|
package storage
|
|
|
|
|
|
|
|
import (
|
2022-12-05 23:27:57 +00:00
|
|
|
"fmt"
|
|
|
|
"path/filepath"
|
|
|
|
|
2019-05-22 21:16:55 +00:00
|
|
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/bytesutil"
|
2021-07-06 13:28:39 +00:00
|
|
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/cgroup"
|
2020-05-14 19:01:51 +00:00
|
|
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/fasttime"
|
2022-12-05 23:27:57 +00:00
|
|
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/fs"
|
2019-05-22 21:16:55 +00:00
|
|
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
|
|
|
|
)
|
|
|
|
|
|
|
|
// inmemoryPart represents in-memory partition.
|
|
|
|
type inmemoryPart struct {
|
|
|
|
ph partHeader
|
|
|
|
|
|
|
|
timestampsData bytesutil.ByteBuffer
|
|
|
|
valuesData bytesutil.ByteBuffer
|
|
|
|
indexData bytesutil.ByteBuffer
|
|
|
|
metaindexData bytesutil.ByteBuffer
|
|
|
|
|
2020-05-14 19:01:51 +00:00
|
|
|
creationTime uint64
|
2019-05-22 21:16:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Reset resets mp.
|
|
|
|
func (mp *inmemoryPart) Reset() {
|
|
|
|
mp.ph.Reset()
|
|
|
|
|
|
|
|
mp.timestampsData.Reset()
|
|
|
|
mp.valuesData.Reset()
|
|
|
|
mp.indexData.Reset()
|
|
|
|
mp.metaindexData.Reset()
|
|
|
|
|
2020-05-14 19:01:51 +00:00
|
|
|
mp.creationTime = 0
|
2019-05-22 21:16:55 +00:00
|
|
|
}
|
|
|
|
|
2022-12-05 23:27:57 +00:00
|
|
|
// StoreToDisk stores the mp to the given path on disk.
|
|
|
|
func (mp *inmemoryPart) StoreToDisk(path string) error {
|
|
|
|
if err := fs.MkdirAllIfNotExist(path); err != nil {
|
|
|
|
return fmt.Errorf("cannot create directory %q: %w", path, err)
|
|
|
|
}
|
2023-03-25 21:33:54 +00:00
|
|
|
timestampsPath := filepath.Join(path, timestampsFilename)
|
2022-12-05 23:27:57 +00:00
|
|
|
if err := fs.WriteFileAndSync(timestampsPath, mp.timestampsData.B); err != nil {
|
|
|
|
return fmt.Errorf("cannot store timestamps: %w", err)
|
|
|
|
}
|
2023-03-25 21:33:54 +00:00
|
|
|
valuesPath := filepath.Join(path, valuesFilename)
|
2022-12-05 23:27:57 +00:00
|
|
|
if err := fs.WriteFileAndSync(valuesPath, mp.valuesData.B); err != nil {
|
|
|
|
return fmt.Errorf("cannot store values: %w", err)
|
|
|
|
}
|
2023-03-25 21:33:54 +00:00
|
|
|
indexPath := filepath.Join(path, indexFilename)
|
2022-12-05 23:27:57 +00:00
|
|
|
if err := fs.WriteFileAndSync(indexPath, mp.indexData.B); err != nil {
|
|
|
|
return fmt.Errorf("cannot store index: %w", err)
|
|
|
|
}
|
2023-03-25 21:33:54 +00:00
|
|
|
metaindexPath := filepath.Join(path, metaindexFilename)
|
2022-12-05 23:27:57 +00:00
|
|
|
if err := fs.WriteFileAndSync(metaindexPath, mp.metaindexData.B); err != nil {
|
|
|
|
return fmt.Errorf("cannot store metaindex: %w", err)
|
|
|
|
}
|
2023-03-19 08:36:05 +00:00
|
|
|
if err := mp.ph.WriteMetadata(path); err != nil {
|
|
|
|
return fmt.Errorf("cannot store metadata: %w", err)
|
2022-12-05 23:27:57 +00:00
|
|
|
}
|
2023-04-14 04:18:39 +00:00
|
|
|
fs.MustSyncPath(path)
|
|
|
|
// Do not sync parent directory - it must be synced by the caller.
|
2022-12-05 23:27:57 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-05-22 21:16:55 +00:00
|
|
|
// InitFromRows initializes mp from the given rows.
|
|
|
|
func (mp *inmemoryPart) InitFromRows(rows []rawRow) {
|
|
|
|
if len(rows) == 0 {
|
|
|
|
logger.Panicf("BUG: Inmemory.InitFromRows must accept at least one row")
|
|
|
|
}
|
|
|
|
|
|
|
|
mp.Reset()
|
|
|
|
rrm := getRawRowsMarshaler()
|
|
|
|
rrm.marshalToInmemoryPart(mp, rows)
|
|
|
|
putRawRowsMarshaler(rrm)
|
2020-05-14 19:01:51 +00:00
|
|
|
mp.creationTime = fasttime.UnixTimestamp()
|
2019-05-22 21:16:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewPart creates new part from mp.
|
|
|
|
//
|
|
|
|
// It is safe calling NewPart multiple times.
|
|
|
|
// It is unsafe re-using mp while the returned part is in use.
|
|
|
|
func (mp *inmemoryPart) NewPart() (*part, error) {
|
2022-12-04 06:30:30 +00:00
|
|
|
size := mp.size()
|
|
|
|
return newPart(&mp.ph, "", size, mp.metaindexData.NewReader(), &mp.timestampsData, &mp.valuesData, &mp.indexData)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (mp *inmemoryPart) size() uint64 {
|
|
|
|
return uint64(cap(mp.timestampsData.B) + cap(mp.valuesData.B) + cap(mp.indexData.B) + cap(mp.metaindexData.B))
|
2019-05-22 21:16:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func getInmemoryPart() *inmemoryPart {
|
2021-07-06 13:28:39 +00:00
|
|
|
select {
|
|
|
|
case mp := <-mpPool:
|
|
|
|
return mp
|
|
|
|
default:
|
2019-05-22 21:16:55 +00:00
|
|
|
return &inmemoryPart{}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func putInmemoryPart(mp *inmemoryPart) {
|
|
|
|
mp.Reset()
|
2021-07-06 13:28:39 +00:00
|
|
|
select {
|
|
|
|
case mpPool <- mp:
|
|
|
|
default:
|
|
|
|
// Drop mp in order to reduce memory usage.
|
|
|
|
}
|
2019-05-22 21:16:55 +00:00
|
|
|
}
|
|
|
|
|
2021-07-06 13:28:39 +00:00
|
|
|
// Use chan instead of sync.Pool in order to reduce memory usage on systems with big number of CPU cores,
|
|
|
|
// since sync.Pool maintains per-CPU pool of inmemoryPart objects.
|
|
|
|
//
|
|
|
|
// The inmemoryPart object size can exceed 64KB, so it is better to use chan instead of sync.Pool for reducing memory usage.
|
|
|
|
var mpPool = make(chan *inmemoryPart, cgroup.AvailableCPUs())
|