mirror of
https://github.com/featurebasedb/featurebase.git
synced 2026-08-28 10:54:59 +00:00
This commit refactors the anti-entropy system to fetch data from all replicated blocks and only set/clear bits which deviate from the consensus between all blocks. An example of this is if 3 nodes had the following bits set for a single bitmap: Node A: 1 2 3 Node B: 2 4 Node C: 1 2 4 Then only bits which are set on a majority will be set. In this case bits 1, 2, & 4 are set but 3 only exists on a single node. The node performing the merge would then determine the following set/clear diffs for each node: Node A: clear(3), set(4) Node B: set(1) Node C: none Once the merge is performed and all nodes receive their diff instructions then the nodes will be in sync: Node A: 1 2 4 Node B: 1 2 4 Node C: 1 2 4 There still exists situations where bits can be reset. If Node A is up and Node B & C are down then Node A's bits will be reset once B & C come back online. We should add write consistency settings for incoming writes so that we can ensure that a quorum is written to before returning a success. This is outside the scope of this commit though.
180 lines
4.9 KiB
Go
180 lines
4.9 KiB
Go
package pilosa
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/umbel/pilosa/roaring"
|
|
)
|
|
|
|
// Iterator is an interface for looping over bitmap/profile pairs.
|
|
type Iterator interface {
|
|
Seek(bitmapID, profileID uint64)
|
|
Next() (bitmapID, profileID uint64, eof bool)
|
|
}
|
|
|
|
// BufIterator wraps an iterator to provide the ability to unread values.
|
|
type BufIterator struct {
|
|
buf struct {
|
|
bitmapID uint64
|
|
profileID uint64
|
|
eof bool
|
|
full bool
|
|
}
|
|
itr Iterator
|
|
}
|
|
|
|
// NewBufIterator returns a buffered iterator that wraps itr.
|
|
func NewBufIterator(itr Iterator) *BufIterator {
|
|
return &BufIterator{itr: itr}
|
|
}
|
|
|
|
// Seek moves to the first pair equal to or greater than pseek/bseek.
|
|
func (itr *BufIterator) Seek(bitmapID, profileID uint64) {
|
|
itr.buf.full = false
|
|
itr.itr.Seek(bitmapID, profileID)
|
|
}
|
|
|
|
// Next returns the next pair in the bitmap.
|
|
// If a value has been buffered then it is returned and the buffer is cleared.
|
|
func (itr *BufIterator) Next() (bitmapID, profileID uint64, eof bool) {
|
|
if itr.buf.full {
|
|
itr.buf.full = false
|
|
return itr.buf.bitmapID, itr.buf.profileID, itr.buf.eof
|
|
}
|
|
|
|
// Read values onto buffer in case of unread.
|
|
itr.buf.bitmapID, itr.buf.profileID, itr.buf.eof = itr.itr.Next()
|
|
|
|
return itr.buf.bitmapID, itr.buf.profileID, itr.buf.eof
|
|
}
|
|
|
|
// Peek reads the next value but leaves it on the buffer.
|
|
func (itr *BufIterator) Peek() (bitmapID, profileID uint64, eof bool) {
|
|
bitmapID, profileID, eof = itr.Next()
|
|
itr.Unread()
|
|
return
|
|
}
|
|
|
|
// Unread pushes previous pair on to the buffer.
|
|
// Panics if the buffer is already full.
|
|
func (itr *BufIterator) Unread() {
|
|
if itr.buf.full {
|
|
panic("pilosa.BufIterator: buffer full")
|
|
}
|
|
itr.buf.full = true
|
|
}
|
|
|
|
// LimitIterator wraps an Iterator and limits it to a max profile/bitmap pair.
|
|
type LimitIterator struct {
|
|
itr Iterator
|
|
maxBitmapID uint64
|
|
maxProfileID uint64
|
|
|
|
eof bool
|
|
}
|
|
|
|
// NewLimitIterator returns a new LimitIterator.
|
|
func NewLimitIterator(itr Iterator, maxBitmapID, maxProfileID uint64) *LimitIterator {
|
|
return &LimitIterator{
|
|
itr: itr,
|
|
maxBitmapID: maxBitmapID,
|
|
maxProfileID: maxProfileID,
|
|
}
|
|
}
|
|
|
|
// Seek moves the underlying iterator to a profile/bitmap pair.
|
|
func (itr *LimitIterator) Seek(bitmapID, profileID uint64) { itr.itr.Seek(bitmapID, profileID) }
|
|
|
|
// Next returns the next bitmap/profile ID pair.
|
|
// If the underlying iterator returns a pair higher than the max then EOF is returned.
|
|
func (itr *LimitIterator) Next() (bitmapID, profileID uint64, eof bool) {
|
|
// Always return EOF once it is reached by limit or the underlying iterator.
|
|
if itr.eof {
|
|
return 0, 0, true
|
|
}
|
|
|
|
// Retrieve pair from underlying iterator.
|
|
// Mark as EOF if it is beyond the limit (or at EOF).
|
|
bitmapID, profileID, eof = itr.itr.Next()
|
|
if eof || bitmapID > itr.maxBitmapID || (bitmapID == itr.maxBitmapID && profileID > itr.maxProfileID) {
|
|
itr.eof = true
|
|
return 0, 0, true
|
|
}
|
|
|
|
return bitmapID, profileID, false
|
|
}
|
|
|
|
// SliceIterator iterates over a pair of bitmap/profile ID slices.
|
|
type SliceIterator struct {
|
|
bitmapIDs []uint64
|
|
profileIDs []uint64
|
|
|
|
i, n int
|
|
}
|
|
|
|
// NewSliceIterator returns an iterator to iterate over a set of bitmap/profile ID pairs.
|
|
// Both slices MUST have an equal length. Otherwise the function will panic.
|
|
func NewSliceIterator(bitmapIDs, profileIDs []uint64) *SliceIterator {
|
|
if len(profileIDs) != len(bitmapIDs) {
|
|
panic(fmt.Sprintf("pilosa.SliceIterator: pair length mismatch: %d != %d", len(bitmapIDs), len(profileIDs)))
|
|
}
|
|
|
|
return &SliceIterator{
|
|
bitmapIDs: bitmapIDs,
|
|
profileIDs: profileIDs,
|
|
|
|
n: len(bitmapIDs),
|
|
}
|
|
}
|
|
|
|
// Seek moves the cursor to a given pair.
|
|
// If the pair is not found, the iterator seeks to the next pair.
|
|
func (itr *SliceIterator) Seek(bseek, pseek uint64) {
|
|
for i := 0; i < itr.n; i++ {
|
|
bitmapID := itr.bitmapIDs[i]
|
|
profileID := itr.profileIDs[i]
|
|
|
|
if (bseek == bitmapID && pseek <= profileID) || bseek < bitmapID {
|
|
itr.i = i
|
|
return
|
|
}
|
|
}
|
|
|
|
// Seek to the end of the slice if all values are less than seek pair.
|
|
itr.i = itr.n
|
|
}
|
|
|
|
// Next returns the next bitmap/profile ID pair.
|
|
func (itr *SliceIterator) Next() (bitmapID, profileID uint64, eof bool) {
|
|
if itr.i >= itr.n {
|
|
return 0, 0, true
|
|
}
|
|
|
|
bitmapID = itr.bitmapIDs[itr.i]
|
|
profileID = itr.profileIDs[itr.i]
|
|
|
|
itr.i++
|
|
return bitmapID, profileID, false
|
|
}
|
|
|
|
// RoaringIterator converts a roaring.Iterator to output profile/bitmap pairs.
|
|
type RoaringIterator struct {
|
|
itr *roaring.Iterator
|
|
}
|
|
|
|
// NewRoaringIterator returns a new iterator wrapping itr.
|
|
func NewRoaringIterator(itr *roaring.Iterator) *RoaringIterator {
|
|
return &RoaringIterator{itr: itr}
|
|
}
|
|
|
|
// Seek moves the cursor to a pair matching bseek/pseek.
|
|
// If the pair is not found then it moves to the next pair.
|
|
func (itr *RoaringIterator) Seek(bseek, pseek uint64) {
|
|
itr.itr.Seek((bseek * SliceWidth) + pseek)
|
|
}
|
|
|
|
// Next returns the next profile/bitmap ID pair.
|
|
func (itr *RoaringIterator) Next() (bitmapID, profileID uint64, eof bool) {
|
|
v, eof := itr.itr.Next()
|
|
return v / SliceWidth, v % SliceWidth, eof
|
|
}
|