github.com/m3db/m3@v1.5.0/src/dbnode/persist/fs/msgpack/schema.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  // Instructions for adding a new field to an existing object in a backwards
    22  // and forwards compatible way:
    23  // 		1. Do not change the objects type
    24  // 		2. Do not change the objects version
    25  // 		3. Modify the encoder to encode the new fields
    26  // 		4. Increase the constant below for the current (not minimum!) number
    27  // 		   of fields for that object type
    28  // 		5. Modify the decoder to selectively decode the new fields based on
    29  // 		   the actual number of fields that are encoded in the file - See the
    30  // 		   decodeIndexInfo function for an example
    31  // 		6. Write forwards/backwards compatibility tests, below are examples:
    32  // 				- TestIndexInfoRoundTripBackwardsCompatibilityV1
    33  // 				- TestIndexInfoRoundTripForwardsCompatibilityV2
    34  
    35  package msgpack
    36  
    37  import "fmt"
    38  
    39  const (
    40  	// Incrementing any of these values is a backwards-compatible change
    41  	// I.E the new binary will still be able to read old files (as long
    42  	// as the minimum number of fields for a given object is not also
    43  	// increased, see the comment below), but it is not a forwards-compatible
    44  	// change I.E old binaries will not be able to read the new files and just
    45  	// skip over any unrecognized fields (which they will do if this value is
    46  	// not incremented when adding new fields)
    47  	indexInfoVersion    = 1
    48  	indexEntryVersion   = 1
    49  	indexSummaryVersion = 1
    50  	logInfoVersion      = 1
    51  	logEntryVersion     = 1
    52  	logMetadataVersion  = 1
    53  )
    54  
    55  type objectType int
    56  
    57  // nolint: varcheck, unused
    58  const (
    59  	// Adding any new object types is a backwards-compatible change I.E
    60  	// the new binary will still be able to read old files, but it is
    61  	// not a forwards-compatible change I.E old binaries will not be
    62  	// able to read the new files.
    63  	unknownType objectType = iota
    64  	rootObjectType
    65  	indexInfoType
    66  	indexSummariesInfoType
    67  	indexBloomFilterInfoType
    68  	indexEntryType
    69  	indexSummaryType
    70  	logInfoType
    71  	logEntryType
    72  	logMetadataType
    73  
    74  	// Total number of object types
    75  	numObjectTypes = iota
    76  )
    77  
    78  const (
    79  	// min number of fields specifies the minimum number of fields that an
    80  	// object must have such that the decoder won't reject it. This value
    81  	// should be equal to the number of fields in objects that were encoded
    82  	// previously that we still want new binaries to be able to read without
    83  	// complaint. I.E if previous versions of M3DB wrote 4 fields for an object,
    84  	// and an even earlier version only wrote 3 fields, than the minimum number
    85  	// should be 3 if we intend to continue reading files that were written by
    86  	// the version that only encoded 3 fields.
    87  	minNumRootObjectFields           = 2
    88  	minNumIndexInfoFields            = 6
    89  	minNumIndexSummariesInfoFields   = 1
    90  	minNumIndexBloomFilterInfoFields = 2
    91  	minNumIndexEntryFields           = 5
    92  	minNumIndexSummaryFields         = 3
    93  	minNumLogInfoFields              = 3
    94  	minNumLogEntryFields             = 7
    95  	minNumLogMetadataFields          = 3
    96  
    97  	// curr number of fields specifies the number of fields that the current
    98  	// version of the M3DB will encode. This is used to ensure that the
    99  	// correct number of fields is encoded into the files. These values need
   100  	// to be incremented whenever we add new fields to an object.
   101  	currNumRootObjectFields           = 2
   102  	currNumIndexInfoFields            = 11
   103  	currNumIndexSummariesInfoFields   = 1
   104  	currNumIndexBloomFilterInfoFields = 2
   105  	currNumIndexEntryFields           = 7
   106  	currNumIndexSummaryFields         = 3
   107  	currNumLogInfoFields              = 3
   108  	currNumLogEntryFields             = 7
   109  	currNumLogMetadataFields          = 3
   110  )
   111  
   112  var (
   113  	minNumObjectFields  []int
   114  	currNumObjectFields []int
   115  
   116  	logEntryHeader    []byte
   117  	logEntryHeaderErr error
   118  
   119  	logMetadataHeader    []byte
   120  	logMetadataHeaderErr error
   121  )
   122  
   123  func numFieldsForType(objType objectType) (min, curr int) {
   124  	return minNumObjectFields[int(objType)-1], currNumObjectFields[int(objType)-1]
   125  }
   126  
   127  func setMinNumObjectFieldsForType(objType objectType, numFields int) {
   128  	minNumObjectFields[int(objType)-1] = numFields
   129  }
   130  
   131  func setCurrNumObjectFieldsForType(objType objectType, numFields int) {
   132  	currNumObjectFields[int(objType)-1] = numFields
   133  }
   134  
   135  func init() {
   136  	minNumObjectFields = make([]int, int(numObjectTypes))
   137  	currNumObjectFields = make([]int, int(numObjectTypes))
   138  
   139  	setMinNumObjectFieldsForType(rootObjectType, minNumRootObjectFields)
   140  	setMinNumObjectFieldsForType(indexInfoType, minNumIndexInfoFields)
   141  	setMinNumObjectFieldsForType(indexSummariesInfoType, minNumIndexSummariesInfoFields)
   142  	setMinNumObjectFieldsForType(indexBloomFilterInfoType, minNumIndexBloomFilterInfoFields)
   143  	setMinNumObjectFieldsForType(indexEntryType, minNumIndexEntryFields)
   144  	setMinNumObjectFieldsForType(indexSummaryType, minNumIndexSummaryFields)
   145  	setMinNumObjectFieldsForType(logInfoType, minNumLogInfoFields)
   146  	setMinNumObjectFieldsForType(logEntryType, minNumLogEntryFields)
   147  	setMinNumObjectFieldsForType(logMetadataType, minNumLogMetadataFields)
   148  
   149  	// Verify all current values are larger than their respective minimum values
   150  	mustBeGreaterThanOrEqual(currNumRootObjectFields, minNumRootObjectFields)
   151  	mustBeGreaterThanOrEqual(currNumIndexInfoFields, minNumIndexInfoFields)
   152  	mustBeGreaterThanOrEqual(currNumIndexSummariesInfoFields, minNumIndexSummariesInfoFields)
   153  	mustBeGreaterThanOrEqual(currNumIndexBloomFilterInfoFields, minNumIndexBloomFilterInfoFields)
   154  	mustBeGreaterThanOrEqual(currNumIndexEntryFields, minNumIndexEntryFields)
   155  	mustBeGreaterThanOrEqual(currNumIndexSummaryFields, minNumIndexSummaryFields)
   156  	mustBeGreaterThanOrEqual(currNumLogInfoFields, minNumLogInfoFields)
   157  	mustBeGreaterThanOrEqual(currNumLogEntryFields, minNumLogEntryFields)
   158  	mustBeGreaterThanOrEqual(currNumLogMetadataFields, minNumLogMetadataFields)
   159  
   160  	setCurrNumObjectFieldsForType(rootObjectType, currNumRootObjectFields)
   161  	setCurrNumObjectFieldsForType(indexInfoType, currNumIndexInfoFields)
   162  	setCurrNumObjectFieldsForType(indexSummariesInfoType, currNumIndexSummariesInfoFields)
   163  	setCurrNumObjectFieldsForType(indexBloomFilterInfoType, currNumIndexBloomFilterInfoFields)
   164  	setCurrNumObjectFieldsForType(indexEntryType, currNumIndexEntryFields)
   165  	setCurrNumObjectFieldsForType(indexSummaryType, currNumIndexSummaryFields)
   166  	setCurrNumObjectFieldsForType(logInfoType, currNumLogInfoFields)
   167  	setCurrNumObjectFieldsForType(logEntryType, currNumLogEntryFields)
   168  	setCurrNumObjectFieldsForType(logMetadataType, currNumLogMetadataFields)
   169  
   170  	// Populate the fixed commit log entry header
   171  	encoder := NewEncoder()
   172  	encoder.encodeRootObject(logEntryVersion, logEntryType)
   173  	encoder.encodeNumObjectFieldsForFn(logEntryType)
   174  	logEntryHeader = encoder.Bytes()
   175  	logEntryHeaderErr = encoder.err
   176  
   177  	// Populate the fixed commit log metadata header
   178  	encoder = NewEncoder()
   179  	encoder.encodeRootObject(logMetadataVersion, logMetadataType)
   180  	encoder.encodeNumObjectFieldsForFn(logMetadataType)
   181  	logMetadataHeader = encoder.Bytes()
   182  	logMetadataHeaderErr = encoder.err
   183  }
   184  
   185  func mustBeGreaterThanOrEqual(x, y int) {
   186  	if x < y {
   187  		panic(fmt.Sprintf("expected %d to be greater than or equal to %d", x, y))
   188  	}
   189  }