-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Merge Iterator alternate implementation #1080
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
25 commits
Select commit
Hold shift + click to select a range
29acf19
Introduce fast merge iterator
bc05e40
fixup
3d23245
More improvements to merge iterator
cb23342
Move iterator_test to table package
8ae39f4
Add missing licence
d90afb4
fix bug in merge iterator
80a0524
Merge branch 'master' into ibrahim/mergeiterator-fast
4f0d265
Merge branch 'ibrahim/mergeiterator-fast' of https://github.com/dgrap…
3f24d43
Add comment and fix typo
a03683a
Update comment
28df20e
Address review comments
ff7427e
Rename smaller/bigger to small and big
37c3827
Rename mergeIteratorChild to node
4476a2f
Add comments
762ce18
Rename fixSmallerBigger to fix
8b48925
Doesn't work
89e4c79
Clean up
ad4c19c
Fix up
c154e93
Remove second iterator
76e7a0a
refactor fix method
9f2541e
refactor test
2429ef3
Add another test
cc86895
Address review comments
59542ea
Reword comment
fcced51
uncomment
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,213 @@ | ||
| /* | ||
| * Copyright 2019 Dgraph Labs, Inc. and Contributors | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
|
|
||
| package table | ||
|
|
||
| import ( | ||
| "github.com/dgraph-io/badger/y" | ||
| "github.com/pkg/errors" | ||
| ) | ||
|
|
||
| // MergeIterator merges multiple iterators. | ||
| // NOTE: MergeIterator owns the array of iterators and is responsible for closing them. | ||
| type MergeIterator struct { | ||
| left node | ||
| right node | ||
| small *node | ||
| reverse bool | ||
| } | ||
|
|
||
| type node struct { | ||
| valid bool | ||
| key []byte | ||
| iter y.Iterator | ||
|
|
||
| // The two iterators are type asserted from `y.Iterator`, used to inline more function calls. | ||
| // Calling functions on concrete types is much faster (about 25-30%) than calling the | ||
| // interface's function. | ||
| merge *MergeIterator | ||
| concat *ConcatIterator | ||
| } | ||
|
|
||
| func (n *node) setIterator(iter y.Iterator) { | ||
| n.iter = iter | ||
| // It's okay if the type assertion below fails and n.merge/n.concat are set to nil. | ||
| // We handle the nil values of merge and concat in all the methods. | ||
| n.merge, _ = iter.(*MergeIterator) | ||
| n.concat, _ = iter.(*ConcatIterator) | ||
| } | ||
|
|
||
| func (n *node) setKey() { | ||
| if n.merge != nil { | ||
| n.valid = n.merge.small.valid | ||
| if n.valid { | ||
| n.key = n.merge.small.key | ||
| } | ||
| } else if n.concat != nil { | ||
| n.valid = n.concat.Valid() | ||
| if n.valid { | ||
| n.key = n.concat.Key() | ||
| } | ||
| } else { | ||
| n.valid = n.iter.Valid() | ||
| if n.valid { | ||
| n.key = n.iter.Key() | ||
| } | ||
| } | ||
| } | ||
|
|
||
| func (n *node) next() { | ||
| if n.merge != nil { | ||
| n.merge.Next() | ||
| } else if n.concat != nil { | ||
| n.concat.Next() | ||
| } else { | ||
| n.iter.Next() | ||
| } | ||
| n.setKey() | ||
| } | ||
|
|
||
| func (n *node) rewind() { | ||
| n.iter.Rewind() | ||
| n.setKey() | ||
| } | ||
|
|
||
| func (n *node) seek(key []byte) { | ||
| n.iter.Seek(key) | ||
| n.setKey() | ||
| } | ||
|
|
||
| func (mi *MergeIterator) fix() { | ||
| if !mi.bigger().valid { | ||
| return | ||
| } | ||
| if !mi.small.valid { | ||
| mi.swapSmall() | ||
| return | ||
| } | ||
| cmp := y.CompareKeys(mi.small.key, mi.bigger().key) | ||
| // Both the keys are equal. | ||
| if cmp == 0 { | ||
| // In case of same keys, move the right iterator ahead. | ||
| mi.right.next() | ||
| if &mi.right == mi.small { | ||
| mi.swapSmall() | ||
| } | ||
| return | ||
| } else if cmp < 0 { // Small is less than bigger(). | ||
| if mi.reverse { | ||
| mi.swapSmall() | ||
| } else { | ||
| // we don't need to do anything. Small already points to the smallest. | ||
| } | ||
| return | ||
| } else { // bigger() is less than small. | ||
| if mi.reverse { | ||
| // Do nothing since we're iterating in reverse. Small currently points to | ||
| // the bigger key and that's okay in reverse iteration. | ||
| } else { | ||
| mi.swapSmall() | ||
| } | ||
jarifibrahim marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| return | ||
| } | ||
| } | ||
|
|
||
| func (mi *MergeIterator) bigger() *node { | ||
| if mi.small == &mi.left { | ||
| return &mi.right | ||
| } | ||
| return &mi.left | ||
| } | ||
|
|
||
| func (mi *MergeIterator) swapSmall() { | ||
| if mi.small == &mi.left { | ||
| mi.small = &mi.right | ||
| return | ||
| } | ||
| if mi.small == &mi.right { | ||
| mi.small = &mi.left | ||
| return | ||
| } | ||
| } | ||
|
|
||
| // Next returns the next element. If it is the same as the current key, ignore it. | ||
| func (mi *MergeIterator) Next() { | ||
| mi.small.next() | ||
| mi.fix() | ||
| } | ||
|
|
||
| // Rewind seeks to first element (or last element for reverse iterator). | ||
| func (mi *MergeIterator) Rewind() { | ||
| mi.left.rewind() | ||
| mi.right.rewind() | ||
| mi.fix() | ||
| } | ||
|
|
||
| // Seek brings us to element with key >= given key. | ||
| func (mi *MergeIterator) Seek(key []byte) { | ||
| mi.left.seek(key) | ||
| mi.right.seek(key) | ||
| mi.fix() | ||
| } | ||
|
|
||
| // Valid returns whether the MergeIterator is at a valid element. | ||
| func (mi *MergeIterator) Valid() bool { | ||
| return mi.small.valid | ||
| } | ||
|
|
||
| // Key returns the key associated with the current iterator. | ||
| func (mi *MergeIterator) Key() []byte { | ||
| return mi.small.key | ||
| } | ||
|
|
||
| // Value returns the value associated with the iterator. | ||
| func (mi *MergeIterator) Value() y.ValueStruct { | ||
| return mi.small.iter.Value() | ||
| } | ||
|
|
||
| // Close implements y.Iterator. | ||
| func (mi *MergeIterator) Close() error { | ||
| err1 := mi.left.iter.Close() | ||
| err2 := mi.right.iter.Close() | ||
| if err1 != nil { | ||
| return errors.Wrap(err1, "MergeIterator") | ||
| } | ||
| return errors.Wrap(err2, "MergeIterator") | ||
| } | ||
|
|
||
| // NewMergeIterator creates a merge iterator. | ||
| func NewMergeIterator(iters []y.Iterator, reverse bool) y.Iterator { | ||
| if len(iters) == 0 { | ||
| return nil | ||
| } else if len(iters) == 1 { | ||
| return iters[0] | ||
| } else if len(iters) == 2 { | ||
| mi := &MergeIterator{ | ||
| reverse: reverse, | ||
| } | ||
| mi.left.setIterator(iters[0]) | ||
| mi.right.setIterator(iters[1]) | ||
| // Assign left iterator randomly. This will be fixed when user calls rewind/seek. | ||
| mi.small = &mi.left | ||
| return mi | ||
| } | ||
| mid := len(iters) / 2 | ||
| return NewMergeIterator( | ||
| []y.Iterator{ | ||
| NewMergeIterator(iters[:mid], reverse), | ||
| NewMergeIterator(iters[mid:], reverse), | ||
| }, reverse) | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.