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

     1  package kv
     2  
     3  import (
     4  	"errors"
     5  	"github.com/angenalZZZ/gofunc/f"
     6  	"github.com/syndtr/goleveldb/leveldb"
     7  	"github.com/syndtr/goleveldb/leveldb/iterator"
     8  	"github.com/syndtr/goleveldb/leveldb/opt"
     9  	"github.com/syndtr/goleveldb/leveldb/util"
    10  	"io/ioutil"
    11  	"os"
    12  	"strconv"
    13  	"strings"
    14  	"time"
    15  )
    16  
    17  // LevelDB represents a leveldb db implementation.
    18  type LevelDB struct {
    19  	DB     *leveldb.DB
    20  	locker *f.Locker
    21  }
    22  
    23  // Open Opens the specified path.
    24  func (db *LevelDB) Open(path ...string) error {
    25  	filename := ""
    26  	if len(path) > 0 {
    27  		filename = path[0]
    28  	} else {
    29  		filename, _ = ioutil.TempDir(os.TempDir(), "")
    30  	}
    31  	var err error
    32  	db.DB, err = leveldb.OpenFile(filename, nil)
    33  	if err != nil {
    34  		return err
    35  	}
    36  	db.locker = f.NewLocker()
    37  	return nil
    38  }
    39  
    40  // Size gets the size of the database in bytes.
    41  func (db *LevelDB) Size() int64 {
    42  	var stats leveldb.DBStats
    43  	if nil != db.DB.Stats(&stats) {
    44  		return -1
    45  	}
    46  	size := int64(0)
    47  	for _, v := range stats.LevelSizes {
    48  		size += v
    49  	}
    50  	return size
    51  }
    52  
    53  // Incr increment the key by the specified value.
    54  func (db *LevelDB) Incr(k string, by int64) (int64, error) {
    55  	db.locker.Lock(k)
    56  	defer db.locker.Unlock(k)
    57  
    58  	val, err := db.get(k)
    59  	if err != nil || val == "" {
    60  		val = "0"
    61  	}
    62  
    63  	valFloat, _ := strconv.ParseInt(val, 10, 64)
    64  	valFloat += by
    65  
    66  	err = db.set(k, strconv.FormatInt(valFloat, 10), -1)
    67  	if err != nil {
    68  		return 0, err
    69  	}
    70  
    71  	return valFloat, nil
    72  }
    73  
    74  // Set sets a key with the specified value and optional ttl.seconds
    75  func (db *LevelDB) Set(k, v string, ttl int) error {
    76  	return db.set(k, v, ttl)
    77  }
    78  
    79  // SetBytes sets a key with the specified value and optional ttl.seconds
    80  func (db *LevelDB) SetBytes(k, v []byte, ttl int) error {
    81  	return db.set(f.String(k), f.String(v), ttl)
    82  }
    83  
    84  // MSet sets multiple key-value pairs.
    85  func (db *LevelDB) MSet(data map[string]string) error {
    86  	batch := new(leveldb.Batch)
    87  	for k, v := range data {
    88  		v = "0;" + v
    89  		batch.Put(f.Bytes(k), f.Bytes(v))
    90  	}
    91  	return db.DB.Write(batch, nil)
    92  }
    93  
    94  // Get fetches the value of the specified k.
    95  func (db *LevelDB) Get(k string) (string, error) {
    96  	return db.get(k)
    97  }
    98  
    99  // GetBytes fetches the value of the specified k.
   100  func (db *LevelDB) GetBytes(k []byte) ([]byte, error) {
   101  	data, err := db.get(f.String(k))
   102  	if err != nil {
   103  		return []byte{}, err
   104  	}
   105  	return f.Bytes(data), nil
   106  }
   107  
   108  // MGet fetch multiple values of the specified keys.
   109  func (db *LevelDB) MGet(keys []string) (data []string) {
   110  	data = make([]string, 0, len(keys))
   111  	for _, key := range keys {
   112  		val, err := db.get(key)
   113  		if err != nil {
   114  			data = append(data, "")
   115  			continue
   116  		}
   117  		data = append(data, val)
   118  	}
   119  	return data
   120  }
   121  
   122  // TTL gets the time.seconds to live of the specified key's value.
   123  func (db *LevelDB) TTL(key string) int64 {
   124  	item, err := db.DB.Get(f.Bytes(key), nil)
   125  	if err != nil {
   126  		return -2
   127  	}
   128  
   129  	parts := strings.SplitN(f.String(item), ";", 2)
   130  	exp, _ := strconv.ParseInt(parts[0], 10, 0)
   131  	if exp == 0 {
   132  		return -1
   133  	}
   134  
   135  	now := time.Now().Unix()
   136  	if now >= exp {
   137  		return -2
   138  	}
   139  
   140  	return (exp - now) / int64(time.Second)
   141  }
   142  
   143  // Del removes key(s) from the store.
   144  func (db *LevelDB) Del(keys []string) error {
   145  	batch := new(leveldb.Batch)
   146  	for _, key := range keys {
   147  		batch.Delete(f.Bytes(key))
   148  	}
   149  	return db.DB.Write(batch, nil)
   150  }
   151  
   152  // Close ...
   153  func (db *LevelDB) Close() error {
   154  	return db.DB.Close()
   155  }
   156  
   157  // Keys gets matched keys.
   158  func (db *LevelDB) Keys(prefix ...string) []string {
   159  	ro := &opt.ReadOptions{DontFillCache: true}
   160  	var prefixBytes []byte
   161  	var keys []string
   162  	l := len(prefix)
   163  	if l > 0 {
   164  		prefixBytes = f.Bytes(prefix[0])
   165  	}
   166  	var iter iterator.Iterator
   167  	if l == 0 {
   168  		iter = db.DB.NewIterator(nil, ro)
   169  	} else {
   170  		iter = db.DB.NewIterator(util.BytesPrefix(prefixBytes), ro)
   171  	}
   172  	defer iter.Release()
   173  	for iter.Next() {
   174  		if iter.Error() != nil {
   175  			continue
   176  		}
   177  		keys = append(keys, f.String(iter.Key()))
   178  	}
   179  	return keys
   180  }
   181  
   182  // GC runs the garbage collector.
   183  func (db *LevelDB) GC() error {
   184  	return db.DB.CompactRange(util.Range{})
   185  }
   186  
   187  func (db *LevelDB) get(k string) (string, error) {
   188  	var data string
   189  	var err error
   190  	var del bool
   191  
   192  	item, err := db.DB.Get(f.Bytes(k), nil)
   193  	if err != nil {
   194  		return "", err
   195  	}
   196  
   197  	parts := strings.SplitN(f.String(item), ";", 2)
   198  	expires, actual := parts[0], parts[1]
   199  
   200  	if exp, _ := strconv.ParseInt(expires, 10, 0); exp > 0 && time.Now().Unix() >= exp {
   201  		del = true
   202  		err = errors.New("key not found")
   203  	} else {
   204  		data = actual
   205  	}
   206  
   207  	if del {
   208  		_ = db.DB.Delete(f.Bytes(k), nil)
   209  		return data, err
   210  	}
   211  
   212  	return data, nil
   213  }
   214  
   215  func (db *LevelDB) set(k, v string, ttl int) error {
   216  	var expires int64
   217  	if ttl > 0 {
   218  		expires = time.Now().Add(time.Duration(ttl) * time.Second).Unix()
   219  	}
   220  	v = strconv.FormatInt(expires, 10) + ";" + v
   221  	return db.DB.Put(f.Bytes(k), f.Bytes(v), nil)
   222  }