Skip to content

Commit

Permalink
[occ] Add incarnation field (#321)
Browse files Browse the repository at this point in the history
## Describe your changes and provide context
This adds the incarnation field to the multiversion item data structure.

## Testing performed to validate your change
updated unit tests
  • Loading branch information
udpatil committed Jan 31, 2024
1 parent 5ad76e0 commit 5e5b4ce
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 39 deletions.
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

0 comments on commit 5e5b4ce

Please sign in to comment.