github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/clusterversion/cockroach_versions.go (about) 1 // Copyright 2017 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package clusterversion 12 13 import "github.com/cockroachdb/cockroach/pkg/roachpb" 14 15 // VersionKey is a unique identifier for a version of CockroachDB. 16 type VersionKey int 17 18 // Version constants. 19 // 20 // To add a version: 21 // - Add it at the end of this block. 22 // - Add it at the end of the `Versions` block below. 23 // - For major or minor versions, bump binaryMinSupportedVersion. For 24 // example, if introducing the `20.1` release, bump it to 25 // VersionStart19_2 (i.e. `19.1-1`). 26 // 27 // To delete a version. 28 // - Remove its associated runtime checks. 29 // - If the version is not the latest one, delete the constant, comment out 30 // its stanza, and say "Removed." above the versionsSingleton. 31 // 32 //go:generate stringer -type=VersionKey 33 const ( 34 _ VersionKey = iota - 1 // want first named one to start at zero 35 Version19_1 36 VersionStart19_2 37 VersionLearnerReplicas 38 VersionTopLevelForeignKeys 39 VersionAtomicChangeReplicasTrigger 40 VersionAtomicChangeReplicas 41 VersionTableDescModificationTimeFromMVCC 42 VersionPartitionedBackup 43 Version19_2 44 VersionStart20_1 45 VersionContainsEstimatesCounter 46 VersionChangeReplicasDemotion 47 VersionSecondaryIndexColumnFamilies 48 VersionNamespaceTableWithSchemas 49 VersionProtectedTimestamps 50 VersionPrimaryKeyChanges 51 VersionAuthLocalAndTrustRejectMethods 52 VersionPrimaryKeyColumnsOutOfFamilyZero 53 VersionRootPassword 54 VersionNoExplicitForeignKeyIndexIDs 55 VersionHashShardedIndexes 56 VersionCreateRolePrivilege 57 VersionStatementDiagnosticsSystemTables 58 VersionSchemaChangeJob 59 VersionSavepoints 60 VersionTimeTZType 61 VersionTimePrecision 62 Version20_1 63 VersionStart20_2 64 VersionGeospatialType 65 VersionEnums 66 VersionRangefeedLeases 67 VersionAlterColumnTypeGeneral 68 VersionAlterSystemJobsAddCreatedByColumns 69 70 // Add new versions here (step one of two). 71 ) 72 73 // versionsSingleton lists all historical versions here in chronological order, 74 // with comments describing what backwards-incompatible features were 75 // introduced. 76 // 77 // A roachpb.Version has the colloquial form MAJOR.MINOR[.PATCH][-UNSTABLE], 78 // where the PATCH and UNSTABLE components can be omitted if zero. Keep in mind 79 // that a version with an unstable component, like 1.1-2, represents a version 80 // that was developed AFTER v1.1 was released and is not slated for release 81 // until the next stable version (either 1.2-0 or 2.0-0). Patch releases, like 82 // 1.1.2, do not have associated migrations. 83 // 84 // NB: The version upgrade process requires the versions as seen by a cluster to 85 // be monotonic. Once we've added 1.1-0, we can't slot in 1.0-4 because 86 // clusters already running 1.1-0 won't migrate through the new 1.0-4 version. 87 // Such clusters would need to be wiped. As a result, do not bump the major or 88 // minor version until we are absolutely sure that no new migrations will need 89 // to be added (i.e., when cutting the final release candidate). 90 var versionsSingleton = keyedVersions([]keyedVersion{ 91 //{ 92 // Removed 93 // VersionUnreplicatedRaftTruncatedState is https://github.com/cockroachdb/cockroach/pull/34660. 94 // When active, it moves the truncated state into unreplicated keyspace 95 // on log truncations. 96 // 97 // The migration works as follows: 98 // 99 // 1. at any log position, the replicas of a Range either use the new 100 // (unreplicated) key or the old one, and exactly one of them exists. 101 // 102 // 2. When a log truncation evaluates under the new cluster version, 103 // it initiates the migration by deleting the old key. Under the old cluster 104 // version, it behaves like today, updating the replicated truncated state. 105 // 106 // 3. The deletion signals new code downstream of Raft and triggers a write 107 // to the new, unreplicated, key (atomic with the deletion of the old key). 108 // 109 // 4. Future log truncations don't write any replicated data any more, but 110 // (like before) send along the TruncatedState which is written downstream 111 // of Raft atomically with the deletion of the log entries. This actually 112 // uses the same code as 3. 113 // What's new is that the truncated state needs to be verified before 114 // replacing a previous one. If replicas disagree about their truncated 115 // state, it's possible for replica X at FirstIndex=100 to apply a 116 // truncated state update that sets FirstIndex to, say, 50 (proposed by a 117 // replica with a "longer" historical log). In that case, the truncated 118 // state update must be ignored (this is straightforward downstream-of-Raft 119 // code). 120 // 121 // 5. When a split trigger evaluates, it seeds the RHS with the legacy 122 // key iff the LHS uses the legacy key, and the unreplicated key otherwise. 123 // This makes sure that the invariant that all replicas agree on the 124 // state of the migration is upheld. 125 // 126 // 6. When a snapshot is applied, the receiver is told whether the snapshot 127 // contains a legacy key. If not, it writes the truncated state (which is 128 // part of the snapshot metadata) in its unreplicated version. Otherwise 129 // it doesn't have to do anything (the range will migrate later). 130 // 131 // The following diagram visualizes the above. Note that it abuses sequence 132 // diagrams to get a nice layout; the vertical lines belonging to NewState 133 // and OldState don't imply any particular ordering of operations. 134 // 135 // ┌────────┐ ┌────────┐ 136 // │OldState│ │NewState│ 137 // └───┬────┘ └───┬────┘ 138 // │ Bootstrap under old version 139 // │ <─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ 140 // │ │ 141 // │ │ Bootstrap under new version 142 // │ │ <─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ 143 // │ │ 144 // │─ ─ ┐ 145 // │ | Log truncation under old version 146 // │< ─ ┘ 147 // │ │ 148 // │─ ─ ┐ │ 149 // │ | Snapshot │ 150 // │< ─ ┘ │ 151 // │ │ 152 // │ │─ ─ ┐ 153 // │ │ | Snapshot 154 // │ │< ─ ┘ 155 // │ │ 156 // │ Log truncation under new version │ 157 // │ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─>│ 158 // │ │ 159 // │ │─ ─ ┐ 160 // │ │ | Log truncation under new version 161 // │ │< ─ ┘ 162 // │ │ 163 // │ │─ ─ ┐ 164 // │ │ | Log truncation under old version 165 // │ │< ─ ┘ (necessarily running new binary) 166 // 167 // Source: http://www.plantuml.com/plantuml/uml/ and the following input: 168 // 169 // @startuml 170 // scale 600 width 171 // 172 // OldState <--] : Bootstrap under old version 173 // NewState <--] : Bootstrap under new version 174 // OldState --> OldState : Log truncation under old version 175 // OldState --> OldState : Snapshot 176 // NewState --> NewState : Snapshot 177 // OldState --> NewState : Log truncation under new version 178 // NewState --> NewState : Log truncation under new version 179 // NewState --> NewState : Log truncation under old version\n(necessarily running new binary) 180 // @enduml 181 182 //Key: VersionUnreplicatedRaftTruncatedState, 183 //Version: roachpb.Version{Major: 2, Minor: 1, Unstable: 6}, 184 //}, 185 { 186 // Version19_1 is CockroachDB v19.1. It's used for all v19.1.x patch releases. 187 Key: Version19_1, 188 Version: roachpb.Version{Major: 19, Minor: 1}, 189 }, 190 { 191 // VersionStart19_2 demarcates work towards CockroachDB v19.2. 192 Key: VersionStart19_2, 193 Version: roachpb.Version{Major: 19, Minor: 1, Unstable: 1}, 194 }, 195 // Removed. 196 // { 197 // // VersionQueryTxnTimestamp is https://github.com/cockroachdb/cockroach/pull/36307. 198 // Key: VersionQueryTxnTimestamp, 199 // Version: roachpb.Version{Major: 19, Minor: 1, Unstable: 2}, 200 // }, 201 // Removed. 202 // { 203 // // VersionStickyBit is https://github.com/cockroachdb/cockroach/pull/37506. 204 // Key: VersionStickyBit, 205 // Version: roachpb.Version{Major: 19, Minor: 1, Unstable: 3}, 206 // }, 207 // Removed. 208 // { 209 // // VersionParallelCommits is https://github.com/cockroachdb/cockroach/pull/37777. 210 // Key: VersionParallelCommits, 211 // Version: roachpb.Version{Major: 19, Minor: 1, Unstable: 4}, 212 // }, 213 // Removed. 214 // { 215 // // VersionGenerationComparable is https://github.com/cockroachdb/cockroach/pull/38334. 216 // Key: VersionGenerationComparable, 217 // Version: roachpb.Version{Major: 19, Minor: 1, Unstable: 5}, 218 // }, 219 { 220 // VersionLearnerReplicas is https://github.com/cockroachdb/cockroach/pull/38149. 221 Key: VersionLearnerReplicas, 222 Version: roachpb.Version{Major: 19, Minor: 1, Unstable: 6}, 223 }, 224 { 225 // VersionTopLevelForeignKeys is https://github.com/cockroachdb/cockroach/pull/39173. 226 // 227 // It represents an upgrade to the table descriptor format in which foreign 228 // key references are pulled out of the index descriptors where they 229 // originally were kept, and rewritten into a top level field on the index's 230 // parent table descriptors. During a mixed-version state, the database will 231 // write old-style table descriptors at all system boundaries, but upgrade 232 // all old-style table descriptors into the new format upon read. Once the 233 // upgrade is finalized, the database will write the upgraded format, but 234 // continue to upgrade old-style descriptors on-demand. 235 // 236 // This version is also used for the new foreign key schema changes which 237 // are run in the schema changer, requiring new types of mutations on the 238 // table descriptor. The same version is used for both of these changes 239 // because the changes are intertwined, and it slightly simplifies some of 240 // the logic to assume that either neither or both sets of changes can be 241 // active. 242 Key: VersionTopLevelForeignKeys, 243 Version: roachpb.Version{Major: 19, Minor: 1, Unstable: 7}, 244 }, 245 { 246 // VersionAtomicChangeReplicasTrigger is https://github.com/cockroachdb/cockroach/pull/39485. 247 // 248 // It enables use of updated fields in ChangeReplicasTrigger that will 249 // support atomic replication changes. 250 Key: VersionAtomicChangeReplicasTrigger, 251 Version: roachpb.Version{Major: 19, Minor: 1, Unstable: 8}, 252 }, 253 { 254 // VersionAtomicChangeReplicas is https://github.com/cockroachdb/cockroach/pull/39936. 255 // 256 // It provides an implementation of (*Replica).ChangeReplicas that uses 257 // atomic replication changes. The corresponding cluster setting 258 // 'kv.atomic_replication_changes.enabled' provides a killswitch (i.e. 259 // no atomic replication changes will be scheduled when it is set to 260 // 'false'). 261 Key: VersionAtomicChangeReplicas, 262 Version: roachpb.Version{Major: 19, Minor: 1, Unstable: 9}, 263 }, 264 { 265 // VersionTableDescModificationTimeFromMVCC is https://github.com/cockroachdb/cockroach/pull/40581 266 // 267 // It represents an upgrade to the table descriptor format in which 268 // CreateAsOfTime and ModifiedTime are set to zero when new versions of 269 // table descriptors are written. This removes the need to fix the commit 270 // timestamp for transactions which update table descriptors. The value 271 // is then populated by the reading client with the MVCC timestamp of the 272 // row which contained the serialized table descriptor. 273 Key: VersionTableDescModificationTimeFromMVCC, 274 Version: roachpb.Version{Major: 19, Minor: 1, Unstable: 10}, 275 }, 276 { 277 // VersionPartitionedBackup is https://github.com/cockroachdb/cockroach/pull/39250. 278 Key: VersionPartitionedBackup, 279 Version: roachpb.Version{Major: 19, Minor: 1, Unstable: 11}, 280 }, 281 { 282 // Version19_2 is CockroachDB v19.2. It's used for all v19.2.x patch releases. 283 Key: Version19_2, 284 Version: roachpb.Version{Major: 19, Minor: 2}, 285 }, 286 { 287 // VersionStart20_1 demarcates work towards CockroachDB v20.1. 288 Key: VersionStart20_1, 289 Version: roachpb.Version{Major: 19, Minor: 2, Unstable: 1}, 290 }, 291 { 292 // VersionContainsEstimatesCounter is https://github.com/cockroachdb/cockroach/pull/37583. 293 // 294 // MVCCStats.ContainsEstimates has been migrated from boolean to a 295 // counter so that the consistency checker and splits can reset it by 296 // returning -ContainsEstimates, avoiding racing with other operations 297 // that want to also change it. 298 // 299 // The migration maintains the invariant that raft commands with 300 // ContainsEstimates zero or one want the bool behavior (i.e. 1+1=1). 301 // Before the cluster version is active, at proposal time we'll refuse 302 // any negative ContainsEstimates plus we clamp all others to {0,1}. 303 // When the version is active, and ContainsEstimates is positive, we 304 // multiply it by 2 (i.e. we avoid 1). Downstream of raft, we use old 305 // behavior for ContainsEstimates=1 and the additive behavior for 306 // anything else. 307 Key: VersionContainsEstimatesCounter, 308 Version: roachpb.Version{Major: 19, Minor: 2, Unstable: 2}, 309 }, 310 { 311 // VersionChangeReplicasDemotion enables the use of voter demotions 312 // during replication changes that remove (one or more) voters. 313 // When this version is active, voters that are being removed transition 314 // first into VOTER_DEMOTING (a joint configuration) and from there to 315 // LEARNER, before they are actually removed. This added intermediate 316 // step avoids losing quorum when the leaseholder crashes at an 317 // inopportune moment. 318 // 319 // For example, without this version active, with nodes n1-n4 and a 320 // range initially replicated on n1, n3, and n4, a rebalance operation 321 // that wants to swap n1 for n2 first transitions into the joint 322 // configuration `(n1 n3 n4) && (n2 n3 n4)`, that is, n2 is 323 // VOTER_OUTGOING. After this is committed and applied (say by 324 // everyone), the configuration entry for the final configuration 325 // `(n2 n3 n4)` is distributed: 326 // 327 //- the leader is n3 328 //- conf entry reaches n1, n2, n3 (so it is committed under the joint config) 329 //- n1 applies conf change and immediately disappears (via replicaGC, 330 // since it's not a part of the latest config) 331 //- n3 crashes 332 // 333 // At this point, the remaining replicas n4 and n2 form a quorum of the 334 // latest committed configuration, but both still have the joint 335 // configuration active, which cannot reach quorum any more. 336 // The intermediate learner step added by this version makes sure that 337 // n1 is still available at this point to help n2 win an election, and 338 // due to the invariant that replicas never have more than one unappliable 339 // configuration change in their logs, the group won't lose availability 340 // when the leader instead crashes while removing the learner. 341 Key: VersionChangeReplicasDemotion, 342 Version: roachpb.Version{Major: 19, Minor: 2, Unstable: 3}, 343 }, 344 { 345 // VersionSecondaryIndexColumnFamilies is https://github.com/cockroachdb/cockroach/pull/42073. 346 // 347 // It allows secondary indexes to respect table level column family definitions. 348 Key: VersionSecondaryIndexColumnFamilies, 349 Version: roachpb.Version{Major: 19, Minor: 2, Unstable: 4}, 350 }, 351 { 352 // VersionNamespaceTableWithSchemas is https://github.com/cockroachdb/cockroach/pull/41977 353 // 354 // It represents the migration to a new system.namespace table that has an 355 // added parentSchemaID column. In addition to the new column, the table is 356 // no longer in the system config range -- implying it is no longer gossiped. 357 Key: VersionNamespaceTableWithSchemas, 358 Version: roachpb.Version{Major: 19, Minor: 2, Unstable: 5}, 359 }, 360 { 361 // VersionProtectedTimestamps introduces the system tables for the protected 362 // timestamps subsystem. 363 // 364 // In this version and later the system.protected_ts_meta and 365 // system.protected_ts_records tables are part of the system bootstap 366 // schema. 367 Key: VersionProtectedTimestamps, 368 Version: roachpb.Version{Major: 19, Minor: 2, Unstable: 6}, 369 }, 370 { 371 // VersionPrimaryKeyChanges is https://github.com/cockroachdb/cockroach/pull/42462 372 // 373 // It allows online primary key changes of tables. 374 Key: VersionPrimaryKeyChanges, 375 Version: roachpb.Version{Major: 19, Minor: 2, Unstable: 7}, 376 }, 377 { 378 // VersionAuthLocalAndTrustRejectMethods introduces the HBA rule 379 // prefix 'local' and auth methods 'trust' and 'reject', for use 380 // in server.host_based_authentication.configuration. 381 // 382 // A separate cluster version ensures the new syntax is not 383 // introduced while previous-version nodes are still running, as 384 // this would block any new SQL client. 385 Key: VersionAuthLocalAndTrustRejectMethods, 386 Version: roachpb.Version{Major: 19, Minor: 2, Unstable: 8}, 387 }, 388 { 389 // VersionPrimaryKeyColumnsOutOfFamilyZero allows for primary key columns 390 // to exist in column families other than 0, in order to prepare for 391 // primary key changes that move primary key columns to different families. 392 Key: VersionPrimaryKeyColumnsOutOfFamilyZero, 393 Version: roachpb.Version{Major: 19, Minor: 2, Unstable: 9}, 394 }, 395 { 396 // VersionRootPassword enables setting a password for the `root` 397 // user from SQL. Even though introducing a password for the root 398 // user in 20.1 does not prevent 19.2 nodes from using the root 399 // account, we need a cluster setting: the 19.2 nodes do not even 400 // *check* the password, so setting a pw in a hybrid 20.1/19.2 401 // cluster would yield different client auth successes in 402 // different nodes (which is poor UX). 403 Key: VersionRootPassword, 404 Version: roachpb.Version{Major: 19, Minor: 2, Unstable: 10}, 405 }, 406 { 407 // VersionNoExplicitForeignKeyIndexIDs is https://github.com/cockroachdb/cockroach/pull/43332. 408 // 409 // It represents the migration away from using explicit index IDs in foreign 410 // key constraints, and instead allows all places that need these IDs to select 411 // an appropriate index to uphold the foreign key relationship. 412 Key: VersionNoExplicitForeignKeyIndexIDs, 413 Version: roachpb.Version{Major: 19, Minor: 2, Unstable: 11}, 414 }, 415 { 416 // VersionHashShardedIndexes is https://github.com/cockroachdb/cockroach/pull/42922 417 // 418 // It allows the creation of "hash sharded indexes", which construct a hidden 419 // shard column, computed from the set of index columns, and prefix the index's 420 // ranges with said shard column. 421 Key: VersionHashShardedIndexes, 422 Version: roachpb.Version{Major: 19, Minor: 2, Unstable: 12}, 423 }, 424 { 425 // VersionCreateRolePrivilege is https://github.com/cockroachdb/cockroach/pull/44232. 426 // 427 // It represents adding role management via CREATEROLE privilege. 428 // Added new column in system.users table to track hasCreateRole. 429 Key: VersionCreateRolePrivilege, 430 Version: roachpb.Version{Major: 19, Minor: 2, Unstable: 13}, 431 }, 432 { 433 // VersionStatementDiagnosticsSystemTables introduces the system tables for 434 // storing statement information (like traces, bundles). 435 // In this version and later the system.statement_diagnostics_requests, 436 // system.statement_diagnostics and system.statement_bundle_chunks tables 437 // are part of the system bootstap schema. 438 Key: VersionStatementDiagnosticsSystemTables, 439 Version: roachpb.Version{Major: 19, Minor: 2, Unstable: 14}, 440 }, 441 { 442 // VersionSchemaChangeJob is https://github.com/cockroachdb/cockroach/pull/45870. 443 // 444 // From this version on, schema changes are run as jobs instead of being 445 // scheduled by the SchemaChangeManager. 446 Key: VersionSchemaChangeJob, 447 Version: roachpb.Version{Major: 19, Minor: 2, Unstable: 15}, 448 }, 449 { 450 // VersionSavepoints enables the use of SQL savepoints, whereby 451 // the ignored seqnum list can become populated in transaction 452 // records. 453 Key: VersionSavepoints, 454 Version: roachpb.Version{Major: 19, Minor: 2, Unstable: 16}, 455 }, 456 { 457 // VersionTimeTZType enables the use of the TimeTZ data type. 458 Key: VersionTimeTZType, 459 Version: roachpb.Version{Major: 19, Minor: 2, Unstable: 17}, 460 }, 461 { 462 // VersionTimePrecision enables the use of precision with time/intervals. 463 Key: VersionTimePrecision, 464 Version: roachpb.Version{Major: 19, Minor: 2, Unstable: 18}, 465 }, 466 { 467 // Version20_1 is CockroachDB v20.1. It's used for all v20.1.x patch releases. 468 Key: Version20_1, 469 Version: roachpb.Version{Major: 20, Minor: 1}, 470 }, 471 { 472 // VersionStart20_2 demarcates work towards CockroachDB v20.2. 473 Key: VersionStart20_2, 474 Version: roachpb.Version{Major: 20, Minor: 1, Unstable: 1}, 475 }, 476 { 477 478 // VersionGeospatialType enables the use of Geospatial features. 479 Key: VersionGeospatialType, 480 Version: roachpb.Version{Major: 20, Minor: 1, Unstable: 2}, 481 }, 482 { 483 // VersionEnums enables the use of ENUM types. 484 Key: VersionEnums, 485 Version: roachpb.Version{Major: 20, Minor: 1, Unstable: 3}, 486 }, 487 { 488 489 // VersionRangefeedLeases is the enablement of leases uses rangefeeds. 490 // All nodes with this versions will have rangefeeds enabled on all system 491 // ranges. Once this version is finalized, gossip is not needed in the 492 // schema lease subsystem. Nodes which start with this version finalized 493 // will not pass gossip to the SQL layer. 494 Key: VersionRangefeedLeases, 495 Version: roachpb.Version{Major: 20, Minor: 1, Unstable: 4}, 496 }, 497 { 498 // VersionAlterColumnTypeGeneral enables the use of alter column type for 499 // conversions that require the column data to be rewritten. 500 Key: VersionAlterColumnTypeGeneral, 501 Version: roachpb.Version{Major: 20, Minor: 1, Unstable: 5}, 502 }, 503 { 504 // VersionAlterSystemJobsTable is a version which modified system.jobs table 505 // 506 Key: VersionAlterSystemJobsAddCreatedByColumns, 507 Version: roachpb.Version{Major: 20, Minor: 1, Unstable: 6}, 508 }, 509 510 // Add new versions here (step two of two). 511 512 }) 513 514 // TODO(irfansharif): clusterversion.binary{,MinimumSupported}Version 515 // feels out of place. A "cluster version" and a "binary version" are two 516 // separate concepts. 517 var ( 518 // binaryMinSupportedVersion is the earliest version of data supported by 519 // this binary. If this binary is started using a store marked with an older 520 // version than binaryMinSupportedVersion, then the binary will exit with 521 // an error. 522 // We support everything after 19.1, including pre-release 19.2 versions. 523 // This is generally beneficial, but in particular it allows the 524 // version-upgrade roachtest to use a pre-release 19.2 binary before upgrading 525 // to HEAD; if we were to set binaryMinSupportedVersion to Version19_2, 526 // that wouldn't work since you'd have to go through the final 19.2 binary 527 // before going to HEAD. 528 binaryMinSupportedVersion = VersionByKey(Version20_1) 529 530 // binaryVersion is the version of this binary. 531 // 532 // This is the version that a new cluster will use when created. 533 binaryVersion = versionsSingleton[len(versionsSingleton)-1].Version 534 ) 535 536 // VersionByKey returns the roachpb.Version for a given key. 537 // It is a fatal error to use an invalid key. 538 func VersionByKey(key VersionKey) roachpb.Version { 539 return versionsSingleton.MustByKey(key) 540 }