Skip to content

Commit 1d45df3

Browse files
committed
Merge branch 'main' into alex/sims2_v2_dev
* main: fix(cosmovisor): premature upgrade on restart (#22528) fix(store/v2/pebble): handle version 0 in keys (#22524)
2 parents 0b4a3fe + fbd725d commit 1d45df3

File tree

5 files changed

+56
-16
lines changed

5 files changed

+56
-16
lines changed

store/v2/storage/pebbledb/batch.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,17 @@ type Batch struct {
1717
batch *pebble.Batch
1818
version uint64
1919
sync bool
20+
size int
21+
}
22+
23+
const (
24+
oneIf64Bit = ^uint(0) >> 63
25+
maxUint32OrInt = (1<<31)<<oneIf64Bit - 1
26+
maxVarintLen32 = 5
27+
)
28+
29+
func keyValueSize(key, value []byte) int {
30+
return len(key) + len(value) + 1 + 2*maxVarintLen32
2031
}
2132

2233
func NewBatch(storage *pebble.DB, version uint64, sync bool) (*Batch, error) {
@@ -34,6 +45,7 @@ func NewBatch(storage *pebble.DB, version uint64, sync bool) (*Batch, error) {
3445
batch: batch,
3546
version: version,
3647
sync: sync,
48+
size: keyValueSize([]byte(latestVersionKey), versionBz[:]),
3749
}, nil
3850
}
3951

@@ -50,9 +62,20 @@ func (b *Batch) set(storeKey []byte, tombstone uint64, key, value []byte) error
5062
prefixedKey := MVCCEncode(prependStoreKey(storeKey, key), b.version)
5163
prefixedVal := MVCCEncode(value, tombstone)
5264

65+
size := keyValueSize(prefixedKey, prefixedVal)
66+
if b.size+size > maxUint32OrInt {
67+
// 4 GB is huge, probably genesis; flush and reset
68+
if err := b.batch.Commit(&pebble.WriteOptions{Sync: b.sync}); err != nil {
69+
return fmt.Errorf("max batch size exceed: failed to write PebbleDB batch: %w", err)
70+
}
71+
b.batch.Reset()
72+
b.size = 0
73+
}
74+
5375
if err := b.batch.Set(prefixedKey, prefixedVal, nil); err != nil {
5476
return fmt.Errorf("failed to write PebbleDB batch: %w", err)
5577
}
78+
b.size += size
5679

5780
return nil
5881
}

store/v2/storage/pebbledb/db.go

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -238,11 +238,14 @@ func (db *Database) Prune(version uint64) (err error) {
238238
return fmt.Errorf("invalid PebbleDB MVCC key: %s", prefixedKey)
239239
}
240240

