github.com/slspeek/camlistore_namedsearch@v0.0.0-20140519202248-ed6f70f7721a/pkg/sorted/kvfile/kvfile.go (about)

     1  /*
     2  Copyright 2013 The Camlistore Authors.
     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 kvfile provides an implementation of sorted.KeyValue
    18  // on top of a single mutable database file on disk using
    19  // github.com/cznic/kv.
    20  package kvfile
    21  
    22  import (
    23  	"bytes"
    24  	"errors"
    25  	"fmt"
    26  	"io"
    27  	"log"
    28  	"os"
    29  	"sync"
    30  
    31  	"camlistore.org/pkg/jsonconfig"
    32  	"camlistore.org/pkg/kvutil"
    33  	"camlistore.org/pkg/sorted"
    34  
    35  	"camlistore.org/third_party/github.com/cznic/kv"
    36  )
    37  
    38  var _ sorted.Wiper = (*kvis)(nil)
    39  
    40  func init() {
    41  	sorted.RegisterKeyValue("kv", newKeyValueFromJSONConfig)
    42  }
    43  
    44  // NewStorage is a convenience that calls newKeyValueFromJSONConfig
    45  // with file as the kv storage file.
    46  func NewStorage(file string) (sorted.KeyValue, error) {
    47  	return newKeyValueFromJSONConfig(jsonconfig.Obj{"file": file})
    48  }
    49  
    50  // newKeyValueFromJSONConfig returns a KeyValue implementation on top of a
    51  // github.com/cznic/kv file.
    52  func newKeyValueFromJSONConfig(cfg jsonconfig.Obj) (sorted.KeyValue, error) {
    53  	file := cfg.RequiredString("file")
    54  	if err := cfg.Validate(); err != nil {
    55  		return nil, err
    56  	}
    57  	opts := &kv.Options{}
    58  	db, err := kvutil.Open(file, opts)
    59  	if err != nil {
    60  		return nil, err
    61  	}
    62  	is := &kvis{
    63  		db:   db,
    64  		opts: opts,
    65  		path: file,
    66  	}
    67  	return is, nil
    68  }
    69  
    70  type kvis struct {
    71  	path string
    72  	db   *kv.DB
    73  	opts *kv.Options
    74  	txmu sync.Mutex
    75  }
    76  
    77  // TODO: use bytepool package.
    78  func getBuf(n int) []byte { return make([]byte, n) }
    79  func putBuf([]byte)       {}
    80  
    81  func (is *kvis) Get(key string) (string, error) {
    82  	buf := getBuf(200)
    83  	defer putBuf(buf)
    84  	val, err := is.db.Get(buf, []byte(key))
    85  	if err != nil {
    86  		return "", err
    87  	}
    88  	if val == nil {
    89  		return "", sorted.ErrNotFound
    90  	}
    91  	return string(val), nil
    92  }
    93  
    94  func (is *kvis) Set(key, value string) error {
    95  	return is.db.Set([]byte(key), []byte(value))
    96  }
    97  
    98  func (is *kvis) Delete(key string) error {
    99  	return is.db.Delete([]byte(key))
   100  }
   101  
   102  func (is *kvis) Find(start, end string) sorted.Iterator {
   103  	it := &iter{
   104  		db:       is.db,
   105  		startKey: start,
   106  		endKey:   []byte(end),
   107  	}
   108  	it.enum, _, it.err = it.db.Seek([]byte(start))
   109  	return it
   110  }
   111  
   112  func (is *kvis) BeginBatch() sorted.BatchMutation {
   113  	return sorted.NewBatchMutation()
   114  }
   115  
   116  func (is *kvis) Wipe() error {
   117  	// Unlock the already open DB.
   118  	if err := is.db.Close(); err != nil {
   119  		return err
   120  	}
   121  	if err := os.Remove(is.path); err != nil {
   122  		return err
   123  	}
   124  
   125  	db, err := kv.Create(is.path, is.opts)
   126  	if err != nil {
   127  		return fmt.Errorf("error creating %s: %v", is.path, err)
   128  	}
   129  	is.db = db
   130  	return nil
   131  }
   132  
   133  type batch interface {
   134  	Mutations() []sorted.Mutation
   135  }
   136  
   137  func (is *kvis) CommitBatch(bm sorted.BatchMutation) error {
   138  	b, ok := bm.(batch)
   139  	if !ok {
   140  		return errors.New("invalid batch type")
   141  	}
   142  	is.txmu.Lock()
   143  	defer is.txmu.Unlock()
   144  
   145  	good := false
   146  	defer func() {
   147  		if !good {
   148  			is.db.Rollback()
   149  		}
   150  	}()
   151  
   152  	if err := is.db.BeginTransaction(); err != nil {
   153  		return err
   154  	}
   155  	for _, m := range b.Mutations() {
   156  		if m.IsDelete() {
   157  			if err := is.db.Delete([]byte(m.Key())); err != nil {
   158  				return err
   159  			}
   160  		} else {
   161  			if err := is.db.Set([]byte(m.Key()), []byte(m.Value())); err != nil {
   162  				return err
   163  			}
   164  		}
   165  	}
   166  
   167  	good = true
   168  	return is.db.Commit()
   169  }
   170  
   171  func (is *kvis) Close() error {
   172  	log.Printf("Closing kvfile database %s", is.path)
   173  	return is.db.Close()
   174  }
   175  
   176  type iter struct {
   177  	db       *kv.DB
   178  	startKey string
   179  	endKey   []byte
   180  
   181  	enum *kv.Enumerator
   182  
   183  	valid      bool
   184  	key, val   []byte
   185  	skey, sval *string // non-nil if valid
   186  
   187  	err    error
   188  	closed bool
   189  }
   190  
   191  func (it *iter) Close() error {
   192  	it.closed = true
   193  	return it.err
   194  }
   195  
   196  func (it *iter) KeyBytes() []byte {
   197  	if !it.valid {
   198  		panic("not valid")
   199  	}
   200  	return it.key
   201  }
   202  
   203  func (it *iter) Key() string {
   204  	if !it.valid {
   205  		panic("not valid")
   206  	}
   207  	if it.skey != nil {
   208  		return *it.skey
   209  	}
   210  	str := string(it.key)
   211  	it.skey = &str
   212  	return str
   213  }
   214  
   215  func (it *iter) ValueBytes() []byte {
   216  	if !it.valid {
   217  		panic("not valid")
   218  	}
   219  	return it.val
   220  }
   221  
   222  func (it *iter) Value() string {
   223  	if !it.valid {
   224  		panic("not valid")
   225  	}
   226  	if it.sval != nil {
   227  		return *it.sval
   228  	}
   229  	str := string(it.val)
   230  	it.sval = &str
   231  	return str
   232  }
   233  
   234  func (it *iter) end() bool {
   235  	it.valid = false
   236  	it.closed = true
   237  	return false
   238  }
   239  
   240  func (it *iter) Next() (ret bool) {
   241  	if it.err != nil {
   242  		return false
   243  	}
   244  	if it.closed {
   245  		panic("Next called after Next returned value")
   246  	}
   247  	it.skey, it.sval = nil, nil
   248  	var err error
   249  	it.key, it.val, err = it.enum.Next()
   250  	if err == io.EOF {
   251  		it.err = nil
   252  		return it.end()
   253  	}
   254  	if err != nil {
   255  		it.err = err
   256  		return it.end()
   257  	}
   258  	if len(it.endKey) > 0 && bytes.Compare(it.key, it.endKey) >= 0 {
   259  		return it.end()
   260  	}
   261  	it.valid = true
   262  	return true
   263  }