influxdb/tsdb/engine/tsm1/digest_writer.go

138 lines
3.0 KiB
Go

package tsm1
import (
"encoding/binary"
"encoding/json"
"errors"
"io"
"github.com/golang/snappy"
)
var (
// ErrNoDigestManifest is returned if an attempt is made to write other parts of a
// digest before writing the manifest.
ErrNoDigestManifest = errors.New("no digest manifest")
// ErrDigestAlreadyWritten is returned if the client attempts to write more than
// one manifest.
ErrDigestAlreadyWritten = errors.New("digest manifest already written")
)
// DigestWriter allows for writing a digest of a shard. A digest is a condensed
// representation of the contents of a shard. It can be scoped to one or more series
// keys, ranges of times or sets of files.
type DigestWriter struct {
w io.WriteCloser
sw *snappy.Writer
manifestWritten bool
}
func NewDigestWriter(w io.WriteCloser) (*DigestWriter, error) {
return &DigestWriter{w: w, sw: snappy.NewBufferedWriter(w)}, nil
}
func (w *DigestWriter) WriteManifest(m *DigestManifest) error {
if w.manifestWritten {
return ErrDigestAlreadyWritten
}
b, err := json.Marshal(m)
if err != nil {
return err
}
// Write length of manifest.
if err := binary.Write(w.sw, binary.BigEndian, uint32(len(b))); err != nil {
return err
}
// Write manifest.
if _, err = w.sw.Write(b); err != nil {
return err
}
w.manifestWritten = true
return err
}
func (w *DigestWriter) WriteTimeSpan(key string, t *DigestTimeSpan) error {
if !w.manifestWritten {
return ErrNoDigestManifest
}
if err := binary.Write(w.sw, binary.BigEndian, uint16(len(key))); err != nil {
return err
}
if _, err := w.sw.Write([]byte(key)); err != nil {
return err
}
if err := binary.Write(w.sw, binary.BigEndian, uint32(t.Len())); err != nil {
return err
}
for _, tr := range t.Ranges {
if err := binary.Write(w.sw, binary.BigEndian, tr.Min); err != nil {
return err
}
if err := binary.Write(w.sw, binary.BigEndian, tr.Max); err != nil {
return err
}
if err := binary.Write(w.sw, binary.BigEndian, tr.CRC); err != nil {
return err
}
if err := binary.Write(w.sw, binary.BigEndian, uint16(tr.N)); err != nil {
return err
}
}
return nil
}
func (w *DigestWriter) Flush() error {
return w.sw.Flush()
}
func (w *DigestWriter) Close() error {
if err := w.Flush(); err != nil {
return err
}
if err := w.sw.Close(); err != nil {
return err
}
return w.w.Close()
}
type DigestTimeSpan struct {
Ranges []DigestTimeRange
}
func (a DigestTimeSpan) Len() int { return len(a.Ranges) }
func (a DigestTimeSpan) Swap(i, j int) { a.Ranges[i], a.Ranges[j] = a.Ranges[j], a.Ranges[i] }
func (a DigestTimeSpan) Less(i, j int) bool {
return a.Ranges[i].Min < a.Ranges[j].Min
}
func (t *DigestTimeSpan) Add(min, max int64, n int, crc uint32) {
for _, v := range t.Ranges {
if v.Min == min && v.Max == max && v.N == n && v.CRC == crc {
return
}
}
t.Ranges = append(t.Ranges, DigestTimeRange{Min: min, Max: max, N: n, CRC: crc})
}
type DigestTimeRange struct {
Min, Max int64
N int
CRC uint32
}