github.com/psiphon-labs/psiphon-tunnel-core@v2.0.28+incompatible/psiphon/dataStore_badger.go (about)

     1  //go:build PSIPHON_USE_BADGER_DB
     2  // +build PSIPHON_USE_BADGER_DB
     3  
     4  /*
     5   * Copyright (c) 2018, Psiphon Inc.
     6   * All rights reserved.
     7   *
     8   * This program is free software: you can redistribute it and/or modify
     9   * it under the terms of the GNU General Public License as published by
    10   * the Free Software Foundation, either version 3 of the License, or
    11   * (at your option) any later version.
    12   *
    13   * This program is distributed in the hope that it will be useful,
    14   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    15   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    16   * GNU General Public License for more details.
    17   *
    18   * You should have received a copy of the GNU General Public License
    19   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    20   *
    21   */
    22  
    23  package psiphon
    24  
    25  import (
    26  	"os"
    27  	"path/filepath"
    28  
    29  	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/errors"
    30  	"github.com/dgraph-io/badger"
    31  	"github.com/dgraph-io/badger/options"
    32  )
    33  
    34  const (
    35  	DATA_STORE_DIRECTORY = "psiphon.badgerdb"
    36  )
    37  
    38  type datastoreDB struct {
    39  	badgerDB *badger.DB
    40  }
    41  
    42  type datastoreTx struct {
    43  	badgerTx *badger.Txn
    44  }
    45  
    46  type datastoreBucket struct {
    47  	name []byte
    48  	tx   *datastoreTx
    49  }
    50  
    51  type datastoreCursor struct {
    52  	badgerIterator *badger.Iterator
    53  	prefix         []byte
    54  }
    55  
    56  func datastoreOpenDB(
    57  	rootDataDirectory string, _ bool) (*datastoreDB, error) {
    58  
    59  	dbDirectory := filepath.Join(rootDataDirectory, "psiphon.badgerdb")
    60  
    61  	err := os.MkdirAll(dbDirectory, 0700)
    62  	if err != nil {
    63  		return nil, errors.Trace(err)
    64  	}
    65  
    66  	opts := badger.DefaultOptions
    67  
    68  	opts.Dir = dbDirectory
    69  	opts.ValueDir = dbDirectory
    70  
    71  	opts.TableLoadingMode = options.FileIO
    72  	opts.ValueLogLoadingMode = options.FileIO
    73  	opts.MaxTableSize = 1 << 16
    74  	opts.ValueLogFileSize = 1 << 20
    75  	opts.NumMemtables = 1
    76  	opts.NumLevelZeroTables = 1
    77  	opts.NumLevelZeroTablesStall = 2
    78  	opts.NumCompactors = 1
    79  
    80  	db, err := badger.Open(opts)
    81  	if err != nil {
    82  		return nil, errors.Trace(err)
    83  	}
    84  
    85  	for {
    86  		if db.RunValueLogGC(0.5) != nil {
    87  			break
    88  		}
    89  	}
    90  
    91  	return &datastoreDB{badgerDB: db}, nil
    92  }
    93  
    94  func (db *datastoreDB) close() error {
    95  	return db.badgerDB.Close()
    96  }
    97  
    98  func (db *datastoreDB) getDataStoreMetrics() string {
    99  	// TODO: report metrics
   100  	return ""
   101  }
   102  
   103  func (db *datastoreDB) view(fn func(tx *datastoreTx) error) error {
   104  	return db.badgerDB.View(
   105  		func(tx *badger.Txn) error {
   106  			err := fn(&datastoreTx{badgerTx: tx})
   107  			if err != nil {
   108  				return errors.Trace(err)
   109  			}
   110  			return nil
   111  		})
   112  }
   113  
   114  func (db *datastoreDB) update(fn func(tx *datastoreTx) error) error {
   115  	return db.badgerDB.Update(
   116  		func(tx *badger.Txn) error {
   117  			err := fn(&datastoreTx{badgerTx: tx})
   118  			if err != nil {
   119  				return errors.Trace(err)
   120  			}
   121  			return nil
   122  		})
   123  }
   124  
   125  func (tx *datastoreTx) bucket(name []byte) *datastoreBucket {
   126  	return &datastoreBucket{
   127  		name: name,
   128  		tx:   tx,
   129  	}
   130  }
   131  
   132  func (tx *datastoreTx) clearBucket(name []byte) error {
   133  	b := tx.bucket(name)
   134  	c := b.cursor()
   135  	for key := c.firstKey(); key != nil; key = c.nextKey() {
   136  		err := tx.badgerTx.Delete(key)
   137  		if err != nil {
   138  			return errors.Trace(err)
   139  		}
   140  	}
   141  	return nil
   142  }
   143  
   144  func (b *datastoreBucket) get(key []byte) []byte {
   145  	keyWithPrefix := append(b.name, key...)
   146  	item, err := b.tx.badgerTx.Get(keyWithPrefix)
   147  	if err != nil {
   148  		if err != badger.ErrKeyNotFound {
   149  			// The original datastore interface does not return an error from
   150  			// Get, so emit notice.
   151  			NoticeWarning("get failed: %s: %s",
   152  				string(keyWithPrefix), errors.Trace(err))
   153  		}
   154  		return nil
   155  	}
   156  	value, err := item.Value()
   157  	if err != nil {
   158  		NoticeWarning("get failed: %s: %s",
   159  			string(keyWithPrefix), errors.Trace(err))
   160  		return nil
   161  	}
   162  	return value
   163  }
   164  
   165  func (b *datastoreBucket) put(key, value []byte) error {
   166  	keyWithPrefix := append(b.name, key...)
   167  	err := b.tx.badgerTx.Set(keyWithPrefix, value)
   168  	if err != nil {
   169  		return errors.Trace(err)
   170  	}
   171  	return nil
   172  }
   173  
   174  func (b *datastoreBucket) delete(key []byte) error {
   175  	keyWithPrefix := append(b.name, key...)
   176  	err := b.tx.badgerTx.Delete(keyWithPrefix)
   177  	if err != nil {
   178  		return errors.Trace(err)
   179  	}
   180  	return nil
   181  }
   182  
   183  func (b *datastoreBucket) cursor() *datastoreCursor {
   184  	opts := badger.DefaultIteratorOptions
   185  	opts.PrefetchValues = false
   186  	iterator := b.tx.badgerTx.NewIterator(opts)
   187  	return &datastoreCursor{badgerIterator: iterator, prefix: b.name}
   188  }
   189  
   190  func (c *datastoreCursor) firstKey() []byte {
   191  	c.badgerIterator.Seek(c.prefix)
   192  	return c.currentKey()
   193  }
   194  
   195  func (c *datastoreCursor) currentKey() []byte {
   196  	if !c.badgerIterator.ValidForPrefix(c.prefix) {
   197  		return nil
   198  	}
   199  	item := c.badgerIterator.Item()
   200  	return item.Key()[len(c.prefix):]
   201  }
   202  
   203  func (c *datastoreCursor) nextKey() []byte {
   204  	c.badgerIterator.Next()
   205  	return c.currentKey()
   206  }
   207  
   208  func (c *datastoreCursor) first() ([]byte, []byte) {
   209  	c.badgerIterator.Seek(c.prefix)
   210  	return c.current()
   211  }
   212  
   213  func (c *datastoreCursor) current() ([]byte, []byte) {
   214  	if !c.badgerIterator.ValidForPrefix(c.prefix) {
   215  		return nil, nil
   216  	}
   217  	item := c.badgerIterator.Item()
   218  	value, err := item.Value()
   219  	if err != nil {
   220  		return nil, nil
   221  	}
   222  	return item.Key()[len(c.prefix):], value
   223  }
   224  
   225  func (c *datastoreCursor) next() ([]byte, []byte) {
   226  	c.badgerIterator.Next()
   227  	return c.current()
   228  }
   229  
   230  func (c *datastoreCursor) close() {
   231  	c.badgerIterator.Close()
   232  }