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  }