github.com/KinWaiYuen/client-go/v2@v2.5.4/internal/unionstore/union_iter.go (about) 1 // Copyright 2021 TiKV Authors 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 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // NOTE: The code in this file is based on code from the 16 // TiDB project, licensed under the Apache License v 2.0 17 // 18 // https://github.com/pingcap/tidb/tree/cc5e161ac06827589c4966674597c137cc9e809c/store/tikv/unionstore/union_iter.go 19 // 20 21 // Copyright 2015 PingCAP, Inc. 22 // 23 // Licensed under the Apache License, Version 2.0 (the "License"); 24 // you may not use this file except in compliance with the License. 25 // You may obtain a copy of the License at 26 // 27 // http://www.apache.org/licenses/LICENSE-2.0 28 // 29 // Unless required by applicable law or agreed to in writing, software 30 // distributed under the License is distributed on an "AS IS" BASIS, 31 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 32 // See the License for the specific language governing permissions and 33 // limitations under the License. 34 35 package unionstore 36 37 import ( 38 "github.com/KinWaiYuen/client-go/v2/internal/logutil" 39 "github.com/KinWaiYuen/client-go/v2/kv" 40 "go.uber.org/zap" 41 ) 42 43 // UnionIter is the iterator on an UnionStore. 44 type UnionIter struct { 45 dirtyIt Iterator 46 snapshotIt Iterator 47 48 dirtyValid bool 49 snapshotValid bool 50 51 curIsDirty bool 52 isValid bool 53 reverse bool 54 } 55 56 // NewUnionIter returns a union iterator for BufferStore. 57 func NewUnionIter(dirtyIt Iterator, snapshotIt Iterator, reverse bool) (*UnionIter, error) { 58 it := &UnionIter{ 59 dirtyIt: dirtyIt, 60 snapshotIt: snapshotIt, 61 dirtyValid: dirtyIt.Valid(), 62 snapshotValid: snapshotIt.Valid(), 63 reverse: reverse, 64 } 65 err := it.updateCur() 66 if err != nil { 67 return nil, err 68 } 69 return it, nil 70 } 71 72 // dirtyNext makes iter.dirtyIt go and update valid status. 73 func (iter *UnionIter) dirtyNext() error { 74 err := iter.dirtyIt.Next() 75 iter.dirtyValid = iter.dirtyIt.Valid() 76 return err 77 } 78 79 // snapshotNext makes iter.snapshotIt go and update valid status. 80 func (iter *UnionIter) snapshotNext() error { 81 err := iter.snapshotIt.Next() 82 iter.snapshotValid = iter.snapshotIt.Valid() 83 return err 84 } 85 86 func (iter *UnionIter) updateCur() error { 87 iter.isValid = true 88 for { 89 if !iter.dirtyValid && !iter.snapshotValid { 90 iter.isValid = false 91 break 92 } 93 94 if !iter.dirtyValid { 95 iter.curIsDirty = false 96 break 97 } 98 99 if !iter.snapshotValid { 100 iter.curIsDirty = true 101 // if delete it 102 if len(iter.dirtyIt.Value()) == 0 { 103 if err := iter.dirtyNext(); err != nil { 104 return err 105 } 106 continue 107 } 108 break 109 } 110 111 // both valid 112 if iter.snapshotValid && iter.dirtyValid { 113 snapshotKey := iter.snapshotIt.Key() 114 dirtyKey := iter.dirtyIt.Key() 115 cmp := kv.CmpKey(dirtyKey, snapshotKey) 116 if iter.reverse { 117 cmp = -cmp 118 } 119 // if equal, means both have value 120 if cmp == 0 { 121 if len(iter.dirtyIt.Value()) == 0 { 122 // snapshot has a record, but txn says we have deleted it 123 // just go next 124 if err := iter.dirtyNext(); err != nil { 125 return err 126 } 127 if err := iter.snapshotNext(); err != nil { 128 return err 129 } 130 continue 131 } 132 // both go next 133 if err := iter.snapshotNext(); err != nil { 134 return err 135 } 136 iter.curIsDirty = true 137 break 138 } else if cmp > 0 { 139 // record from snapshot comes first 140 iter.curIsDirty = false 141 break 142 } else { 143 // record from dirty comes first 144 if len(iter.dirtyIt.Value()) == 0 { 145 logutil.BgLogger().Warn("delete a record not exists?", 146 zap.String("key", kv.StrKey(iter.dirtyIt.Key()))) 147 // jump over this deletion 148 if err := iter.dirtyNext(); err != nil { 149 return err 150 } 151 continue 152 } 153 iter.curIsDirty = true 154 break 155 } 156 } 157 } 158 return nil 159 } 160 161 // Next implements the Iterator Next interface. 162 func (iter *UnionIter) Next() error { 163 var err error 164 if !iter.curIsDirty { 165 err = iter.snapshotNext() 166 } else { 167 err = iter.dirtyNext() 168 } 169 if err != nil { 170 return err 171 } 172 err = iter.updateCur() 173 return err 174 } 175 176 // Value implements the Iterator Value interface. 177 // Multi columns 178 func (iter *UnionIter) Value() []byte { 179 if !iter.curIsDirty { 180 return iter.snapshotIt.Value() 181 } 182 return iter.dirtyIt.Value() 183 } 184 185 // Key implements the Iterator Key interface. 186 func (iter *UnionIter) Key() []byte { 187 if !iter.curIsDirty { 188 return iter.snapshotIt.Key() 189 } 190 return iter.dirtyIt.Key() 191 } 192 193 // Valid implements the Iterator Valid interface. 194 func (iter *UnionIter) Valid() bool { 195 return iter.isValid 196 } 197 198 // Close implements the Iterator Close interface. 199 func (iter *UnionIter) Close() { 200 if iter.snapshotIt != nil { 201 iter.snapshotIt.Close() 202 iter.snapshotIt = nil 203 } 204 if iter.dirtyIt != nil { 205 iter.dirtyIt.Close() 206 iter.dirtyIt = nil 207 } 208 }