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