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

Lab solution #37

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
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
114 changes: 64 additions & 50 deletions common/consistenthash/consistenthash.go
Original file line number Diff line number Diff line change
@@ -1,108 +1,122 @@
package consistenthash

import (
"errors"
"hash/crc32"
"sort"
"sync"
"errors"
"hash/crc32"
"sort"
"sync"
)

var ErrNodeNotFound = errors.New("node not found")

// Ring is the data structure that holds a consistent hashed ring.
type Ring struct {
Nodes Nodes
sync.Mutex
Nodes Nodes
sync.Mutex
}

// search will find the index of the node that is responsible for the range that
// includes the hashed value of key.
func (r *Ring) search(key string) int {
/////////////////////////
// YOUR CODE GOES HERE //
/////////////////////////
var (
hashValue uint32
nodeIndex int
)

return 0
hashValue = hashId(key)
nodeIndex = 0

// the responsible node is the first one with HashId higher than the hashed value being searched for.
for _, node := range r.Nodes {
if node.HashId >= hashValue {
return nodeIndex
}

nodeIndex++
}

// If no node has HashId higher the hashed value, it means that the value falls on the last part of the ring, which belongs to the first node (because of the circular ring structure).
return 0
}

// NewRing will create a new Ring object and return a pointer to it.
func NewRing() *Ring {
return &Ring{Nodes: Nodes{}}
return &Ring{Nodes: Nodes{}}
}

// Verify if a node with a given id already exists in the ring and if so return
// a pointer to it.
func (r *Ring) Exists(id string) (bool, *Node) {
r.Lock()
defer r.Unlock()
r.Lock()
defer r.Unlock()

for _, node := range r.Nodes {
if node.Id == id {
return true, node
}
}
for _, node := range r.Nodes {
if node.Id == id {
return true, node
}
}

return false, nil
return false, nil
}

// Add a node to the ring and return a pointer to it.
func (r *Ring) AddNode(id string) *Node {
r.Lock()
defer r.Unlock()
r.Lock()
defer r.Unlock()

node := NewNode(id)
r.Nodes = append(r.Nodes, node)
node := NewNode(id)
r.Nodes = append(r.Nodes, node)

sort.Sort(r.Nodes)
sort.Sort(r.Nodes)

return node
return node
}

// Remove a node from the ring.
func (r *Ring) RemoveNode(id string) error {
r.Lock()
defer r.Unlock()
r.Lock()
defer r.Unlock()

i := r.search(id)
if i >= r.Nodes.Len() || r.Nodes[i].Id != id {
return ErrNodeNotFound
}
i := r.search(id)
if i >= r.Nodes.Len() || r.Nodes[i].Id != id {
return ErrNodeNotFound
}

r.Nodes = append(r.Nodes[:i], r.Nodes[i+1:]...)
r.Nodes = append(r.Nodes[:i], r.Nodes[i+1:]...)

return nil
return nil
}

// Get the id of the node responsible for the hash range of id.
func (r *Ring) Get(id string) string {
i := r.search(id)
if i >= r.Nodes.Len() {
i = 0
}
i := r.search(id)
if i >= r.Nodes.Len() {
i = 0
}

return r.Nodes[i].Id
return r.Nodes[i].Id
}

// GetNext will return the next node after the one responsible for the hash
// range of id.
func (r *Ring) GetNext(id string) (string, error) {
r.Lock()
defer r.Unlock()
var i = 0
for i < r.Nodes.Len() && r.Nodes[i].Id != id {
i++
}
r.Lock()
defer r.Unlock()
var i = 0
for i < r.Nodes.Len() && r.Nodes[i].Id != id {
i++
}

if i >= r.Nodes.Len() {
return "", ErrNodeNotFound
}
if i >= r.Nodes.Len() {
return "", ErrNodeNotFound
}

nextIndex := (i + 1) % r.Nodes.Len()
nextIndex := (i + 1) % r.Nodes.Len()

return r.Nodes[nextIndex].Id, nil
return r.Nodes[nextIndex].Id, nil
}

// hashId returns the hashed form of a key.
func hashId(key string) uint32 {
return crc32.ChecksumIEEE([]byte(key)) % uint32(1000)
return crc32.ChecksumIEEE([]byte(key)) % uint32(1000)
}
45 changes: 24 additions & 21 deletions dynamo/cache.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package dynamo

import (
"log"
"sync"
"log"
"sync"
)

///////////////////////////////
Expand All @@ -11,47 +11,50 @@ import (

// Cache is the struct that handle all the data storage for the dynamo server.
type Cache struct {
data map[string]string
sync.Mutex
data map[string]string
timestamps map[string]int64
sync.Mutex
}

// Create a new cache object and return a pointer to it.
func NewCache() *Cache {
var s Cache
var s Cache

s.data = make(map[string]string)
s.data = make(map[string]string)
s.timestamps = make(map[string]int64)

return &s
return &s
}

// Get the value of a key from the storage. This will handle concurrent get
// requests by locking the structure.
func (cache *Cache) Get(key string) (value string, timestamp int64) {
cache.Lock()
value = cache.data[key]
timestamp = 0
cache.Unlock()
cache.Lock()
value = cache.data[key]
timestamp = cache.timestamps[key]
cache.Unlock()

log.Printf("[CACHE] Getting Key '%v' with Value '%v' @ timestamp '%v'\n", key, value, timestamp)
return
log.Printf("[CACHE] Getting Key '%v' with Value '%v' @ timestamp '%v'\n", key, value, timestamp)
return value, timestamp
}

// Put a value to a key in the storage. This will handle concurrent put
// requests by locking the structure.
func (cache *Cache) Put(key string, value string, timestamp int64) {
log.Printf("[CACHE] Putting Key '%v' with Value '%v' @ timestamp '%v'\n", key, value, timestamp)
log.Printf("[CACHE] Putting Key '%v' with Value '%v' @ timestamp '%v'\n", key, value, timestamp)

cache.Lock()
cache.data[key] = value
cache.Unlock()
cache.Lock()
cache.data[key] = value
cache.timestamps[key] = timestamp
cache.Unlock()

return
return
}

// Retrieve all information from the server. This shouldn't be used in any way
// except for testing purposes.
func (cache *Cache) getAll() (data map[string]string, timestamps map[string]int64) {
data = cache.data
timestamps = make(map[string]int64)
return data, timestamps
data = cache.data
timestamps = cache.timestamps
return data, timestamps
}
18 changes: 13 additions & 5 deletions dynamo/coordinator.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,13 +186,21 @@ func (server *Server) Voting(key string, quorum int) (string, error) {

// aggregateVotes will select the right value from the votes received.
func aggregateVotes(votes []*vote) (result string) {
var (
lastStamp int64
finalValue string
)

lastStamp = 0

for _, vote := range votes {
log.Printf("[COORDINATOR] Vote: %v\n", vote.value)

if vote.timestamp > lastStamp {
finalValue = vote.value
lastStamp = vote.timestamp
}
}

/////////////////////////
// YOUR CODE GOES HERE //
/////////////////////////
result = votes[0].value
return
return finalValue
}