gitee.com/lh-her-team/common@v1.5.1/birdsnest/birdsnest.go (about) 1 // Package birdsnest bird's nest implement 2 package birdsnest 3 4 import ( 5 "errors" 6 "path/filepath" 7 8 birdsnestpb "gitee.com/lh-her-team/common/birdsnest/pb" 9 "go.uber.org/atomic" 10 ) 11 12 const ( 13 Filepath = "birdsnest" 14 ) 15 16 var ( 17 // ErrKeyCannotBeEmpty key can not be empty error 18 ErrKeyCannotBeEmpty = errors.New("key cannot be empty") 19 // ErrCannotModifyTheNestConfiguration cannot modify the nest configuration error 20 ErrCannotModifyTheNestConfiguration = errors.New("when historical data exists, you cannot modify the nest " + 21 "configuration") 22 // ErrBirdsNestSizeCannotBeZero bird's nest size cannot be zero error 23 ErrBirdsNestSizeCannotBeZero = errors.New("the size cannot be 0") 24 ) 25 26 // BirdsNestImpl impl 27 type BirdsNestImpl struct { 28 height uint64 29 preHeight *atomic.Uint64 30 // A set of cuckoo filters, stored on a linked list 31 filters []CuckooFilter 32 // BirdsNest Bird's Nest configuration 33 config *BirdsNestConfig 34 // Max strategy function 35 strategy Strategy 36 // The cuckoo filter is currently operational for use by the Add method 37 currentIndex int 38 // rules Bird's Nest rule 39 rules map[RuleType]Rule 40 // log Logger wrapper 41 log Logger 42 exitC chan struct{} 43 // serializeC serialize channel 44 serializeC chan serializeSignal 45 // snapshot Wal implementation 46 snapshot *WalSnapshot 47 } 48 49 // NewBirdsNest Create a BirdsNest 50 func NewBirdsNest(config *BirdsNestConfig, exitC chan struct{}, strategy Strategy, logger Logger) ( 51 *BirdsNestImpl, error) { 52 return NewBirdsNestByNumber(config, exitC, strategy, logger, -1) 53 } 54 55 // NewBirdsNestByNumber Create a numbered BirdsNest 56 func NewBirdsNestByNumber(config *BirdsNestConfig, exitC chan struct{}, strategy Strategy, logger Logger, 57 number int) (*BirdsNestImpl, error) { 58 if config.Length <= 0 { 59 return nil, ErrBirdsNestSizeCannotBeZero 60 } 61 // eg: data/org1/tx_filter/chain1/birdsnest1 62 join := filepath.Join(config.Snapshot.Path, config.ChainId) 63 snapshot, err := NewWalSnapshot(join, Filepath, number) 64 if err != nil { 65 return nil, err 66 } 67 bn := &BirdsNestImpl{ 68 height: 0, 69 preHeight: atomic.NewUint64(0), 70 filters: newCuckooFilters(config.Cuckoo, config.Length+1), 71 config: config, 72 currentIndex: 0, 73 exitC: exitC, 74 serializeC: make(chan serializeSignal), 75 log: logger, 76 // There is currently only one rule 77 rules: map[RuleType]Rule{ 78 RuleType_AbsoluteExpireTime: NewAETRule(config.Rules.AbsoluteExpireTime, logger), 79 }, 80 snapshot: snapshot, 81 strategy: strategy, 82 } 83 err = bn.Deserialize() 84 if err != nil { 85 if err != ErrCannotModifyTheNestConfiguration { 86 return nil, err 87 } 88 } 89 return bn, err 90 } 91 92 // GetHeight get current height 93 func (b *BirdsNestImpl) GetHeight() uint64 { 94 return b.height 95 } 96 97 // SetHeight set height 98 func (b *BirdsNestImpl) SetHeight(height uint64) { 99 b.height = height 100 b.serializeHeight(height) 101 } 102 103 // AddsAndSetHeight Add multiple Key and set height 104 func (b *BirdsNestImpl) AddsAndSetHeight(keys []Key, height uint64) (result error) { 105 err := b.Adds(keys) 106 if err != nil { 107 return err 108 } 109 b.SetHeight(height) 110 return nil 111 } 112 113 // Adds Add multiple Key 114 func (b *BirdsNestImpl) Adds(keys []Key) error { 115 for _, k := range keys { 116 err := b.Add(k) 117 if err != nil { 118 return err 119 } 120 } 121 return nil 122 } 123 124 // Add a Key 125 func (b *BirdsNestImpl) Add(key Key) error { 126 if key == nil || key.Len() == 0 { 127 return ErrKeyCannotBeEmpty 128 } 129 for { 130 var add bool 131 add, err := b.filters[b.currentIndex].Add(key) 132 if err != nil { 133 return err 134 } 135 if add { 136 return nil 137 } 138 // fullStrategy Execute strategy when cuckoo filter is full 139 err = b.fullStrategy() 140 if err != nil { 141 return err 142 } 143 } 144 } 145 146 func (b *BirdsNestImpl) ValidateRule(key Key, rules ...RuleType) error { 147 if key == nil || key.Len() == 0 { 148 return ErrKeyCannotBeEmpty 149 } 150 for _, rule := range rules { 151 r, ok := b.rules[rule] 152 if !ok { 153 continue 154 } 155 err := r.Validate(key) 156 if err != nil { 157 return err 158 } 159 } 160 return nil 161 } 162 163 // fullStrategy Execute strategy when cuckoo filter is full 164 func (b *BirdsNestImpl) fullStrategy() error { 165 if !b.filters[b.currentIndex].IsFull() { 166 return nil 167 } 168 err := b.strategy(b) 169 if err != nil { 170 return err 171 } 172 return nil 173 } 174 175 // Info 176 // index 0 height 177 // index 1 cuckoo size 178 // index 2 current index 179 // index 3 total cuckoo size 180 // index 4 total space occupied by cuckoo 181 func (b *BirdsNestImpl) Info() []uint64 { 182 var infos = make([]uint64, 5) 183 infos[0] = b.height 184 infos[1] = uint64(b.config.Length) // cuckoo size 185 infos[2] = uint64(b.currentIndex) // current index 186 for _, filter := range b.filters { 187 info := filter.Info() 188 infos[3] += info[0] // total keys size 189 infos[4] += info[1] // total space 190 } 191 return infos 192 } 193 194 // Convert common.KeyType to common.FilterExtensionType 195 func statusConvertExtension(kt KeyType) FilterExtensionType { 196 switch kt { 197 case KeyType_KTDefault: 198 return FilterExtensionType_FETDefault 199 case KeyType_KTTimestampKey: 200 return FilterExtensionType_FETTimestamp 201 default: 202 return -1 203 } 204 } 205 206 // TODO 下一版优化 go Encode 207 func analysisCuckooFilters(f []CuckooFilter) ([]*birdsnestpb.CuckooFilter, error) { 208 var filters = make([]*birdsnestpb.CuckooFilter, len(f)) 209 for i, filter := range f { 210 encode, err := filter.Encode() 211 if err != nil { 212 return nil, err 213 } 214 filters[i] = &birdsnestpb.CuckooFilter{ 215 Cuckoo: encode.filter, 216 Extension: filter.Extension().Serialize(), 217 Config: encode.config, 218 } 219 } 220 return filters, nil 221 }