Skip to content

Commit ca4a6f5

Browse files
authored
Add a function to memstore to implement a fast Exists method on db (#43)
1 parent b06d88e commit ca4a6f5

File tree

3 files changed

+37
-7
lines changed

3 files changed

+37
-7
lines changed

memstore/memstore.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package memstore
22

33
import (
44
"errors"
5+
56
"github.com/thomasjungblut/go-sstables/skiplist"
67
"github.com/thomasjungblut/go-sstables/sstables"
78
)
@@ -19,6 +20,8 @@ type MemStoreI interface {
1920
Add(key []byte, value []byte) error
2021
// Contains returns true when the given key exists, false otherwise
2122
Contains(key []byte) bool
23+
// IsTombstoned returns true when the given key exists and is tombstoned, false otherwise
24+
IsTombstoned(key []byte) bool
2225
// Get returns the values for the given key, if not exists returns a KeyNotFound error
2326
// if the key exists (meaning it was added and deleted) it will return KeyTombstoned as an error
2427
Get(key []byte) ([]byte, error)
@@ -81,6 +84,21 @@ func (m *MemStore) Contains(key []byte) bool {
8184
return true
8285
}
8386

87+
func (m *MemStore) IsTombstoned(key []byte) bool {
88+
exist := m.skipListMap.Contains(key)
89+
if !exist {
90+
return false
91+
}
92+
element, err := m.skipListMap.Get(key)
93+
if errors.Is(err, skiplist.NotFound) {
94+
return false
95+
}
96+
if *element.value == nil {
97+
return true
98+
}
99+
return false
100+
}
101+
84102
func (m *MemStore) Get(key []byte) ([]byte, error) {
85103
element, err := m.skipListMap.Get(key)
86104
// we can return false if we didn't find it by error, or when the key is tomb-stoned

memstore/memstore_test.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@ package memstore
22

33
import (
44
"errors"
5+
"os"
6+
"testing"
7+
58
"github.com/stretchr/testify/assert"
69
"github.com/stretchr/testify/require"
710
"github.com/thomasjungblut/go-sstables/sstables"
8-
"os"
9-
"testing"
1011
)
1112

1213
func TestMemStoreAddHappyPath(t *testing.T) {
@@ -18,6 +19,8 @@ func TestMemStoreAddHappyPath(t *testing.T) {
1819
err = m.Add([]byte("a"), []byte("aVal"))
1920
assert.Nil(t, err)
2021
assert.True(t, m.Contains([]byte("a")))
22+
assert.False(t, m.IsTombstoned([]byte("a")))
23+
2124
val, err = m.Get([]byte("a"))
2225
assert.Nil(t, err)
2326
assert.Equal(t, []byte("aVal"), val)
@@ -80,6 +83,7 @@ func TestMemStoreDeleteTombstones(t *testing.T) {
8083

8184
err = m.Delete([]byte("a"))
8285
assert.False(t, m.Contains([]byte("a")))
86+
assert.True(t, m.IsTombstoned([]byte("a")))
8387
// make sure that the value was changed under the hood
8488
kv, err = m.skipListMap.Get([]byte("a"))
8589
assert.Nil(t, err)

sstables/super_sstable_reader.go

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,20 @@ type SuperSSTableReader struct {
1616
}
1717

1818
func (s SuperSSTableReader) Contains(key []byte) bool {
19-
// this can't be implemented using contains because NotFound is the same as false, thus we have to go via Get
20-
_, err := s.Get(key)
21-
if err != nil && errors.Is(err, NotFound) {
22-
return false
19+
// scanning from back to front to get the latest definitive answer
20+
for i := len(s.readers) - 1; i >= 0; i-- {
21+
// first check if key exist to return fast
22+
keyExist := s.readers[i].Contains(key)
23+
if !keyExist {
24+
continue
25+
}
26+
// we have to check if the value is not tombstoned
27+
// maybe had to be implemented in an IsTombstoned in sstableReader
28+
res, _ := s.readers[i].Get(key)
29+
return res != nil
2330
}
24-
return true
31+
32+
return false
2533
}
2634

2735
func (s SuperSSTableReader) Get(key []byte) ([]byte, error) {

0 commit comments

Comments
 (0)