github.com/turingchain2020/turingchain@v1.1.21/common/db/go_pegasus.go (about)

     1  // Copyright Turing Corp. 2018 All Rights Reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package db
     6  
     7  import (
     8  	"bytes"
     9  	"context"
    10  	"strings"
    11  	"time"
    12  
    13  	log "github.com/turingchain2020/turingchain/common/log/log15"
    14  	"github.com/turingchain2020/turingchain/types"
    15  	"github.com/XiaoMi/pegasus-go-client/pegasus"
    16  	"github.com/syndtr/goleveldb/leveldb/util"
    17  )
    18  
    19  var slog = log.New("module", "db.pegasus")
    20  var pdbBench = &SsdbBench{}
    21  
    22  //HashKeyLen hash长度
    23  var HashKeyLen = 24
    24  
    25  func init() {
    26  	dbCreator := func(name string, dir string, cache int) (DB, error) {
    27  		return NewPegasusDB(name, dir, cache)
    28  	}
    29  	registerDBCreator(goPegasusDbBackendStr, dbCreator, false)
    30  }
    31  
    32  //PegasusDB db
    33  type PegasusDB struct {
    34  	BaseDB
    35  	cfg    *pegasus.Config
    36  	name   string
    37  	client pegasus.Client
    38  	table  pegasus.TableConnector
    39  }
    40  
    41  func printPegasusBenchmark() {
    42  	tick := time.Tick(time.Minute * 5)
    43  	for {
    44  		<-tick
    45  		slog.Info(pdbBench.String())
    46  	}
    47  }
    48  
    49  //NewPegasusDB new
    50  func NewPegasusDB(name string, dir string, cache int) (*PegasusDB, error) {
    51  	database := &PegasusDB{name: name}
    52  	database.cfg = parsePegasusNodes(dir)
    53  
    54  	if database.cfg == nil {
    55  		slog.Error("no valid instance exists, exit!")
    56  		return nil, types.ErrDataBaseDamage
    57  	}
    58  	var err error
    59  	database.client = pegasus.NewClient(*database.cfg)
    60  	tb, err := database.client.OpenTable(context.Background(), database.name)
    61  	if err != nil {
    62  		slog.Error("connect to pegasus error!", "pegasus", database.cfg, "error", err)
    63  		err = database.client.Close()
    64  		if err != nil {
    65  			slog.Error("database.client", "close err", err)
    66  		}
    67  		return nil, types.ErrDataBaseDamage
    68  	}
    69  	database.table = tb
    70  
    71  	go printPegasusBenchmark()
    72  	return database, nil
    73  }
    74  
    75  // url pattern: ip:port,ip:port
    76  func parsePegasusNodes(url string) *pegasus.Config {
    77  	hosts := strings.Split(url, ",")
    78  	if hosts == nil {
    79  		slog.Error("invalid url")
    80  		return nil
    81  	}
    82  
    83  	cfg := &pegasus.Config{MetaServers: hosts}
    84  	return cfg
    85  }
    86  
    87  //Get get
    88  func (db *PegasusDB) Get(key []byte) ([]byte, error) {
    89  	start := time.Now()
    90  	hashKey := getHashKey(key)
    91  	value, err := db.table.Get(context.Background(), hashKey, key)
    92  	if err != nil {
    93  		//slog.Error("Get value error", "error", err, "key", key, "keyhex", hex.EncodeToString(key), "keystr", string(key))
    94  		return nil, err
    95  	}
    96  	if value == nil {
    97  		return nil, ErrNotFoundInDb
    98  	}
    99  
   100  	pdbBench.read(1, time.Since(start))
   101  	return value, nil
   102  }
   103  
   104  //Set set
   105  func (db *PegasusDB) Set(key []byte, value []byte) error {
   106  	start := time.Now()
   107  	hashKey := getHashKey(key)
   108  	err := db.table.Set(context.Background(), hashKey, key, value)
   109  	if err != nil {
   110  		slog.Error("Set", "error", err)
   111  		return err
   112  	}
   113  	pdbBench.write(1, time.Since(start))
   114  	return nil
   115  }
   116  
   117  //SetSync 设置同步
   118  func (db *PegasusDB) SetSync(key []byte, value []byte) error {
   119  	return db.Set(key, value)
   120  }
   121  
   122  //Delete 删除
   123  func (db *PegasusDB) Delete(key []byte) error {
   124  	start := time.Now()
   125  	defer pdbBench.write(1, time.Since(start))
   126  	hashKey := getHashKey(key)
   127  	err := db.table.Del(context.Background(), hashKey, key)
   128  	if err != nil {
   129  		slog.Error("Delete", "error", err)
   130  		return err
   131  	}
   132  	return nil
   133  }
   134  
   135  //DeleteSync 删除同步
   136  func (db *PegasusDB) DeleteSync(key []byte) error {
   137  	return db.Delete(key)
   138  }
   139  
   140  //Close 同步
   141  func (db *PegasusDB) Close() {
   142  	err := db.table.Close()
   143  	if err != nil {
   144  		llog.Error("Close", "db table error", err)
   145  	}
   146  	err = db.client.Close()
   147  	if err != nil {
   148  		llog.Error("Close", "client error", err)
   149  	}
   150  }
   151  
   152  //Print 打印
   153  func (db *PegasusDB) Print() {
   154  }
   155  
   156  //Stats ...
   157  func (db *PegasusDB) Stats() map[string]string {
   158  	return nil
   159  }
   160  
   161  //Iterator 迭代器
   162  func (db *PegasusDB) Iterator(begin []byte, end []byte, reverse bool) Iterator {
   163  	var (
   164  		err   error
   165  		vals  []*pegasus.KeyValue
   166  		start []byte
   167  		over  []byte
   168  	)
   169  	if end == nil {
   170  		end = bytesPrefix(begin)
   171  	}
   172  	if bytes.Equal(end, types.EmptyValue) {
   173  		end = nil
   174  	}
   175  	limit := util.Range{Start: begin, Limit: end}
   176  	hashKey := getHashKey(begin)
   177  
   178  	if reverse {
   179  		start = begin
   180  		over = limit.Limit
   181  	} else {
   182  		start = limit.Limit
   183  		over = begin
   184  	}
   185  	dbit := &PegasusIt{itBase: itBase{begin, end, reverse}, index: -1, table: db.table, itbegin: start, itend: over}
   186  	opts := &pegasus.MultiGetOptions{StartInclusive: false, StopInclusive: false, MaxFetchCount: IteratorPageSize, Reverse: dbit.reverse}
   187  	vals, _, err = db.table.MultiGetRangeOpt(context.Background(), hashKey, begin, limit.Limit, opts)
   188  	if err != nil {
   189  		slog.Error("create iterator error!")
   190  		return nil
   191  	}
   192  	if len(vals) > 0 {
   193  		dbit.vals = vals
   194  		// 如果返回的数据大小刚好满足分页,则假设下一页还有数据
   195  		if len(dbit.vals) == IteratorPageSize {
   196  			dbit.nextPage = true
   197  			// 下一页数据的开始,等于本页数据的结束,不过在下次查询时需要设置StartInclusiv=false,因为本条数据已经包含
   198  			dbit.tmpEnd = dbit.vals[IteratorPageSize-1].SortKey
   199  		}
   200  	}
   201  	return dbit
   202  }
   203  
   204  //PegasusIt ...
   205  type PegasusIt struct {
   206  	itBase
   207  	table    pegasus.TableConnector
   208  	vals     []*pegasus.KeyValue
   209  	index    int
   210  	nextPage bool
   211  	tmpEnd   []byte
   212  
   213  	// 迭代开始位置
   214  	itbegin []byte
   215  	// 迭代结束位置
   216  	itend []byte
   217  	// 当前所属的页数(从0开始)
   218  	pageNo int
   219  }
   220  
   221  //Close 关闭
   222  func (dbit *PegasusIt) Close() {
   223  	dbit.index = -1
   224  }
   225  
   226  //Next next
   227  func (dbit *PegasusIt) Next() bool {
   228  	if len(dbit.vals) > dbit.index+1 {
   229  		dbit.index++
   230  		return true
   231  	}
   232  	// 如果有下一页数据,则自动抓取
   233  	if dbit.nextPage {
   234  		return dbit.cacheNextPage(dbit.tmpEnd)
   235  	}
   236  	return false
   237  
   238  }
   239  
   240  func (dbit *PegasusIt) initPage(begin, end []byte) bool {
   241  	var (
   242  		vals []*pegasus.KeyValue
   243  		err  error
   244  	)
   245  	opts := &pegasus.MultiGetOptions{StartInclusive: false, StopInclusive: false, MaxFetchCount: IteratorPageSize, Reverse: dbit.reverse}
   246  	hashKey := getHashKey(begin)
   247  	vals, _, err = dbit.table.MultiGetRangeOpt(context.Background(), hashKey, begin, end, opts)
   248  
   249  	if err != nil {
   250  		slog.Error("get iterator next page error", "error", err, "begin", begin, "end", dbit.itend, "reverse", dbit.reverse)
   251  		return false
   252  	}
   253  
   254  	if len(vals) > 0 {
   255  		// 这里只改变keys,不改变index
   256  		dbit.vals = vals
   257  
   258  		// 如果返回的数据大小刚好满足分页,则假设下一页还有数据
   259  		if len(vals) == IteratorPageSize {
   260  			dbit.nextPage = true
   261  			dbit.tmpEnd = dbit.vals[IteratorPageSize-1].SortKey
   262  		} else {
   263  			dbit.nextPage = false
   264  		}
   265  		return true
   266  	}
   267  	return false
   268  
   269  }
   270  
   271  // 获取下一页的数据
   272  func (dbit *PegasusIt) cacheNextPage(flag []byte) bool {
   273  	var (
   274  		over []byte
   275  	)
   276  	// 如果是逆序,则取从开始到flag的数据
   277  	if dbit.reverse {
   278  		over = dbit.itbegin
   279  	} else {
   280  		over = dbit.itend
   281  	}
   282  	// 如果是正序,则取从flag到结束的数据
   283  	if dbit.initPage(flag, over) {
   284  		dbit.index = 0
   285  		dbit.pageNo++
   286  		return true
   287  	}
   288  	return false
   289  
   290  }
   291  
   292  func (dbit *PegasusIt) checkKeyCmp(key1, key2 []byte, reverse bool) bool {
   293  	if reverse {
   294  		return bytes.Compare(key1, key2) < 0
   295  	}
   296  	return bytes.Compare(key1, key2) > 0
   297  }
   298  
   299  func (dbit *PegasusIt) findInPage(key []byte) int {
   300  	pos := -1
   301  	for i, v := range dbit.vals {
   302  		if i < dbit.index {
   303  			continue
   304  		}
   305  		if dbit.checkKeyCmp(key, v.SortKey, dbit.reverse) {
   306  			continue
   307  		} else {
   308  			pos = i
   309  			break
   310  		}
   311  	}
   312  	return pos
   313  }
   314  
   315  //Seek 查找
   316  func (dbit *PegasusIt) Seek(key []byte) bool {
   317  	pos := dbit.findInPage(key)
   318  
   319  	// 如果第一页已经找到,不会走入此逻辑
   320  	for pos == -1 && dbit.nextPage {
   321  		if dbit.cacheNextPage(dbit.tmpEnd) {
   322  			pos = dbit.findInPage(key)
   323  		} else {
   324  			break
   325  		}
   326  	}
   327  
   328  	dbit.index = pos
   329  	return dbit.Valid()
   330  }
   331  
   332  //Rewind 从头开始
   333  func (dbit *PegasusIt) Rewind() bool {
   334  	// 目前代码的Rewind调用都是在第一页,正常情况下走不到else分支;
   335  	// 但为了代码健壮性考虑,这里增加对else分支的处理
   336  	if dbit.pageNo == 0 {
   337  		dbit.index = 0
   338  		return true
   339  	}
   340  
   341  	// 当数据取到第N页的情况时,Rewind需要返回到第一页第一条
   342  	if dbit.initPage(dbit.itbegin, dbit.itend) {
   343  		dbit.index = 0
   344  		dbit.pageNo = 0
   345  		return true
   346  	}
   347  	return false
   348  
   349  }
   350  
   351  //Key key
   352  func (dbit *PegasusIt) Key() []byte {
   353  	if dbit.index >= 0 && dbit.index < len(dbit.vals) {
   354  		return dbit.vals[dbit.index].SortKey
   355  	}
   356  	return nil
   357  
   358  }
   359  
   360  //Value value
   361  func (dbit *PegasusIt) Value() []byte {
   362  	if dbit.index >= len(dbit.vals) {
   363  		slog.Error("get iterator value error: index out of bounds")
   364  		return nil
   365  	}
   366  
   367  	return dbit.vals[dbit.index].Value
   368  }
   369  
   370  func (dbit *PegasusIt) Error() error {
   371  	return nil
   372  }
   373  
   374  //ValueCopy 复制
   375  func (dbit *PegasusIt) ValueCopy() []byte {
   376  	v := dbit.Value()
   377  	value := make([]byte, len(v))
   378  	copy(value, v)
   379  	return value
   380  }
   381  
   382  //Valid 合法性
   383  func (dbit *PegasusIt) Valid() bool {
   384  	start := time.Now()
   385  	if dbit.index < 0 {
   386  		return false
   387  	}
   388  	if len(dbit.vals) <= dbit.index {
   389  		return false
   390  	}
   391  	key := dbit.vals[dbit.index].SortKey
   392  	pdbBench.read(1, time.Since(start))
   393  	return dbit.checkKey(key)
   394  }
   395  
   396  //PegasusBatch batch
   397  type PegasusBatch struct {
   398  	table    pegasus.TableConnector
   399  	batchset map[string][]byte
   400  	batchdel map[string][]byte
   401  	size     int
   402  }
   403  
   404  //NewBatch new
   405  func (db *PegasusDB) NewBatch(sync bool) Batch {
   406  	return &PegasusBatch{table: db.table, batchset: make(map[string][]byte), batchdel: make(map[string][]byte)}
   407  }
   408  
   409  //Set set
   410  func (db *PegasusBatch) Set(key, value []byte) {
   411  	db.batchset[string(key)] = value
   412  	delete(db.batchdel, string(key))
   413  	db.size += len(value)
   414  	db.size += len(key)
   415  }
   416  
   417  //Delete 删除
   418  func (db *PegasusBatch) Delete(key []byte) {
   419  	db.batchset[string(key)] = []byte("")
   420  	delete(db.batchset, string(key))
   421  	db.batchdel[string(key)] = key
   422  	db.size += len(key)
   423  }
   424  
   425  // 注意本方法的实现逻辑,因为ssdb没有提供删除和更新同时进行的批量操作;
   426  // 所以这里先执行更新操作(删除的KEY在这里会将VALUE设置为空);
   427  // 然后再执行删除操作;
   428  // 这样即使中间执行出错,也不会导致删除结果未写入的情况(值已经被置空);
   429  func (db *PegasusBatch) Write() error {
   430  	start := time.Now()
   431  
   432  	// 这里其实也需要对hashKey进行分别计算,然后分组查询,最后汇总结果
   433  	if len(db.batchset) > 0 {
   434  		var (
   435  			keysMap map[string][][]byte
   436  			valsMap map[string][][]byte
   437  			hashKey []byte
   438  			byteKey []byte
   439  			keys    [][]byte
   440  			values  [][]byte
   441  		)
   442  		keysMap = make(map[string][][]byte)
   443  		valsMap = make(map[string][][]byte)
   444  
   445  		// 首先,使用hashKey进行数据分组
   446  		for k, v := range db.batchset {
   447  			byteKey = []byte(k)
   448  			hashKey = getHashKey(byteKey)
   449  			if value, ok := keysMap[string(hashKey)]; ok {
   450  				keysMap[string(hashKey)] = append(value, byteKey)
   451  				valsMap[string(hashKey)] = append(valsMap[string(hashKey)], v)
   452  			} else {
   453  				keysMap[string(hashKey)] = [][]byte{byteKey}
   454  				valsMap[string(hashKey)] = [][]byte{v}
   455  			}
   456  		}
   457  		// 然后,再分别提交修改
   458  		for k, v := range keysMap {
   459  			keys = v
   460  			values = valsMap[k]
   461  
   462  			err := db.table.MultiSet(context.Background(), []byte(k), keys, values)
   463  			if err != nil {
   464  				slog.Error("Write (multi_set)", "error", err)
   465  				return err
   466  			}
   467  		}
   468  	}
   469  
   470  	if len(db.batchdel) > 0 {
   471  		var (
   472  			keysMap map[string][][]byte
   473  			hashKey []byte
   474  			byteKey []byte
   475  		)
   476  		keysMap = make(map[string][][]byte)
   477  
   478  		// 首先,使用hashKey进行数据分组
   479  		for k := range db.batchdel {
   480  			byteKey = []byte(k)
   481  			hashKey = getHashKey(byteKey)
   482  			if value, ok := keysMap[string(hashKey)]; ok {
   483  				keysMap[string(hashKey)] = append(value, byteKey)
   484  			} else {
   485  				keysMap[string(hashKey)] = [][]byte{byteKey}
   486  			}
   487  		}
   488  
   489  		// 然后,再分别提交删除
   490  		for k, v := range keysMap {
   491  			err := db.table.MultiDel(context.Background(), []byte(k), v)
   492  			if err != nil {
   493  				slog.Error("Write (multi_del)", "error", err)
   494  				return err
   495  			}
   496  		}
   497  	}
   498  
   499  	pdbBench.write(len(db.batchset)+len(db.batchdel), time.Since(start))
   500  	return nil
   501  }
   502  
   503  //ValueSize value批长度
   504  func (db *PegasusBatch) ValueSize() int {
   505  	return db.size
   506  }
   507  
   508  //ValueLen  batch数量
   509  func (db *PegasusBatch) ValueLen() int {
   510  	return len(db.batchset)
   511  }
   512  
   513  //Reset 重置
   514  func (db *PegasusBatch) Reset() {
   515  	db.batchset = make(map[string][]byte)
   516  	db.batchdel = make(map[string][]byte)
   517  	db.size = 0
   518  }
   519  
   520  // UpdateWriteSync ...
   521  func (db *PegasusBatch) UpdateWriteSync(sync bool) {
   522  }
   523  
   524  func getHashKey(key []byte) []byte {
   525  	if len(key) <= HashKeyLen {
   526  		return key
   527  	}
   528  	return key[:HashKeyLen]
   529  }