github.com/m3db/m3@v1.5.0/src/dbnode/persist/fs/msgpack/encoder.go (about)

     1  // Copyright (c) 2016 Uber Technologies, Inc
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE
    20  
    21  package msgpack
    22  
    23  import (
    24  	"bytes"
    25  
    26  	"github.com/m3db/m3/src/dbnode/digest"
    27  	"github.com/m3db/m3/src/dbnode/persist/schema"
    28  
    29  	"gopkg.in/vmihailenco/msgpack.v2"
    30  )
    31  
    32  type encodeVersionFn func(value int)
    33  type encodeNumObjectFieldsForFn func(value objectType)
    34  type encodeVarintFn func(value int64)
    35  type encodeVarUintFn func(value uint64)
    36  type encodeFloat64Fn func(value float64)
    37  type encodeBytesFn func(value []byte)
    38  type encodeArrayLenFn func(value int)
    39  
    40  // Encoder encodes data in msgpack format for persistence.
    41  type Encoder struct {
    42  	buf *bytes.Buffer
    43  	enc *msgpack.Encoder
    44  	err error
    45  
    46  	encodeVersionFn            encodeVersionFn
    47  	encodeNumObjectFieldsForFn encodeNumObjectFieldsForFn
    48  	encodeVarintFn             encodeVarintFn
    49  	encodeVarUintFn            encodeVarUintFn
    50  	encodeFloat64Fn            encodeFloat64Fn
    51  	encodeBytesFn              encodeBytesFn
    52  	encodeArrayLenFn           encodeArrayLenFn
    53  
    54  	legacy LegacyEncodingOptions
    55  }
    56  
    57  // LegacyEncodingIndexInfoVersion is the encoding/decoding version to use when processing index info files
    58  type LegacyEncodingIndexInfoVersion int
    59  
    60  const (
    61  	LegacyEncodingIndexVersionCurrent                                = LegacyEncodingIndexVersionV5
    62  	LegacyEncodingIndexVersionV1      LegacyEncodingIndexInfoVersion = iota
    63  	LegacyEncodingIndexVersionV2
    64  	LegacyEncodingIndexVersionV3
    65  	LegacyEncodingIndexVersionV4
    66  	LegacyEncodingIndexVersionV5
    67  )
    68  
    69  // LegacyEncodingIndexEntryVersion is the encoding/decoding version to use when processing index entries
    70  type LegacyEncodingIndexEntryVersion int
    71  
    72  const (
    73  	LegacyEncodingIndexEntryVersionCurrent                                 = LegacyEncodingIndexEntryVersionV3
    74  	LegacyEncodingIndexEntryVersionV1      LegacyEncodingIndexEntryVersion = iota
    75  	LegacyEncodingIndexEntryVersionV2
    76  	LegacyEncodingIndexEntryVersionV3
    77  )
    78  
    79  // LegacyEncodingOptions allows you to specify the version to use when encoding/decoding
    80  // index info and index files
    81  type LegacyEncodingOptions struct {
    82  	EncodeLegacyIndexInfoVersion LegacyEncodingIndexInfoVersion
    83  	DecodeLegacyIndexInfoVersion LegacyEncodingIndexInfoVersion
    84  
    85  	EncodeLegacyIndexEntryVersion LegacyEncodingIndexEntryVersion
    86  	DecodeLegacyIndexEntryVersion LegacyEncodingIndexEntryVersion
    87  }
    88  
    89  // DefaultLegacyEncodingOptions are the default options to use with msgpack.Encoder and msgpack.Decoder.
    90  var DefaultLegacyEncodingOptions = LegacyEncodingOptions{
    91  	EncodeLegacyIndexInfoVersion: LegacyEncodingIndexVersionCurrent,
    92  	DecodeLegacyIndexInfoVersion: LegacyEncodingIndexVersionCurrent,
    93  
    94  	EncodeLegacyIndexEntryVersion: LegacyEncodingIndexEntryVersionCurrent,
    95  	DecodeLegacyIndexEntryVersion: LegacyEncodingIndexEntryVersionCurrent,
    96  }
    97  
    98  // NewEncoder creates a new encoder.
    99  func NewEncoder() *Encoder {
   100  	return newEncoder(DefaultLegacyEncodingOptions)
   101  }
   102  
   103  // NewEncoderWithOptions creates a new encoder with the specified legacy options.
   104  func NewEncoderWithOptions(legacy LegacyEncodingOptions) *Encoder {
   105  	return newEncoder(legacy)
   106  }
   107  
   108  func newEncoder(legacy LegacyEncodingOptions) *Encoder {
   109  	buf := bytes.NewBuffer(nil)
   110  	enc := &Encoder{
   111  		buf: buf,
   112  		enc: msgpack.NewEncoder(buf),
   113  	}
   114  
   115  	enc.encodeVersionFn = enc.encodeVersion
   116  	enc.encodeNumObjectFieldsForFn = enc.encodeNumObjectFieldsFor
   117  	enc.encodeVarintFn = enc.encodeVarint
   118  	enc.encodeVarUintFn = enc.encodeVarUint
   119  	enc.encodeFloat64Fn = enc.encodeFloat64
   120  	enc.encodeBytesFn = enc.encodeBytes
   121  	enc.encodeArrayLenFn = enc.encodeArrayLen
   122  
   123  	// Used primarily for testing however legitimate production uses exist (e.g. addition of IndexEntryChecksum in
   124  	// IndexEntryV3)
   125  	enc.legacy = legacy
   126  
   127  	return enc
   128  }
   129  
   130  // Reset resets the buffer.
   131  func (enc *Encoder) Reset() {
   132  	enc.buf.Truncate(0)
   133  	enc.err = nil
   134  }
   135  
   136  // Bytes returns the encoded bytes.
   137  func (enc *Encoder) Bytes() []byte { return enc.buf.Bytes() }
   138  
   139  // EncodeIndexInfo encodes index info.
   140  func (enc *Encoder) EncodeIndexInfo(info schema.IndexInfo) error {
   141  	if enc.err != nil {
   142  		return enc.err
   143  	}
   144  	enc.encodeRootObject(indexInfoVersion, indexInfoType)
   145  	switch enc.legacy.EncodeLegacyIndexInfoVersion {
   146  	case LegacyEncodingIndexVersionV1:
   147  		enc.encodeIndexInfoV1(info)
   148  	case LegacyEncodingIndexVersionV2:
   149  		enc.encodeIndexInfoV2(info)
   150  	case LegacyEncodingIndexVersionV3:
   151  		enc.encodeIndexInfoV3(info)
   152  	case LegacyEncodingIndexVersionV4:
   153  		enc.encodeIndexInfoV4(info)
   154  	default:
   155  		enc.encodeIndexInfoV5(info)
   156  	}
   157  	return enc.err
   158  }
   159  
   160  // EncodeIndexEntry encodes index entry.
   161  func (enc *Encoder) EncodeIndexEntry(entry schema.IndexEntry) error {
   162  	if enc.err != nil {
   163  		return enc.err
   164  	}
   165  
   166  	// There's no guarantee EncodeIndexEntry is called with an empty buffer so ensure
   167  	// only checksumming the bits we care about.
   168  	checksumStart := enc.buf.Len()
   169  
   170  	enc.encodeRootObject(indexEntryVersion, indexEntryType)
   171  	switch enc.legacy.EncodeLegacyIndexEntryVersion {
   172  	case LegacyEncodingIndexEntryVersionV1:
   173  		enc.encodeIndexEntryV1(entry)
   174  	case LegacyEncodingIndexEntryVersionV2:
   175  		enc.encodeIndexEntryV2(entry)
   176  	default:
   177  		enc.encodeIndexEntryV3(entry, checksumStart)
   178  	}
   179  	return enc.err
   180  }
   181  
   182  // EncodeIndexSummary encodes index summary.
   183  func (enc *Encoder) EncodeIndexSummary(summary schema.IndexSummary) error {
   184  	if enc.err != nil {
   185  		return enc.err
   186  	}
   187  	enc.encodeRootObject(indexSummaryVersion, indexSummaryType)
   188  	enc.encodeIndexSummary(summary)
   189  	return enc.err
   190  }
   191  
   192  // EncodeLogInfo encodes commit log info.
   193  func (enc *Encoder) EncodeLogInfo(info schema.LogInfo) error {
   194  	if enc.err != nil {
   195  		return enc.err
   196  	}
   197  	enc.encodeRootObject(logInfoVersion, logInfoType)
   198  	enc.encodeLogInfo(info)
   199  	return enc.err
   200  }
   201  
   202  // EncodeLogEntry encodes commit log entry.
   203  func (enc *Encoder) EncodeLogEntry(entry schema.LogEntry) error {
   204  	if enc.err != nil {
   205  		return enc.err
   206  	}
   207  	enc.encodeRootObject(logEntryVersion, logEntryType)
   208  	enc.encodeLogEntry(entry)
   209  	return enc.err
   210  }
   211  
   212  // EncodeLogMetadata encodes commit log metadata
   213  func (enc *Encoder) EncodeLogMetadata(entry schema.LogMetadata) error {
   214  	if enc.err != nil {
   215  		return enc.err
   216  	}
   217  	enc.encodeRootObject(logMetadataVersion, logMetadataType)
   218  	enc.encodeLogMetadata(entry)
   219  	return enc.err
   220  }
   221  
   222  // We only keep this method around for the sake of testing
   223  // backwards-compatbility.
   224  func (enc *Encoder) encodeIndexInfoV1(info schema.IndexInfo) {
   225  	// Manually encode num fields for testing purposes.
   226  	enc.encodeArrayLenFn(6) // V1 had 6 fields.
   227  	enc.encodeVarintFn(info.BlockStart)
   228  	enc.encodeVarintFn(info.BlockSize)
   229  	enc.encodeVarintFn(info.Entries)
   230  	enc.encodeVarintFn(info.MajorVersion)
   231  	enc.encodeIndexSummariesInfo(info.Summaries)
   232  	enc.encodeIndexBloomFilterInfo(info.BloomFilter)
   233  }
   234  
   235  // We only keep this method around for the sake of testing
   236  // backwards-compatbility.
   237  func (enc *Encoder) encodeIndexInfoV2(info schema.IndexInfo) {
   238  	// Manually encode num fields for testing purposes.
   239  	enc.encodeArrayLenFn(8) // V2 had 8 fields.
   240  	enc.encodeVarintFn(info.BlockStart)
   241  	enc.encodeVarintFn(info.BlockSize)
   242  	enc.encodeVarintFn(info.Entries)
   243  	enc.encodeVarintFn(info.MajorVersion)
   244  	enc.encodeIndexSummariesInfo(info.Summaries)
   245  	enc.encodeIndexBloomFilterInfo(info.BloomFilter)
   246  	enc.encodeVarintFn(info.SnapshotTime)
   247  	enc.encodeVarintFn(int64(info.FileType))
   248  }
   249  
   250  // We only keep this method around for the sake of testing
   251  // backwards-compatbility.
   252  func (enc *Encoder) encodeIndexInfoV3(info schema.IndexInfo) {
   253  	// Manually encode num fields for testing purposes.
   254  	enc.encodeArrayLenFn(9) // V3 had 9 fields.
   255  	enc.encodeVarintFn(info.BlockStart)
   256  	enc.encodeVarintFn(info.BlockSize)
   257  	enc.encodeVarintFn(info.Entries)
   258  	enc.encodeVarintFn(info.MajorVersion)
   259  	enc.encodeIndexSummariesInfo(info.Summaries)
   260  	enc.encodeIndexBloomFilterInfo(info.BloomFilter)
   261  	enc.encodeVarintFn(info.SnapshotTime)
   262  	enc.encodeVarintFn(int64(info.FileType))
   263  	enc.encodeBytesFn(info.SnapshotID)
   264  }
   265  
   266  func (enc *Encoder) encodeIndexInfoV4(info schema.IndexInfo) {
   267  	enc.encodeArrayLenFn(10) // V4 had 10 fields.
   268  	enc.encodeVarintFn(info.BlockStart)
   269  	enc.encodeVarintFn(info.BlockSize)
   270  	enc.encodeVarintFn(info.Entries)
   271  	enc.encodeVarintFn(info.MajorVersion)
   272  	enc.encodeIndexSummariesInfo(info.Summaries)
   273  	enc.encodeIndexBloomFilterInfo(info.BloomFilter)
   274  	enc.encodeVarintFn(info.SnapshotTime)
   275  	enc.encodeVarintFn(int64(info.FileType))
   276  	enc.encodeBytesFn(info.SnapshotID)
   277  	enc.encodeVarintFn(int64(info.VolumeIndex))
   278  }
   279  
   280  func (enc *Encoder) encodeIndexInfoV5(info schema.IndexInfo) {
   281  	enc.encodeNumObjectFieldsForFn(indexInfoType)
   282  	enc.encodeVarintFn(info.BlockStart)
   283  	enc.encodeVarintFn(info.BlockSize)
   284  	enc.encodeVarintFn(info.Entries)
   285  	enc.encodeVarintFn(info.MajorVersion)
   286  	enc.encodeIndexSummariesInfo(info.Summaries)
   287  	enc.encodeIndexBloomFilterInfo(info.BloomFilter)
   288  	enc.encodeVarintFn(info.SnapshotTime)
   289  	enc.encodeVarintFn(int64(info.FileType))
   290  	enc.encodeBytesFn(info.SnapshotID)
   291  	enc.encodeVarintFn(int64(info.VolumeIndex))
   292  	enc.encodeVarintFn(info.MinorVersion)
   293  }
   294  
   295  func (enc *Encoder) encodeIndexSummariesInfo(info schema.IndexSummariesInfo) {
   296  	enc.encodeNumObjectFieldsForFn(indexSummariesInfoType)
   297  	enc.encodeVarintFn(info.Summaries)
   298  }
   299  
   300  func (enc *Encoder) encodeIndexBloomFilterInfo(info schema.IndexBloomFilterInfo) {
   301  	enc.encodeNumObjectFieldsForFn(indexBloomFilterInfoType)
   302  	enc.encodeVarintFn(info.NumElementsM)
   303  	enc.encodeVarintFn(info.NumHashesK)
   304  }
   305  
   306  // We only keep this method around for the sake of testing
   307  // backwards-compatbility.
   308  func (enc *Encoder) encodeIndexEntryV1(entry schema.IndexEntry) {
   309  	// Manually encode num fields for testing purposes.
   310  	enc.encodeArrayLenFn(5) // V1 had 5 fields.
   311  	enc.encodeVarintFn(entry.Index)
   312  	enc.encodeBytesFn(entry.ID)
   313  	enc.encodeVarintFn(entry.Size)
   314  	enc.encodeVarintFn(entry.Offset)
   315  	enc.encodeVarintFn(entry.DataChecksum)
   316  }
   317  
   318  func (enc *Encoder) encodeIndexEntryV2(entry schema.IndexEntry) {
   319  	enc.encodeArrayLenFn(6) // V2 had 6 fields.
   320  	enc.encodeVarintFn(entry.Index)
   321  	enc.encodeBytesFn(entry.ID)
   322  	enc.encodeVarintFn(entry.Size)
   323  	enc.encodeVarintFn(entry.Offset)
   324  	enc.encodeVarintFn(entry.DataChecksum)
   325  	enc.encodeBytesFn(entry.EncodedTags)
   326  }
   327  
   328  func (enc *Encoder) encodeIndexEntryV3(entry schema.IndexEntry, checksumStart int) {
   329  	enc.encodeNumObjectFieldsForFn(indexEntryType)
   330  	enc.encodeVarintFn(entry.Index)
   331  	enc.encodeBytesFn(entry.ID)
   332  	enc.encodeVarintFn(entry.Size)
   333  	enc.encodeVarintFn(entry.Offset)
   334  	enc.encodeVarintFn(entry.DataChecksum)
   335  	enc.encodeBytesFn(entry.EncodedTags)
   336  
   337  	checksum := digest.Checksum(enc.Bytes()[checksumStart:])
   338  	enc.encodeVarintFn(int64(checksum))
   339  }
   340  
   341  func (enc *Encoder) encodeIndexSummary(summary schema.IndexSummary) {
   342  	enc.encodeNumObjectFieldsForFn(indexSummaryType)
   343  	enc.encodeVarintFn(summary.Index)
   344  	enc.encodeBytesFn(summary.ID)
   345  	enc.encodeVarintFn(summary.IndexEntryOffset)
   346  }
   347  
   348  func (enc *Encoder) encodeLogInfo(info schema.LogInfo) {
   349  	enc.encodeNumObjectFieldsForFn(logInfoType)
   350  
   351  	// Deprecated, have to encode anyways for backwards compatibility, but we ignore the values.
   352  	// TODO(V1): Remove when we make backwards incompatible changes with an upgrade to V1.
   353  	enc.encodeVarintFn(info.DeprecatedDoNotUseStart)
   354  	enc.encodeVarintFn(info.DeprecatedDoNotUseDuration)
   355  
   356  	enc.encodeVarintFn(info.Index)
   357  }
   358  
   359  func (enc *Encoder) encodeLogEntry(entry schema.LogEntry) {
   360  	enc.encodeNumObjectFieldsForFn(logEntryType)
   361  	// Encode the index first because the commitlog reader needs this information first
   362  	// to distribute the rest of the decoding to a group of workers.
   363  	enc.encodeVarUintFn(entry.Index)
   364  	enc.encodeVarintFn(entry.Create)
   365  	enc.encodeBytesFn(entry.Metadata)
   366  	enc.encodeVarintFn(entry.Timestamp)
   367  	enc.encodeFloat64Fn(entry.Value)
   368  	enc.encodeVarUintFn(uint64(entry.Unit))
   369  	enc.encodeBytesFn(entry.Annotation)
   370  }
   371  
   372  func (enc *Encoder) encodeLogMetadata(metadata schema.LogMetadata) {
   373  	enc.encodeNumObjectFieldsForFn(logMetadataType)
   374  	enc.encodeBytesFn(metadata.ID)
   375  	enc.encodeBytesFn(metadata.Namespace)
   376  	enc.encodeVarUintFn(uint64(metadata.Shard))
   377  	enc.encodeBytesFn(metadata.EncodedTags)
   378  }
   379  
   380  func (enc *Encoder) encodeRootObject(version int, objType objectType) {
   381  	enc.encodeVersionFn(version)
   382  	enc.encodeNumObjectFieldsForFn(rootObjectType)
   383  	enc.encodeObjectType(objType)
   384  }
   385  
   386  func (enc *Encoder) encodeVersion(version int) {
   387  	enc.encodeVarintFn(int64(version))
   388  }
   389  
   390  func (enc *Encoder) encodeNumObjectFieldsFor(objType objectType) {
   391  	_, curr := numFieldsForType(objType)
   392  	enc.encodeArrayLenFn(curr)
   393  }
   394  
   395  func (enc *Encoder) encodeObjectType(objType objectType) {
   396  	enc.encodeVarintFn(int64(objType))
   397  }
   398  
   399  func (enc *Encoder) encodeVarint(value int64) {
   400  	if enc.err != nil {
   401  		return
   402  	}
   403  	enc.err = enc.enc.EncodeInt64(value)
   404  }
   405  
   406  func (enc *Encoder) encodeVarUint(value uint64) {
   407  	if enc.err != nil {
   408  		return
   409  	}
   410  	enc.err = enc.enc.EncodeUint64(value)
   411  }
   412  
   413  func (enc *Encoder) encodeFloat64(value float64) {
   414  	if enc.err != nil {
   415  		return
   416  	}
   417  	enc.err = enc.enc.EncodeFloat64(value)
   418  }
   419  
   420  func (enc *Encoder) encodeBytes(value []byte) {
   421  	if enc.err != nil {
   422  		return
   423  	}
   424  	enc.err = enc.enc.EncodeBytes(value)
   425  }
   426  
   427  func (enc *Encoder) encodeArrayLen(value int) {
   428  	if enc.err != nil {
   429  		return
   430  	}
   431  	enc.err = enc.enc.EncodeArrayLen(value)
   432  }