Select the types of activity you want to include in your feed.
Bug fixes and improvements
- Fix major data corruption by removing Next skip from iterator adapter (a hack that was added for leveldb) - Serialize and restore DID bloom filter - Move stuff around, clean up test helpers
···11-package main
11+package badgertodbm
2233import (
44 "bytes"
···4343 // because these large values don't seem to change (unlike the non-leaf nodes within the iavl tree, which are changing all the time),
4444 // this makes compaction much faster and decreases SSD thrashing
4545 opts.ValueThreshold = 192
4646+ return NewBadgerDBWithOptions(opts)
4747+}
4848+4949+func NewBadgerInMemoryDB() (*badger.DB, *BadgerDB, error) {
5050+ opts := badger.DefaultOptions("")
5151+ opts.InMemory = true
4652 return NewBadgerDBWithOptions(opts)
4753}
4854···297303}
298304299305// Key implements Iterator.
300300-// The caller should not modify the contents of the returned slice.
301301-// Instead, the caller should make a copy and work on the copy.
302306func (i *badgerDBIterator) Key() []byte {
303307 if !i.Valid() {
304308 panic("iterator is invalid")
···307311}
308312309313// Value implements Iterator.
310310-// The returned slice is a copy of the original data, therefore it is safe to modify.
311314func (i *badgerDBIterator) Value() []byte {
312315 if !i.Valid() {
313316 panic("iterator is invalid")
+13-6
dbadapter/adapter.go
dbmtoiavldb/adapter.go
···11-package dbadapter
11+package dbmtoiavldb
2233import (
44+ "slices"
55+46 "cosmossdk.io/core/store"
57 dbm "github.com/cometbft/cometbft-db"
68 iavldbm "github.com/cosmos/iavl/db"
···7173}
72747375func (i *AdaptedIterator) Next() {
7474- if !i.calledNextOnce {
7676+ /*if !i.calledNextOnce {
7577 i.calledNextOnce = true
7678 return
7777- }
7979+ }*/
7880 i.underlying.Next()
7981}
80828183func (i *AdaptedIterator) Key() []byte {
8282- return i.underlying.Key()
8484+ // dbm.Iterator says the result of underlying.Key() is not safe for modification, but
8585+ // corestore.Iterator (used by iavldbm) says "the key returned should be a copy and thus safe for modification"
8686+ return slices.Clone(i.underlying.Key())
8387}
84888589func (i *AdaptedIterator) Value() []byte {
9090+ // dbm.Iterator says the result of underlying.Value() is not safe for modification, but
9191+ // corestore.Iterator (used by iavldbm) says "the value returned should be a copy and thus safe for modification"
8692 v, _ := decompressValue(i.zstdDecoder, i.underlying.Value())
8793 return v
8894}
···175181}
176182177183func decompressValue(decoder *zstd.Decoder, value []byte) ([]byte, error) {
184184+ // always create a copy of the value, see comment on Value() about iavldb expectations
178185 if decoder == nil || len(value) == 0 {
179179- return value, nil
186186+ return slices.Clone(value), nil
180187 } else if value[0] == 0x00 {
181181- return value[1:], nil
188188+ return slices.Clone(value[1:]), nil
182189 }
183190 // passing a nil output buffer to DecodeAll means it'll optimistically start by allocating len(value)*2
184191 // but we observe compression ratios better than 50% frequently, so we allocate a slice ourselves with cap len(value)*3
···11+package transaction
22+33+import (
44+ "errors"
55+ "io"
66+ "math"
77+ "sync"
88+ "time"
99+1010+ "github.com/bits-and-blooms/bloom/v3"
1111+ dbm "github.com/cometbft/cometbft-db"
1212+ "github.com/cosmos/iavl"
1313+ "github.com/cosmos/iavl/db"
1414+ "github.com/dgraph-io/badger/v4"
1515+ "github.com/palantir/stacktrace"
1616+)
1717+1818+type ExtendedDB interface {
1919+ dbm.DB
2020+ IteratorWithOptions(start, end []byte, opts badger.IteratorOptions) (dbm.Iterator, error)
2121+}
2222+2323+type BloomFilterStorage interface {
2424+ BuildDIDBloomFilter(tx Read) (*bloom.BloomFilter, error)
2525+ StoreDIDBloomFilter(writeBloomTo func(w io.Writer) (uint64, uint64, uint64, error)) error
2626+}
2727+2828+type Factory struct {
2929+ bloomFilterStorage BloomFilterStorage
3030+3131+ db ExtendedDB
3232+ mutableTree *iavl.MutableTree
3333+ sequenceGetter func(tx Read) (uint64, error)
3434+3535+ bloomMu sync.RWMutex
3636+ bloomSeq uint64
3737+ bloomFilter *bloom.BloomFilter
3838+3939+ bloomSaverMu sync.Mutex
4040+ bloomLastSaveSeq uint64
4141+}
4242+4343+func NewFactory(tree *iavl.MutableTree, indexDB ExtendedDB, sequenceGetter func(tx Read) (uint64, error), bloomFilterStorage BloomFilterStorage) (*Factory, error) {
4444+ f := &Factory{
4545+ bloomFilterStorage: bloomFilterStorage,
4646+ db: indexDB,
4747+ mutableTree: tree,
4848+ sequenceGetter: sequenceGetter,
4949+ }
5050+5151+ var err error
5252+ // if we use ReadCommitted, it's going to fail when the tree doesn't have versions yet
5353+ // in practice we just want to blindly list all DIDs "past present and future", so it doesn't matter what the version is
5454+ // (when doing historical queries, it's fine if the bloom filter claims that a DID already exists when it might not exist yet)
5555+ tx := f.ReadWorking(time.Now())
5656+ f.bloomFilter, err = bloomFilterStorage.BuildDIDBloomFilter(tx)
5757+ if err != nil {
5858+ return nil, stacktrace.Propagate(err, "")
5959+ }
6060+ f.bloomSeq, err = f.sequenceGetter(tx)
6161+ // since the sequenceGetter always returns the next sequence
6262+ f.bloomSeq--
6363+ return f, stacktrace.Propagate(err, "")
6464+}
6565+6666+func (f *Factory) ReadWorking(ts time.Time) Read {
6767+ return &readTx{
6868+ ts: ts,
6969+ height: f.mutableTree.WorkingVersion(),
7070+ mutableTree: f.mutableTree,
7171+ db: f.db,
7272+ sequenceGetter: f.sequenceGetter,
7373+ bloomMu: &f.bloomMu,
7474+ bloomFilter: f.bloomFilter,
7575+ bloomSeq: &f.bloomSeq,
7676+ }
7777+}
7878+7979+func (f *Factory) ReadCommitted() Read {
8080+ tx, err := f.ReadHeight(time.Now(), f.mutableTree.Version())
8181+ if err != nil {
8282+ // this should never happen, it's not worth making the signature of this function more
8383+ // complex for an error we'll never return unless the ABCI application is yet to be initialized
8484+ panic(stacktrace.Propagate(err, ""))
8585+ }
8686+ return tx
8787+}
8888+8989+func (f *Factory) ReadHeight(ts time.Time, height int64) (Read, error) {
9090+ immutable, err := f.mutableTree.GetImmutable(height)
9191+ if err != nil {
9292+ if !errors.Is(err, iavl.ErrVersionDoesNotExist) || height != 0 {
9393+ return nil, stacktrace.Propagate(err, "")
9494+ }
9595+ // give the reader an empty tree just to satisfy expectations
9696+ tmpTree := iavl.NewMutableTree(db.NewMemDB(), 1, false, iavl.NewNopLogger())
9797+ _, v, err := tmpTree.SaveVersion()
9898+ if err != nil {
9999+ return nil, stacktrace.Propagate(err, "")
100100+ }
101101+ immutable, err = tmpTree.GetImmutable(v)
102102+ if err != nil {
103103+ return nil, stacktrace.Propagate(err, "")
104104+ }
105105+ }
106106+ return &readTx{
107107+ ts: ts,
108108+ height: height,
109109+ tree: AdaptImmutableTree(immutable),
110110+ db: f.db,
111111+ sequenceGetter: f.sequenceGetter,
112112+ bloomMu: &f.bloomMu,
113113+ bloomFilter: f.bloomFilter,
114114+ }, nil
115115+}
116116+117117+func (f *Factory) SaveDIDBloomFilter() error {
118118+ f.bloomSaverMu.Lock()
119119+ defer f.bloomSaverMu.Unlock()
120120+121121+ f.bloomMu.RLock()
122122+ shouldSave := f.bloomSeq > f.bloomLastSaveSeq
123123+ f.bloomMu.RUnlock()
124124+ if !shouldSave {
125125+ return nil
126126+ }
127127+128128+ var savedSeq uint64
129129+ err := f.bloomFilterStorage.StoreDIDBloomFilter(func(w io.Writer) (uint64, uint64, uint64, error) {
130130+ f.bloomMu.RLock()
131131+ defer f.bloomMu.RUnlock()
132132+133133+ _, err := f.bloomFilter.WriteTo(w)
134134+ savedSeq = f.bloomSeq
135135+136136+ // we reimplement bloomFilter.ApproximatedSize() here, because their version deals poorly with the case where the filter is completely full
137137+ // (it will return 0)
138138+ // and it also returns a uint32, which theoretically means we can't have more DIDs than humans on the planet...
139139+140140+ x := float64(f.bloomFilter.BitSet().Count())
141141+ m := float64(f.bloomFilter.Cap())
142142+ k := float64(f.bloomFilter.K())
143143+ size := -1 * m / k * math.Log(1-x/m) / math.Log(math.E)
144144+ curItemCap := uint64(m * math.Log(2) / k)
145145+ sizeInt := curItemCap
146146+ if !math.IsInf(size, 0) {
147147+ sizeInt = uint64(math.Floor(size + 0.5))
148148+ }
149149+ return f.bloomSeq, sizeInt, curItemCap, stacktrace.Propagate(err, "")
150150+ })
151151+152152+ if err != nil {
153153+ return stacktrace.Propagate(err, "")
154154+ }
155155+156156+ f.bloomLastSaveSeq = savedSeq
157157+158158+ return nil
159159+}
+2-1
transaction/interface.go
···2626 Tree() UnifiedTree
2727 IndexDB() WriteIndex
2828 TestDIDBloomFilter(did []byte) bool
2929- AddToDIDBloomFilter(did []byte)
2929+ // sequence is only used to track how up-to-date the bloom filter is. it is not added to the bloom filter
3030+ AddToDIDBloomFilter(did []byte, sequence uint64)
30313132 Commit() error
3233 Rollback() error