github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/pingcap/tidb/kv/union_store.go (about)

     1  // Copyright 2015 PingCAP, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package kv
    15  
    16  import (
    17  	"bytes"
    18  
    19  	"github.com/insionng/yougam/libraries/juju/errors"
    20  )
    21  
    22  // UnionStore is a store that wraps a snapshot for read and a BufferStore for buffered write.
    23  // Also, it provides some transaction related utilities.
    24  type UnionStore interface {
    25  	MemBuffer
    26  	// CheckLazyConditionPairs loads all lazy values from store then checks if all values are matched.
    27  	// Lazy condition pairs should be checked before transaction commit.
    28  	CheckLazyConditionPairs() error
    29  	// WalkBuffer iterates all buffered kv pairs.
    30  	WalkBuffer(f func(k Key, v []byte) error) error
    31  	// SetOption sets an option with a value, when val is nil, uses the default
    32  	// value of this option.
    33  	SetOption(opt Option, val interface{})
    34  	// DelOption deletes an option.
    35  	DelOption(opt Option)
    36  }
    37  
    38  // Option is used for customizing kv store's behaviors during a transaction.
    39  type Option int
    40  
    41  // Options is an interface of a set of options. Each option is associated with a value.
    42  type Options interface {
    43  	// Get gets an option value.
    44  	Get(opt Option) (v interface{}, ok bool)
    45  }
    46  
    47  var (
    48  	p = newCache("memdb pool", 100, func() MemBuffer {
    49  		return NewMemDbBuffer()
    50  	})
    51  )
    52  
    53  // conditionPair is used to store lazy check condition.
    54  // If condition not match (value is not equal as expected one), returns err.
    55  type conditionPair struct {
    56  	key   Key
    57  	value []byte
    58  	err   error
    59  }
    60  
    61  // UnionStore is an in-memory Store which contains a buffer for write and a
    62  // snapshot for read.
    63  type unionStore struct {
    64  	*BufferStore
    65  	snapshot           Snapshot                    // for read
    66  	lazyConditionPairs map[string](*conditionPair) // for delay check
    67  	opts               options
    68  }
    69  
    70  // NewUnionStore builds a new UnionStore.
    71  func NewUnionStore(snapshot Snapshot) UnionStore {
    72  	return &unionStore{
    73  		BufferStore:        NewBufferStore(snapshot),
    74  		snapshot:           snapshot,
    75  		lazyConditionPairs: make(map[string](*conditionPair)),
    76  		opts:               make(map[Option]interface{}),
    77  	}
    78  }
    79  
    80  type lazyMemBuffer struct {
    81  	mb MemBuffer
    82  }
    83  
    84  func (lmb *lazyMemBuffer) Get(k Key) ([]byte, error) {
    85  	if lmb.mb == nil {
    86  		return nil, ErrNotExist
    87  	}
    88  
    89  	return lmb.mb.Get(k)
    90  }
    91  
    92  func (lmb *lazyMemBuffer) Set(key Key, value []byte) error {
    93  	if lmb.mb == nil {
    94  		lmb.mb = p.get()
    95  	}
    96  
    97  	return lmb.mb.Set(key, value)
    98  }
    99  
   100  func (lmb *lazyMemBuffer) Delete(k Key) error {
   101  	if lmb.mb == nil {
   102  		lmb.mb = p.get()
   103  	}
   104  
   105  	return lmb.mb.Delete(k)
   106  }
   107  
   108  func (lmb *lazyMemBuffer) Seek(k Key) (Iterator, error) {
   109  	if lmb.mb == nil {
   110  		lmb.mb = p.get()
   111  	}
   112  
   113  	return lmb.mb.Seek(k)
   114  }
   115  
   116  func (lmb *lazyMemBuffer) SeekReverse(k Key) (Iterator, error) {
   117  	if lmb.mb == nil {
   118  		lmb.mb = p.get()
   119  	}
   120  	return lmb.mb.SeekReverse(k)
   121  }
   122  
   123  func (lmb *lazyMemBuffer) Release() {
   124  	if lmb.mb == nil {
   125  		return
   126  	}
   127  
   128  	lmb.mb.Release()
   129  
   130  	p.put(lmb.mb)
   131  	lmb.mb = nil
   132  }
   133  
   134  // Get implements the Retriever interface.
   135  func (us *unionStore) Get(k Key) ([]byte, error) {
   136  	v, err := us.MemBuffer.Get(k)
   137  	if IsErrNotFound(err) {
   138  		if _, ok := us.opts.Get(PresumeKeyNotExists); ok {
   139  			e, ok := us.opts.Get(PresumeKeyNotExistsError)
   140  			if ok && e != nil {
   141  				us.markLazyConditionPair(k, nil, e.(error))
   142  			} else {
   143  				us.markLazyConditionPair(k, nil, ErrKeyExists)
   144  			}
   145  			return nil, errors.Trace(ErrNotExist)
   146  		}
   147  	}
   148  	if IsErrNotFound(err) {
   149  		v, err = us.BufferStore.r.Get(k)
   150  	}
   151  	if err != nil {
   152  		return v, errors.Trace(err)
   153  	}
   154  	if len(v) == 0 {
   155  		return nil, errors.Trace(ErrNotExist)
   156  	}
   157  	return v, nil
   158  }
   159  
   160  // markLazyConditionPair marks a kv pair for later check.
   161  // If condition not match, should return e as error.
   162  func (us *unionStore) markLazyConditionPair(k Key, v []byte, e error) {
   163  	us.lazyConditionPairs[string(k)] = &conditionPair{
   164  		key:   k.Clone(),
   165  		value: v,
   166  		err:   e,
   167  	}
   168  }
   169  
   170  // CheckLazyConditionPairs implements the UnionStore interface.
   171  func (us *unionStore) CheckLazyConditionPairs() error {
   172  	if len(us.lazyConditionPairs) == 0 {
   173  		return nil
   174  	}
   175  	keys := make([]Key, 0, len(us.lazyConditionPairs))
   176  	for _, v := range us.lazyConditionPairs {
   177  		keys = append(keys, v.key)
   178  	}
   179  	values, err := us.snapshot.BatchGet(keys)
   180  	if err != nil {
   181  		return errors.Trace(err)
   182  	}
   183  
   184  	for k, v := range us.lazyConditionPairs {
   185  		if len(v.value) == 0 {
   186  			if _, exist := values[k]; exist {
   187  				return errors.Trace(v.err)
   188  			}
   189  		} else {
   190  			if bytes.Compare(values[k], v.value) != 0 {
   191  				return errors.Trace(ErrLazyConditionPairsNotMatch)
   192  			}
   193  		}
   194  	}
   195  	return nil
   196  }
   197  
   198  // SetOption implements the UnionStore SetOption interface.
   199  func (us *unionStore) SetOption(opt Option, val interface{}) {
   200  	us.opts[opt] = val
   201  }
   202  
   203  // DelOption implements the UnionStore DelOption interface.
   204  func (us *unionStore) DelOption(opt Option) {
   205  	delete(us.opts, opt)
   206  }
   207  
   208  // Release implements the UnionStore Release interface.
   209  func (us *unionStore) Release() {
   210  	us.snapshot.Release()
   211  	us.BufferStore.Release()
   212  }
   213  
   214  type options map[Option]interface{}
   215  
   216  func (opts options) Get(opt Option) (interface{}, bool) {
   217  	v, ok := opts[opt]
   218  	return v, ok
   219  }