github.com/angenalZZZ/gofunc@v0.0.0-20210507121333-48ff1be3917b/data/kv/kv-bunt-db.go (about)

     1  package kv
     2  
     3  import (
     4  	"github.com/angenalZZZ/gofunc/f"
     5  	"github.com/tidwall/buntdb"
     6  	"strconv"
     7  	"strings"
     8  	"time"
     9  )
    10  
    11  // BuntDB is a low-level, in-memory, key/value store.
    12  // It persists to disk, is ACID compliant, and uses locking for multiple readers and a single writer.
    13  // It supports custom indexes and geo-spatial data.
    14  // It's ideal for projects that need a dependable database and favor speed over data size.
    15  type BuntDB struct {
    16  	DB     *buntdb.DB
    17  	locker *f.Locker
    18  }
    19  
    20  // Open Opens the specified path, default in memory.
    21  func (db *BuntDB) Open(path ...string) error {
    22  	filename := ":memory:"
    23  	if len(path) > 0 {
    24  		filename = path[0]
    25  	} else {
    26  		//filename, _ = ioutil.TempDir(os.TempDir(), "")
    27  	}
    28  	var err error
    29  	db.DB, err = buntdb.Open(filename)
    30  	if err != nil {
    31  		return err
    32  	}
    33  	db.locker = f.NewLocker()
    34  	return nil
    35  }
    36  
    37  // Size gets the size of the database in bytes.
    38  func (db *BuntDB) Size() int64 {
    39  	return 0
    40  }
    41  
    42  // Incr increment the key by the specified value.
    43  func (db *BuntDB) Incr(k string, by int64) (int64, error) {
    44  	db.locker.Lock(k)
    45  	defer db.locker.Unlock(k)
    46  
    47  	val, err := db.get(k)
    48  	if err != nil || val == "" {
    49  		val = "0"
    50  	}
    51  
    52  	valFloat, _ := strconv.ParseInt(val, 10, 64)
    53  	valFloat += by
    54  
    55  	err = db.set(k, strconv.FormatInt(valFloat, 10), -1)
    56  	if err != nil {
    57  		return 0, err
    58  	}
    59  
    60  	return valFloat, nil
    61  }
    62  
    63  // Set sets a key with the specified value and optional ttl.seconds
    64  func (db *BuntDB) Set(k, v string, ttl int) error {
    65  	return db.set(k, v, ttl)
    66  }
    67  
    68  // SetBytes sets a key with the specified value and optional ttl.seconds
    69  func (db *BuntDB) SetBytes(k, v []byte, ttl int) error {
    70  	return db.set(f.String(k), f.String(v), ttl)
    71  }
    72  
    73  // MSet sets multiple key-value pairs.
    74  func (db *BuntDB) MSet(data map[string]string) (err error) {
    75  	err = db.DB.Update(func(tx *buntdb.Tx) (err1 error) {
    76  		for k, v := range data {
    77  			_, _, err1 = tx.Set(k, v, nil)
    78  		}
    79  		return
    80  	})
    81  	return
    82  }
    83  
    84  // Get fetches the value of the specified k.
    85  func (db *BuntDB) Get(k string) (string, error) {
    86  	return db.get(k)
    87  }
    88  
    89  // GetBytes fetches the value of the specified k.
    90  func (db *BuntDB) GetBytes(k []byte) ([]byte, error) {
    91  	data, err := db.get(f.String(k))
    92  	if err != nil {
    93  		return []byte{}, err
    94  	}
    95  	return f.Bytes(data), nil
    96  }
    97  
    98  // MGet fetch multiple values of the specified keys.
    99  func (db *BuntDB) MGet(keys []string) (data []string) {
   100  	data = make([]string, 0, len(keys))
   101  	for _, key := range keys {
   102  		val, err := db.get(key)
   103  		if err != nil {
   104  			data = append(data, "")
   105  			continue
   106  		}
   107  		data = append(data, val)
   108  	}
   109  	return data
   110  }
   111  
   112  // TTL gets the time.seconds to live of the specified key's value.
   113  func (db *BuntDB) TTL(key string) int64 {
   114  	ts := time.Nanosecond
   115  	_ = db.DB.View(func(tx *buntdb.Tx) (err1 error) {
   116  		ts, err1 = tx.TTL(key)
   117  		return
   118  	})
   119  	return int64(ts.Seconds())
   120  }
   121  
   122  // Del removes key(s) from the store.
   123  func (db *BuntDB) Del(keys []string) (err error) {
   124  	err = db.DB.Update(func(tx *buntdb.Tx) (err1 error) {
   125  		for _, k := range keys {
   126  			_, err1 = tx.Delete(k)
   127  		}
   128  		return
   129  	})
   130  	return
   131  }
   132  
   133  // Close ...
   134  func (db *BuntDB) Close() error {
   135  	return db.DB.Close()
   136  }
   137  
   138  // Keys gets matched keys.
   139  func (db *BuntDB) Keys(prefix ...string) []string {
   140  	var prefix1 string
   141  	var keys []string
   142  	l := len(prefix)
   143  	if l > 0 {
   144  		prefix1 = prefix[0]
   145  		l = len(prefix1)
   146  	}
   147  	_ = db.DB.View(func(tx *buntdb.Tx) (err1 error) {
   148  		err1 = tx.AscendKeys("", func(key, _ string) bool {
   149  			if l == 0 || strings.HasPrefix(key, prefix1) {
   150  				keys = append(keys, key)
   151  			}
   152  			return true
   153  		})
   154  		return
   155  	})
   156  	return keys
   157  }
   158  
   159  // GC runs the garbage collector.
   160  func (db *BuntDB) GC() error {
   161  	return db.DB.Shrink()
   162  }
   163  
   164  func (db *BuntDB) get(k string) (data string, err error) {
   165  	err = db.DB.View(func(tx *buntdb.Tx) (err1 error) {
   166  		data, err1 = tx.Get(k)
   167  		return
   168  	})
   169  	return
   170  }
   171  
   172  func (db *BuntDB) set(k, v string, ttl int) (err error) {
   173  	err = db.DB.Update(func(tx *buntdb.Tx) (err1 error) {
   174  		_, _, err1 = tx.Set(k, v, &buntdb.SetOptions{Expires: true, TTL: time.Duration(ttl) * time.Second})
   175  		return
   176  	})
   177  	return
   178  }