241-
keyVersion, err := decodeUint64Ascending(verBz)
242-
if err != nil {
243-
return fmt.Errorf("failed to decode key version: %w", err)
241+
var keyVersion uint64
242+
// handle version 0 (no version prefix)
243+
if len(verBz) > 0 {
244+
keyVersion, err = decodeUint64Ascending(verBz)
245+
if err != nil {
246+
return fmt.Errorf("failed to decode key version: %w", err)
247+
}
244248
}
245-
246249
// seek to next key if we are at a version which is higher than prune height
247250
if keyVersion > version {
248251
itr.NextPrefix()
@@ -432,9 +435,13 @@ func getMVCCSlice(db *pebble.DB, storeKey, key []byte, version uint64) ([]byte,
432435
return nil, fmt.Errorf("invalid PebbleDB MVCC key: %s", itr.Key())
433436
}
434437

435-
keyVersion, err := decodeUint64Ascending(vBz)
436-
if err != nil {
437-
return nil, fmt.Errorf("failed to decode key version: %w", err)
438+
var keyVersion uint64
439+
// handle version 0 (no version prefix)
440+
if len(vBz) > 0 {
441+
keyVersion, err = decodeUint64Ascending(vBz)
442+
if err != nil {
443+
return nil, fmt.Errorf("failed to decode key version: %w", err)
444+
}
438445
}
439446
if keyVersion > version {
440447
return nil, fmt.Errorf("key version too large: %d", keyVersion)

store/v2/storage/pebbledb/iterator.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -216,15 +216,20 @@ func (itr *iterator) DebugRawIterate() {
216216
valid = itr.source.SeekLT(MVCCEncode(firstKey, itr.version+1))
217217
}
218218

219+
var err error
219220
for valid {
220221
key, vBz, ok := SplitMVCCKey(itr.source.Key())
221222
if !ok {
222223
panic(fmt.Sprintf("invalid PebbleDB MVCC key: %s", itr.source.Key()))
223224
}
224225

225-
version, err := decodeUint64Ascending(vBz)
226-
if err != nil {
227-
panic(fmt.Errorf("failed to decode key version: %w", err))
226+
var version uint64
227+
// handle version 0 (no version prefix)
228+
if len(vBz) > 0 {
229+
version, err = decodeUint64Ascending(vBz)
230+
if err != nil {
231+
panic(fmt.Errorf("failed to decode key version: %w", err))
232+
}
228233
}
229234

230235
val, tombBz, ok := SplitMVCCKey(itr.source.Value())

tools/cosmovisor/CHANGELOG.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,19 +36,22 @@ Ref: https://keepachangelog.com/en/1.0.0/
3636

3737
## [Unreleased]
3838

39+
## v1.7.0 - 2024-11-18
40+
3941
### Features
4042

4143
* [#21790](https://github.com/cosmos/cosmos-sdk/pull/21790) Add `add-batch-upgrade` command.
4244
* [#21972](https://github.com/cosmos/cosmos-sdk/pull/21972) Add `prepare-upgrade` command
45+
* [#21932](https://github.com/cosmos/cosmos-sdk/pull/21932) Add `cosmovisor show-upgrade-info` command to display the upgrade-info.json into stdout.
4346

4447
### Improvements
4548

4649
* [#21891](https://github.com/cosmos/cosmos-sdk/pull/21891) create `current` symlink as relative
4750
* [#21462](https://github.com/cosmos/cosmos-sdk/pull/21462) Pass `stdin` to binary.
51+
52+
### Bug Fixes
4853

49-
### Features
50-
51-
* [#21932](https://github.com/cosmos/cosmos-sdk/pull/21932) Add `cosmovisor show-upgrade-info` command to display the upgrade-info.json into stdout.
54+
* [#22528](https://github.com/cosmos/cosmos-sdk/pull/22528) Fix premature upgrades on restarting cosmovisor.
5255

5356
## v1.6.0 - 2024-08-12
5457

tools/cosmovisor/scanner.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import (
1515
upgradetypes "cosmossdk.io/x/upgrade/types"
1616
)
1717

18+
var errUntestAble = errors.New("untestable")
19+
1820
type fileWatcher struct {
1921
daemonHome string
2022
filename string // full path to a watched file
@@ -149,8 +151,8 @@ func (fw *fileWatcher) CheckUpdate(currentUpgrade upgradetypes.Plan) bool {
149151
}
150152

151153
// file exist but too early in height
152-
currentHeight, _ := fw.checkHeight()
153-
if currentHeight != 0 && currentHeight < info.Height {
154+
currentHeight, err := fw.checkHeight()
155+
if (err != nil || currentHeight < info.Height) && !errors.Is(err, errUntestAble) { // ignore this check for tests
154156
return false
155157
}
156158

@@ -182,7 +184,7 @@ func (fw *fileWatcher) CheckUpdate(currentUpgrade upgradetypes.Plan) bool {
182184
// checkHeight checks if the current block height
183185
func (fw *fileWatcher) checkHeight() (int64, error) {
184186
if testing.Testing() { // we cannot test the command in the test environment
185-
return 0, nil
187+
return 0, errUntestAble
186188
}
187189

188190
result, err := exec.Command(fw.currentBin, "status", "--home", fw.daemonHome).CombinedOutput() //nolint:gosec // we want to execute the status command

0 commit comments

Comments
 (0)