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 }