kythe.io@v0.0.68-0.20240422202219-7225dbc01741/kythe/go/storage/leveldb/leveldb.go (about)

     1  /*
     2   * Copyright 2014 The Kythe Authors. All rights reserved.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *   http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  // Package leveldb implements a graphstore.Service using a LevelDB backend
    18  // database.
    19  package leveldb // import "kythe.io/kythe/go/storage/leveldb"
    20  
    21  import (
    22  	"bytes"
    23  	"context"
    24  	"fmt"
    25  	"io"
    26  	"os"
    27  
    28  	"kythe.io/kythe/go/services/graphstore"
    29  	"kythe.io/kythe/go/storage/gsutil"
    30  	"kythe.io/kythe/go/storage/keyvalue"
    31  
    32  	"github.com/jmhodges/levigo"
    33  )
    34  
    35  func init() {
    36  	gsutil.Register("leveldb", func(spec string) (graphstore.Service, error) { return OpenGraphStore(spec, nil) })
    37  	gsutil.RegisterDefault("leveldb")
    38  }
    39  
    40  // levelDB is a wrapper around a levigo.DB that implements keyvalue.DB
    41  type levelDB struct {
    42  	db    *levigo.DB
    43  	cache *levigo.Cache
    44  
    45  	// save options to reduce number of allocations during high load
    46  	readOpts      *levigo.ReadOptions
    47  	largeReadOpts *levigo.ReadOptions
    48  	writeOpts     *levigo.WriteOptions
    49  }
    50  
    51  // CompactRange runs a manual compaction on the Range of keys given.
    52  // If r == nil, the entire table will be compacted.
    53  func CompactRange(path string, r *keyvalue.Range) error {
    54  	options := levigo.NewOptions()
    55  	defer options.Close()
    56  	db, err := levigo.Open(path, options)
    57  	if err != nil {
    58  		return err
    59  	}
    60  	lr := levigo.Range{}
    61  	if r != nil {
    62  		lr.Start = r.Start
    63  		lr.Limit = r.End
    64  	}
    65  	db.CompactRange(lr)
    66  	db.Close()
    67  	return nil
    68  }
    69  
    70  // DefaultOptions is the default Options struct passed to Open when not
    71  // otherwise given one.
    72  var DefaultOptions = &Options{
    73  	CacheCapacity:   512 * 1024 * 1024, // 512mb
    74  	WriteBufferSize: 60 * 1024 * 1024,  // 60mb
    75  }
    76  
    77  // Options for customizing a LevelDB backend.
    78  type Options struct {
    79  	// CacheCapacity is the caching capacity (in bytes) used for the LevelDB.
    80  	CacheCapacity int
    81  
    82  	// CacheLargeReads determines whether to use the cache for large reads. This
    83  	// is usually discouraged but may be useful when the entire LevelDB is known
    84  	// to fit into the cache.
    85  	CacheLargeReads bool
    86  
    87  	// WriteBufferSize is the number of bytes the database will build up in memory
    88  	// (backed by a disk log) before writing to the on-disk table.
    89  	WriteBufferSize int
    90  
    91  	// MustExist ensures that the given database exists before opening it.  If
    92  	// false and the database does not exist, it will be created.
    93  	MustExist bool
    94  }
    95  
    96  // ValidDB determines if the given path could be a LevelDB database.
    97  func ValidDB(path string) bool {
    98  	stat, err := os.Stat(path)
    99  	return os.IsNotExist(err) || (err == nil && stat.IsDir())
   100  }
   101  
   102  // OpenGraphStore returns a graphstore.Service backed by a LevelDB database at
   103  // the given filepath.  If opts==nil, the DefaultOptions are used.
   104  func OpenGraphStore(path string, opts *Options) (graphstore.Service, error) {
   105  	db, err := Open(path, opts)
   106  	if err != nil {
   107  		return nil, err
   108  	}
   109  	return keyvalue.NewGraphStore(db), nil
   110  }
   111  
   112  // Open returns a keyvalue DB backed by a LevelDB database at the given
   113  // filepath.  If opts==nil, the DefaultOptions are used.
   114  func Open(path string, opts *Options) (keyvalue.DB, error) {
   115  	if opts == nil {
   116  		opts = DefaultOptions
   117  	}
   118  
   119  	options := levigo.NewOptions()
   120  	defer options.Close()
   121  	cache := levigo.NewLRUCache(opts.CacheCapacity)
   122  	options.SetCache(cache)
   123  	options.SetCreateIfMissing(!opts.MustExist)
   124  	if opts.WriteBufferSize > 0 {
   125  		options.SetWriteBufferSize(opts.WriteBufferSize)
   126  	}
   127  	db, err := levigo.Open(path, options)
   128  	if err != nil {
   129  		return nil, fmt.Errorf("could not open LevelDB at %q: %v", path, err)
   130  	}
   131  	largeReadOpts := levigo.NewReadOptions()
   132  	largeReadOpts.SetFillCache(opts.CacheLargeReads)
   133  	return &levelDB{
   134  		db:            db,
   135  		cache:         cache,
   136  		readOpts:      levigo.NewReadOptions(),
   137  		largeReadOpts: largeReadOpts,
   138  		writeOpts:     levigo.NewWriteOptions(),
   139  	}, nil
   140  }
   141  
   142  // Close will close the underlying LevelDB database.
   143  func (s *levelDB) Close(_ context.Context) error {
   144  	s.db.Close()
   145  	s.cache.Close()
   146  	s.readOpts.Close()
   147  	s.largeReadOpts.Close()
   148  	s.writeOpts.Close()
   149  	return nil
   150  }
   151  
   152  type snapshot struct {
   153  	db *levigo.DB
   154  	s  *levigo.Snapshot
   155  }
   156  
   157  // NewSnapshot implements part of the keyvalue.DB interface.
   158  func (s *levelDB) NewSnapshot(_ context.Context) keyvalue.Snapshot {
   159  	return &snapshot{s.db, s.db.NewSnapshot()}
   160  }
   161  
   162  // Close implements part of the keyvalue.Snapshot interface.
   163  func (s *snapshot) Close() error {
   164  	s.db.ReleaseSnapshot(s.s)
   165  	return nil
   166  }
   167  
   168  // Writer implements part of the keyvalue.DB interface.
   169  func (s *levelDB) Writer(_ context.Context) (keyvalue.Writer, error) {
   170  	return &writer{s, levigo.NewWriteBatch()}, nil
   171  }
   172  
   173  // Get implements part of the keyvalue.DB interface.
   174  func (s *levelDB) Get(_ context.Context, key []byte, opts *keyvalue.Options) ([]byte, error) {
   175  	ro := s.readOptions(opts)
   176  	if ro != s.largeReadOpts && ro != s.readOpts {
   177  		defer ro.Close()
   178  	}
   179  	v, err := s.db.Get(ro, key)
   180  	if err != nil {
   181  		return nil, err
   182  	} else if v == nil {
   183  		return nil, io.EOF
   184  	}
   185  	return v, nil
   186  }
   187  
   188  // ScanPrefix implements part of the keyvalue.DB interface.
   189  func (s *levelDB) ScanPrefix(_ context.Context, prefix []byte, opts *keyvalue.Options) (keyvalue.Iterator, error) {
   190  	iter, ro := s.iterator(opts)
   191  	if len(prefix) == 0 {
   192  		iter.SeekToFirst()
   193  	} else {
   194  		iter.Seek(prefix)
   195  	}
   196  	return &iterator{iter, ro, prefix, nil}, nil
   197  }
   198  
   199  // ScanRange implements part of the keyvalue.DB interface.
   200  func (s *levelDB) ScanRange(_ context.Context, r *keyvalue.Range, opts *keyvalue.Options) (keyvalue.Iterator, error) {
   201  	iter, ro := s.iterator(opts)
   202  	iter.Seek(r.Start)
   203  	return &iterator{iter, ro, nil, r}, nil
   204  }
   205  
   206  func (s *levelDB) readOptions(opts *keyvalue.Options) *levigo.ReadOptions {
   207  	if snap := opts.GetSnapshot(); snap != nil {
   208  		ro := levigo.NewReadOptions()
   209  		ro.SetSnapshot(snap.(*snapshot).s)
   210  		ro.SetFillCache(!opts.IsLargeRead())
   211  		return ro
   212  	}
   213  	if opts.IsLargeRead() {
   214  		return s.largeReadOpts
   215  	}
   216  	return s.readOpts
   217  }
   218  
   219  // iterator creates a new levigo Iterator based on the given options.  It also
   220  // returns any ReadOptions that should be Closed once the Iterator is Closed.
   221  func (s *levelDB) iterator(opts *keyvalue.Options) (*levigo.Iterator, *levigo.ReadOptions) {
   222  	ro := s.readOptions(opts)
   223  	it := s.db.NewIterator(ro)
   224  	if ro == s.largeReadOpts || ro == s.readOpts {
   225  		ro = nil
   226  	}
   227  	return it, ro
   228  }
   229  
   230  type writer struct {
   231  	s *levelDB
   232  	*levigo.WriteBatch
   233  }
   234  
   235  // Write implements part of the keyvalue.Writer interface.
   236  func (w *writer) Write(key, val []byte) error {
   237  	w.Put(key, val)
   238  	return nil
   239  }
   240  
   241  // Close implements part of the keyvalue.Writer interface.
   242  func (w *writer) Close() error {
   243  	if err := w.s.db.Write(w.s.writeOpts, w.WriteBatch); err != nil {
   244  		return err
   245  	}
   246  	w.WriteBatch.Close()
   247  	return nil
   248  }
   249  
   250  type iterator struct {
   251  	it   *levigo.Iterator
   252  	opts *levigo.ReadOptions
   253  
   254  	prefix []byte
   255  	r      *keyvalue.Range
   256  }
   257  
   258  // Close implements part of the keyvalue.Iterator interface.
   259  func (i iterator) Close() error {
   260  	if i.opts != nil {
   261  		i.opts.Close()
   262  	}
   263  	i.it.Close()
   264  	return nil
   265  }
   266  
   267  // Next implements part of the keyvalue.Iterator interface.
   268  func (i iterator) Next() ([]byte, []byte, error) {
   269  	if !i.it.Valid() {
   270  		if err := i.it.GetError(); err != nil {
   271  			return nil, nil, err
   272  		}
   273  		return nil, nil, io.EOF
   274  	}
   275  	key, val := i.it.Key(), i.it.Value()
   276  	if (i.r == nil && !bytes.HasPrefix(key, i.prefix)) || (i.r != nil && bytes.Compare(key, i.r.End) >= 0) {
   277  		return nil, nil, io.EOF
   278  	}
   279  	i.it.Next()
   280  	return key, val, nil
   281  }
   282  
   283  // Seek implements part of the keyvalue.Iterator interface.
   284  func (i *iterator) Seek(k []byte) error {
   285  	i.it.Seek(k)
   286  	if !i.it.Valid() {
   287  		if err := i.it.GetError(); err != nil {
   288  			return err
   289  		}
   290  		return io.EOF
   291  	}
   292  	return nil
   293  }