github.com/cockroachdb/pebble@v0.0.0-20231214172447-ab4952c5f87b/docs/RFCS/20220112_pebble_sstable_format_versions.md (about) 1 - Feature Name: Pebble SSTable Format Versions 2 - Status: completed 3 - Start Date: 2022-01-12 4 - Authors: Nick Travers 5 - RFC PR: https://github.com/cockroachdb/pebble/pull/1450 6 - Pebble Issues: 7 https://github.com/cockroachdb/pebble/issues/1409 8 https://github.com/cockroachdb/pebble/issues/1339 9 - Cockroach Issues: 10 11 # Summary 12 13 To safely support changes to the SSTable structure, a new versioning scheme 14 under a Pebble magic number is proposed. 15 16 This RFC also outlines the relationship between the SSTable format version and 17 the existing Pebble format major version, in addition to how the two are to 18 be used in Cockroach for safely enabling new table format versions. 19 20 # Motivation 21 22 Pebble currently uses a "format major version" scheme for the store (or DB) 23 that indicates which Pebble features should be enabled when the store is first 24 opened, before any SSTables are opened. The versions indicate points of 25 backwards incompatibility for a store. For example, the introduction of the 26 `SetWithDelete` key kind is gated behind a version, as is block property 27 collection. This format major version scheme was introduced in 28 [#1227](https://github.com/cockroachdb/pebble/issues/1227). 29 30 While Pebble can use the format major version to infer how to load and 31 interpret data in the LSM, the SSTables that make up the store itself have 32 their own notion of a "version". This "SSTable version" (also referred to as a 33 "table format") is written to the footer (or trailing section) of each SSTable 34 file and determines how the file is to be interpreted by Pebble. As of the time 35 of writing, Pebble supports two table formats - LevelDB's format, and RocksDB's 36 v2 format. Pebble inherited the latter as the default table format as it was 37 the version that RocksDB used at the time Pebble was being developed, and 38 remained the default to allow for a simpler migration path from Cockroach 39 clusters that were originally using RocksDB as the storage engine. The 40 RocksDBv2 table format adds various features on top of the LevelDB format, 41 including a two-level index, configurable checksum algorithms, and an explicit 42 versioning scheme to allow for the introduction of changes, amongst other 43 features. 44 45 While the RocksDBv2 SSTable format has been sufficient for Pebble's needs since 46 inception, new Pebble features and potential backports from RocksDB itself 47 require that the SSTable format evolve over time and therefore that the table 48 format be updated. As the majority of new features added over time will be 49 specific to Pebble, it does not make sense to repurpose the RocksDB format 50 versions that exist upstream for use with Pebble features (at the time of 51 writing, RocksDB had added versions 3 and 4 on top of the version 2 in use by 52 Pebble). A new Pebble-specific table format scheme is proposed. 53 54 In the context of a distributed system such as Cockroach, certain SSTable 55 features are backwards incompatible (e.g. the block property collection and 56 filtering feature extends the RocksDBv2 SSTable block index format to encoded 57 various block properties, which is a breaking change). Participants must 58 _first_ ensure that their stores have the code-level features available to read 59 and write these newer SSTables (indicated by Pebble's format major version). 60 Once all stores agree that they are running the minimum Pebble format major 61 version and will not roll back (e.g. Cockroach cluster version finalization), 62 SSTables can be written and read using more recent table formats. The Pebble 63 "format major version" and "table format version" are therefore no longer 64 independent - the former implies an upper bound on the latter. 65 66 Additionally, certain SSTable generation operations are independent of a 67 specific Pebble instance. For example, SSTable construction for the purposes of 68 backup and restore generates SSTables that are stored external to a specific 69 Pebble store (e.g. in cloud storage) can be used at a later point in time to 70 restore a store. SSTables constructed for such purposes must be carefully 71 versioned to ensure compatibility with existing clusters that may run with a 72 mixture of Pebble versions. 73 74 As a real-world example of the need for the above, consider two Cockroach nodes 75 each with a Pebble store, one at version A, the other at version B (version A 76 (newer) > B (older)). Store A constructs an SSTable for an external backup 77 containing a newer block index format (for block property collection). This 78 SSTable is then imported in to store B. Store B fails to read the SSTable as it 79 is not running with a format major version recent enough make sense of the 80 newer index format. The two stores require a method for agreeing on a minimum 81 supported table format. 82 83 The remainder of this document outlines a new table format for Pebble. This new 84 table format will be used for new table-level features such as block properties 85 and range keys (see 86 [#1339](https://github.com/cockroachdb/pebble/issues/1339)), but also for 87 backporting table-level features from RocksDB that would be useful to Pebble 88 (e.g. version 3 avoids encoding sequence numbers in the index, and version 4 89 uses delta encoding for the block offsets in the index, both of which are 90 useful for Pebble). 91 92 # Technical design 93 94 ## Pebble magic number 95 96 The last 8 bytes of an SSTable is referred to as the "magic number". 97 98 LevelDB uses the first 8 bytes of the SHA1 hash of the string 99 `http://code.google.com/p/leveldb/` for the magic number. 100 101 RocksDB uses its own magic number, which indicates the use of a slightly 102 different table layout - the footer (the name for the end of an SSTable) is 103 slightly larger to accommodate a 32-bit version number and 8 bits for a 104 checksum type to be used for all blocks in the SSTable. 105 106 A new 8-byte magic number will be introduced for Pebble: 107 108 ``` 109 \xf0\x9f\xaa\xb3\xf0\x9f\xaa\xb3 // 🪳🪳 110 ``` 111 112 ## Pebble version scheme 113 114 Tables with a Pebble magic number will use a dedicated versioning scheme, 115 starting with version `1`. No new versions other than version `2` will be 116 supported for tables containing the RocksDB magic number. 117 118 The choice of switching to a Pebble versioning scheme starting `1` simplifies 119 the implementation. Essentially all existing Pebble stores are managed via 120 Cockroach, and were either previously using RocksDB and migrated to Pebble, or 121 were created with Pebble stores. In both situations the table format used is 122 RocksDB v2. 123 124 Given that Pebble has not needed (and likely will not need) to support other 125 RocksDB table formats, it is reasonable to introduce a new magic number for 126 Pebble and reset the version counter to v1. 127 128 The following initial versions will correspond to the following new Pebble 129 features, that have yet to be introduced to Cockroach clusters as of the time 130 of writing: 131 132 - Version 1: block property collectors (block properties are encoded into the 133 block index) 134 - Version 2: range keys (a new block is present in the table for range keys). 135 136 Subsequent alterations to the SSTable format should only increment the _Pebble 137 version number_. It should be noted that backported RocksDB table format 138 features (e.g. RocksDB versions 3 and 4) would use a different version number, 139 within the Pebble version sequence. While possibly confusing, the RocksDB 140 features are being "adopted" by Pebble, rather than directly ported, so a 141 Pebble specific version number is appropriate. 142 143 An alternative would be to allow RocksDB table format features to be backported 144 into Pebble under their existing RocksDB magic number, _alongside_ 145 Pebble-specific features. The complexity required to determine the set of 146 characteristics to read and write to each SSTable would increase with such a 147 scheme, compared to the simpler "linear history" approach described above, 148 where new features simply ratchet the Pebble table format version number. 149 150 ## Footer format 151 152 The footer format for SSTables with Pebble magic numbers _will remain the same_ 153 as the RocksDB footer format - specifically, the trailing 53-bytes of the 154 SSTable consisting of the following fields with the given indices, 155 little-endian encoded: 156 157 - `0`: Checksum type 158 - `1-20`: Meta-index block handle 159 - `21-40`: Index block handle 160 - `41-44`: Version number 161 - `45-52`: Magic number 162 163 ## Changes / additions to `sstable.TableFormat` 164 165 The `sstable.TableFormat` enum is a `uint32` representation of the tuple 166 `(magic number, format version). The current values are: 167 168 ```go 169 type TableFormat uint32 170 171 const ( 172 TableFormatRocksDBv2 TableFormat = iota 173 TableFormatLevelDB 174 ) 175 ``` 176 177 It should be noted that this enum is _not_ persisted in the SSTable. It is 178 purely an internal type that represents the tuple that simplifies a number of 179 version checks when reading / writing an SSTable. The values are free to 180 change, provided care is taken with default values and existing usage. 181 182 The existing `sstable.TableFormat` will be altered to reflect the "linear" 183 nature of the version history. New versions will be added with the next value 184 in the sequence. 185 186 ```go 187 const ( 188 TableFormatUnspecified TableFormat = iota 189 TableFormatLevelDB // The original LevelDB table format. 190 TableFormatRocksDBv2 // The current default table format. 191 TableFormatPebblev1 // Block properties. 192 TableFormatPebblev2 // Range keys. 193 ... 194 TableFormatPebbleDBvN 195 ) 196 ``` 197 198 The introduction of `TableFormatUnspecified` can be used to ensure that where a 199 `sstable.TableFormat` is _not_ specified, Pebble can select a suitable default 200 for writing the table (most likely based on the format major version in use by 201 the store; more in the next section). 202 203 ## Interaction with the format major version 204 205 The `FormatMajorVersion` type is used to determine the set of features the 206 store supports. 207 208 A Pebble store may be read-from / written-to by a Pebble binary that supports 209 newer features, with more recent Pebble format major versions. These newer 210 features could include the ability to read and write more recent SSTables. 211 While the store _could_ read and write SSTables at the most recent version the 212 binary supports, it is not safe to do so, for reasons outlined earlier. 213 214 The format major version will have a "maximum table format version" associated 215 with it that indicates the maximum `sstable.TableFormat` that can be safely 216 handled by the store. 217 218 When introducing a new _table format_ version, it should be gated behind an 219 associated `FormatMajorVersion` that has the new table format as its "maximum 220 table format version". 221 222 For example: 223 224 ```go 225 // Existing verisons. 226 FormatDefault.MaxTableFormat() // sstable.TableFormatRocksDBv2 227 ... 228 FormatSetWithDelete.MaxTableFormat() // sstable.TableFormatRocksDBv2 229 // Proposed versions with Pebble version scheme. 230 FormatBlockPropertyCollector.MaxTableFormat() // sstable.TableFormatPebbleDBv1 231 FormatRangeKeys.MaxTableFormat() // sstable.TableFormatPebbleDBv2 232 ``` 233 234 ## Usage in Cockroach 235 236 The introduction of new SSTable format versions needs to be carefully 237 coordinated between stores to ensure there are no incompatibilities (i.e. newer 238 store writes an SSTable that cannot be understood by other stores). 239 240 It is only safe to use a new table format when all nodes in a cluster have been 241 finalized. A newer Cockroach node, with newer Pebble code, should continue to 242 write SSTables with a table format version equal to or less than the smallest 243 table format version across all nodes in the cluster. Once the cluster version 244 has been finalized, and `(*DB).RatchetFormatMajorVersion(FormatMajorVersion)` 245 has been called, nodes are free to write SSTables at newer table format 246 versions. 247 248 At runtime, Pebble exposes a `(*DB).FormatMajorVersion()` method, which may be 249 used to determine the current format major version of the store, and hence, the 250 associated table format version. 251 252 In addition to the above, there are situations where SSTables are created for 253 consumption at a later point in time, independent of any Pebble store - 254 specifically backup and restore. Currently, Cockroach uses two functions in 255 `pkg/sstable` to construct SSTables for both ingestion and backup 256 ([here](https://github.com/cockroachdb/cockroach/blob/20eaf0b415f1df361246804e5d1d80c7a20a8eb6/pkg/storage/sst_writer.go#L57) 257 and 258 [here](https://github.com/cockroachdb/cockroach/blob/20eaf0b415f1df361246804e5d1d80c7a20a8eb6/pkg/storage/sst_writer.go#L78)). 259 Both will need to be updated to take into account the cluster version to ensure 260 that SSTables with newer versions are only written once the cluster version has 261 been finalized. 262 263 ### Cluster version migration sequencing 264 265 Cockroach uses cluster versions as a guarantee that all nodes in a cluster are 266 running at a particular binary version, with a particular set of features 267 enabled. The Pebble store is ratcheted as the cluster version passes certain 268 versions that correspond to new Pebble functionality. Care must be taken to 269 prevent subtle race conditions while the cluster version is being updated 270 across all nodes in a cluster. 271 272 Consider a cluster at cluster version `n-1` with corresponding Pebble format 273 major version `A`. A new cluster version `n` introduces a new Pebble format 274 major version `B` with new table level features. One by one, nodes will bump 275 their format major versions from `A` to `B` as they are upgraded to cluster 276 version `n`. There exists a period of time where nodes in a cluster are split 277 between cluster versions `n-1` and `n`, and Pebble format major versions `A` 278 and `B`. If version `B` introduces SSTable level features that nodes with 279 stores at format major version `A` do not yet understand, there exists the risk 280 for runtime incompatibilities. 281 282 To guard against the window of incompatibility, _two_ cluster versions are 283 employed when bumping Pebble format major versions that correspond to new 284 SSTable level features. The first cluster verison is uesd to synchronize all 285 stores at the same Pebble format major version (and therefore table format 286 version). The second cluster version is used as a feature gate that enables 287 Cockroach nodes to make use of the newer table format, relying on the guarantee 288 that if a node is at version `n + 1`, then all other nodes in the cluster must 289 all be at least at version `n`, and therefore have Pebble stores at format 290 major version `B`.