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 }