From 7399782f48ac1f35e05054159d943623ff3a20b7 Mon Sep 17 00:00:00 2001 From: Sarthak Makhija Date: Mon, 10 Apr 2023 11:23:45 +0530 Subject: [PATCH] Sarthak | Adds InMemoryMap abstraction that will be used in the workshop in place of skiplist --- storage/memory/InMemoryMap.go | 69 +++++++++++++++++ storage/memory/InMemoryMap_test.go | 120 +++++++++++++++++++++++++++++ 2 files changed, 189 insertions(+) create mode 100644 storage/memory/InMemoryMap.go create mode 100644 storage/memory/InMemoryMap_test.go diff --git a/storage/memory/InMemoryMap.go b/storage/memory/InMemoryMap.go new file mode 100644 index 0000000..396aa5a --- /dev/null +++ b/storage/memory/InMemoryMap.go @@ -0,0 +1,69 @@ +package memory + +import ( + "sort" + "storage-engine-workshop/db/model" + "storage-engine-workshop/storage/comparator" +) + +type InMemoryMap struct { + keyValues map[string]model.Slice +} + +func NewInMemoryMap() *InMemoryMap { + return &InMemoryMap{ + keyValues: make(map[string]model.Slice), + } +} + +func (inMemoryMap *InMemoryMap) Put(key model.Slice, value model.Slice) bool { + keyAsString := key.AsString() + if _, ok := inMemoryMap.keyValues[keyAsString]; ok { + return false + } + inMemoryMap.keyValues[keyAsString] = value + return true +} + +func (inMemoryMap *InMemoryMap) Get(key model.Slice) model.GetResult { + keyAsString := key.AsString() + if value, ok := inMemoryMap.keyValues[keyAsString]; ok { + return model.GetResult{ + Key: key, + Value: value, + Exists: true, + } + } + return model.GetResult{ + Key: key, + Value: model.NilSlice(), + Exists: false, + } +} + +func (inMemoryMap *InMemoryMap) MultiGet(keys []model.Slice) (model.MultiGetResult, []model.Slice) { + response := model.MultiGetResult{} + var missingKeys []model.Slice + + for _, key := range keys { + getResult := inMemoryMap.Get(key) + if getResult.Exists { + response.Add(getResult) + } else { + missingKeys = append(missingKeys, key) + } + } + return response, missingKeys +} + +func (inMemoryMap *InMemoryMap) AllKeyValues(keyComparator comparator.KeyComparator) []model.KeyValuePair { + var pairs []model.KeyValuePair + for key, value := range inMemoryMap.keyValues { + pairs = append(pairs, model.KeyValuePair{Key: model.NewSlice([]byte(key)), Value: value}) + } + + sort.SliceStable(pairs, func(i, j int) bool { + return keyComparator.Compare(pairs[i].Key, pairs[j].Key) < 0 + }) + return pairs +} diff --git a/storage/memory/InMemoryMap_test.go b/storage/memory/InMemoryMap_test.go new file mode 100644 index 0000000..3be9126 --- /dev/null +++ b/storage/memory/InMemoryMap_test.go @@ -0,0 +1,120 @@ +package memory + +import ( + "reflect" + "storage-engine-workshop/db/model" + "storage-engine-workshop/storage/comparator" + "testing" +) + +func TestPutsAKeyValueAndGetByKeyInMemoryMap(t *testing.T) { + sentinelNode := NewInMemoryMap() + + key := model.NewSlice([]byte("HDD")) + value := model.NewSlice([]byte("Hard disk")) + + sentinelNode.Put(key, value) + + getResult := sentinelNode.Get(key) + if getResult.Value.AsString() != "Hard disk" { + t.Fatalf("Expected %v, received %v", "Hard disk", getResult.Value.AsString()) + } +} + +func TestPutAKeyValueAndAssertsItsExistenceInMemoryMap(t *testing.T) { + sentinelNode := NewInMemoryMap() + + key := model.NewSlice([]byte("HDD")) + value := model.NewSlice([]byte("Hard disk")) + + sentinelNode.Put(key, value) + + getResult := sentinelNode.Get(key) + if getResult.Exists != true { + t.Fatalf("Expected key to exist, but it did not. Key was %v", "HDD") + } +} + +func TestPutsKeyValuesAndDoesMultiGetByKeysInMemoryMap(t *testing.T) { + sentinelNode := NewInMemoryMap() + + sentinelNode.Put(model.NewSlice([]byte("HDD")), model.NewSlice([]byte("Hard disk"))) + sentinelNode.Put(model.NewSlice([]byte("SDD")), model.NewSlice([]byte("Solid state"))) + + keys := []model.Slice{ + model.NewSlice([]byte("HDD")), + model.NewSlice([]byte("SDD")), + } + multiGetResult, _ := sentinelNode.MultiGet(keys) + allGetResults := multiGetResult.Values + + expected := []model.GetResult{ + {Value: model.NewSlice([]byte("Hard disk")), Exists: true}, + {Value: model.NewSlice([]byte("Solid state")), Exists: true}, + } + + for index, e := range expected { + if e.Value.AsString() != allGetResults[index].Value.AsString() { + t.Fatalf("Expected %v, received %v", e.Value.AsString(), allGetResults[index].Value.AsString()) + } + } +} + +func TestPutsKeyValuesAndDoesMultiGetByKeysWithMissingKeysInMemoryMap(t *testing.T) { + sentinelNode := NewInMemoryMap() + + sentinelNode.Put(model.NewSlice([]byte("HDD")), model.NewSlice([]byte("Hard disk"))) + sentinelNode.Put(model.NewSlice([]byte("SDD")), model.NewSlice([]byte("Solid state"))) + + keys := []model.Slice{ + model.NewSlice([]byte("HDD")), + model.NewSlice([]byte("SDD")), + model.NewSlice([]byte("PMEM")), + } + multiGetResult, missingKeys := sentinelNode.MultiGet(keys) + allGetResults := multiGetResult.Values + + expected := []model.GetResult{ + {Value: model.NewSlice([]byte("Hard disk")), Exists: true}, + {Value: model.NewSlice([]byte("Solid state")), Exists: true}, + } + expectedMissing := []model.Slice{ + model.NewSlice([]byte("PMEM")), + } + + for index, e := range expected { + if e.Value.AsString() != allGetResults[index].Value.AsString() { + t.Fatalf("Expected %v, received %v", e.Value.AsString(), allGetResults[index].Value.AsString()) + } + } + if !reflect.DeepEqual(missingKeys, expectedMissing) { + t.Fatalf("Expected missing keys to be %v, received %v", missingKeys, expectedMissing) + } +} + +func TestGetsAllKeyValuesInMemoryMap(t *testing.T) { + keyComparator := comparator.StringKeyComparator{} + + sentinelNode := NewInMemoryMap() + key := model.NewSlice([]byte("HDD")) + value := model.NewSlice([]byte("Hard disk")) + + sentinelNode.Put(key, value) + sentinelNode.Put(model.NewSlice([]byte("Disk")), model.NewSlice([]byte("SSD"))) + + keyValuePairs := sentinelNode.AllKeyValues(keyComparator) + + if keyValuePairs[0].Key.AsString() != model.NewSlice([]byte("Disk")).AsString() { + t.Fatalf("Expected persistent key to be %v received %v", model.NewSlice([]byte("Disk")).AsString(), keyValuePairs[0].Key.AsString()) + } + if keyValuePairs[1].Key.AsString() != key.AsString() { + t.Fatalf("Expected persistent key to be %v received %v", key.AsString(), keyValuePairs[0].Key.AsString()) + } + + if keyValuePairs[0].Value.AsString() != model.NewSlice([]byte("SSD")).AsString() { + t.Fatalf("Expected persistent value to be %v received %v", model.NewSlice([]byte("SSD")).AsString(), keyValuePairs[0].Value.AsString()) + } + if keyValuePairs[1].Value.AsString() != value.AsString() { + t.Fatalf("Expected persistent value to be %v received %v", value.AsString(), keyValuePairs[0].Value.AsString()) + } +}