github.com/vescale/zgraph@v0.0.0-20230410094002-959c02d50f95/storage/union_iter.go (about)

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