Skip to content
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

[occ] Add incarnation field #321

Merged
merged 1 commit into from
Sep 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 35 additions & 25 deletions store/multiversion/data_structures.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,16 @@ const (
type MultiVersionValue interface {
GetLatest() (value MultiVersionValueItem, found bool)
GetLatestBeforeIndex(index int) (value MultiVersionValueItem, found bool)
Set(index int, value []byte)
SetEstimate(index int)
Delete(index int)
Set(index int, incarnation int, value []byte)
SetEstimate(index int, incarnation int)
Delete(index int, incarnation int)
}

type MultiVersionValueItem interface {
IsDeleted() bool
IsEstimate() bool
Value() []byte
Incarnation() int
Index() int
}

Expand Down Expand Up @@ -63,7 +64,7 @@ func (item *multiVersionItem) GetLatestBeforeIndex(index int) (MultiVersionValue
defer item.mtx.RUnlock()

// we want to find the value at the index that is LESS than the current index
pivot := NewDeletedItem(index - 1)
pivot := &valueItem{index: index - 1}

var vItem *valueItem
var found bool
Expand All @@ -77,35 +78,36 @@ func (item *multiVersionItem) GetLatestBeforeIndex(index int) (MultiVersionValue
return vItem, found
}

func (item *multiVersionItem) Set(index int, value []byte) {
func (item *multiVersionItem) Set(index int, incarnation int, value []byte) {
types.AssertValidValue(value)
item.mtx.Lock()
defer item.mtx.Unlock()

valueItem := NewValueItem(index, value)
valueItem := NewValueItem(index, incarnation, value)
item.valueTree.ReplaceOrInsert(valueItem)
}

func (item *multiVersionItem) Delete(index int) {
func (item *multiVersionItem) Delete(index int, incarnation int) {
item.mtx.Lock()
defer item.mtx.Unlock()

deletedItem := NewDeletedItem(index)
deletedItem := NewDeletedItem(index, incarnation)
item.valueTree.ReplaceOrInsert(deletedItem)
}

func (item *multiVersionItem) SetEstimate(index int) {
func (item *multiVersionItem) SetEstimate(index int, incarnation int) {
item.mtx.Lock()
defer item.mtx.Unlock()

estimateItem := NewEstimateItem(index)
estimateItem := NewEstimateItem(index, incarnation)
item.valueTree.ReplaceOrInsert(estimateItem)
}

type valueItem struct {
index int
value []byte
estimate bool
index int
incarnation int
value []byte
estimate bool
}

var _ MultiVersionValueItem = (*valueItem)(nil)
Expand All @@ -115,6 +117,11 @@ func (v *valueItem) Index() int {
return v.index
}

// Incarnation implements MultiVersionValueItem.
func (v *valueItem) Incarnation() int {
return v.incarnation
}

// IsDeleted implements MultiVersionValueItem.
func (v *valueItem) IsDeleted() bool {
return v.value == nil && !v.estimate
Expand All @@ -135,26 +142,29 @@ func (i *valueItem) Less(other btree.Item) bool {
return i.index < other.(*valueItem).index
}

func NewValueItem(index int, value []byte) *valueItem {
func NewValueItem(index int, incarnation int, value []byte) *valueItem {
return &valueItem{
index: index,
value: value,
estimate: false,
index: index,
incarnation: incarnation,
value: value,
estimate: false,
}
}

func NewEstimateItem(index int) *valueItem {
func NewEstimateItem(index int, incarnation int) *valueItem {
return &valueItem{
index: index,
value: nil,
estimate: true,
index: index,
incarnation: incarnation,
value: nil,
estimate: true,
}
}

func NewDeletedItem(index int) *valueItem {
func NewDeletedItem(index int, incarnation int) *valueItem {
return &valueItem{
index: index,
value: nil,
estimate: false,
index: index,
incarnation: incarnation,
value: nil,
estimate: false,
}
}
36 changes: 22 additions & 14 deletions store/multiversion/data_structures_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,31 +16,35 @@ func TestMultiversionItemGetLatest(t *testing.T) {

// assert that we find a value after it's set
one := []byte("one")
mvItem.Set(1, one)
mvItem.Set(1, 0, one)
value, found = mvItem.GetLatest()
require.True(t, found)
require.Equal(t, one, value.Value())

// assert that we STILL get the "one" value since it is the latest
zero := []byte("zero")
mvItem.Set(0, zero)
mvItem.Set(0, 0, zero)
value, found = mvItem.GetLatest()
require.True(t, found)
require.Equal(t, one, value.Value())
require.Equal(t, 1, value.Index())
require.Equal(t, 0, value.Incarnation())

// we should see a deletion as the latest now, aka nil value and found == true
mvItem.Delete(2)
mvItem.Delete(2, 0)
value, found = mvItem.GetLatest()
require.True(t, found)
require.True(t, value.IsDeleted())
require.Nil(t, value.Value())

// Overwrite the deleted value with some data
two := []byte("two")
mvItem.Set(2, two)
mvItem.Set(2, 3, two)
value, found = mvItem.GetLatest()
require.True(t, found)
require.Equal(t, two, value.Value())
require.Equal(t, 2, value.Index())
require.Equal(t, 3, value.Incarnation())
}

func TestMultiversionItemGetByIndex(t *testing.T) {
Expand All @@ -52,7 +56,7 @@ func TestMultiversionItemGetByIndex(t *testing.T) {

// assert that we find a value after it's set
one := []byte("one")
mvItem.Set(1, one)
mvItem.Set(1, 0, one)
// should not be found because we specifically search "LESS THAN"
value, found = mvItem.GetLatestBeforeIndex(1)
require.False(t, found)
Expand All @@ -69,7 +73,7 @@ func TestMultiversionItemGetByIndex(t *testing.T) {

// assert that we STILL get the "one" value when querying with a later index
zero := []byte("zero")
mvItem.Set(0, zero)
mvItem.Set(0, 0, zero)
// verify that querying for zero should ALWAYS return nil
value, found = mvItem.GetLatestBeforeIndex(0)
require.False(t, found)
Expand All @@ -84,7 +88,7 @@ func TestMultiversionItemGetByIndex(t *testing.T) {
require.Equal(t, zero, value.Value())

// we should see a deletion as the latest now, aka nil value and found == true, but index 4 still returns `one`
mvItem.Delete(4)
mvItem.Delete(4, 0)
value, found = mvItem.GetLatestBeforeIndex(4)
require.True(t, found)
require.Equal(t, one, value.Value())
Expand All @@ -100,7 +104,7 @@ func TestMultiversionItemGetByIndex(t *testing.T) {

// Overwrite the deleted value with some data and verify we read it properly
four := []byte("four")
mvItem.Set(4, four)
mvItem.Set(4, 0, four)
// also reads the four
value, found = mvItem.GetLatestBeforeIndex(6)
require.True(t, found)
Expand All @@ -120,7 +124,7 @@ func TestMultiversionItemEstimate(t *testing.T) {

// assert that we find a value after it's set
one := []byte("one")
mvItem.Set(1, one)
mvItem.Set(1, 0, one)
// should not be found because we specifically search "LESS THAN"
value, found = mvItem.GetLatestBeforeIndex(1)
require.False(t, found)
Expand All @@ -131,7 +135,7 @@ func TestMultiversionItemEstimate(t *testing.T) {
require.False(t, value.IsEstimate())
require.Equal(t, one, value.Value())
// set as estimate
mvItem.SetEstimate(1)
mvItem.SetEstimate(1, 2)
// should not be found because we specifically search "LESS THAN"
value, found = mvItem.GetLatestBeforeIndex(1)
require.False(t, found)
Expand All @@ -140,6 +144,8 @@ func TestMultiversionItemEstimate(t *testing.T) {
value, found = mvItem.GetLatestBeforeIndex(2)
require.True(t, found)
require.True(t, value.IsEstimate())
require.Equal(t, 1, value.Index())
require.Equal(t, 2, value.Incarnation())

// verify that querying for an earlier index returns nil
value, found = mvItem.GetLatestBeforeIndex(0)
Expand All @@ -148,7 +154,7 @@ func TestMultiversionItemEstimate(t *testing.T) {

// assert that we STILL get the "one" value when querying with a later index
zero := []byte("zero")
mvItem.Set(0, zero)
mvItem.Set(0, 0, zero)
// verify that querying for zero should ALWAYS return nil
value, found = mvItem.GetLatestBeforeIndex(0)
require.False(t, found)
Expand All @@ -162,24 +168,26 @@ func TestMultiversionItemEstimate(t *testing.T) {
require.True(t, found)
require.Equal(t, zero, value.Value())
// reset one to no longer be an estiamte
mvItem.Set(1, one)
mvItem.Set(1, 0, one)
// we should see a deletion as the latest now, aka nil value and found == true, but index 4 still returns `one`
mvItem.Delete(4)
mvItem.Delete(4, 1)
value, found = mvItem.GetLatestBeforeIndex(4)
require.True(t, found)
require.Equal(t, one, value.Value())
// should get deletion item for a later index
value, found = mvItem.GetLatestBeforeIndex(5)
require.True(t, found)
require.True(t, value.IsDeleted())
require.Equal(t, 4, value.Index())
require.Equal(t, 1, value.Incarnation())

// verify that we still read the proper underlying item for an older index
value, found = mvItem.GetLatestBeforeIndex(3)
require.True(t, found)
require.Equal(t, one, value.Value())

// Overwrite the deleted value with an estimate and verify we read it properly
mvItem.SetEstimate(4)
mvItem.SetEstimate(4, 0)
// also reads the four
value, found = mvItem.GetLatestBeforeIndex(6)
require.True(t, found)
Expand Down
Loading