Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
322c112
implements an interface idea for #15
SUMUKHA-PK Jul 20, 2020
5edcfd1
return owner details in CheckAcquire
SUMUKHA-PK Jul 23, 2020
8930600
this commit implements in its entirity the method to watch locks only…
SUMUKHA-PK Jul 24, 2020
c65adc7
this commit fixes some minor issues with formatting
SUMUKHA-PK Jul 25, 2020
0dae8af
fixed RemoveElement to reduce cache size, added cache vs non cache be…
SUMUKHA-PK Jul 25, 2020
ea80df4
this commit adds a wrapper for lock status check
SUMUKHA-PK Jul 29, 2020
3975b10
this commit completes lock watching with a cache in the lockservice
SUMUKHA-PK Jul 29, 2020
38e3514
this commit adds more clarity on pouncing on a lock
SUMUKHA-PK Jul 31, 2020
52b008f
this commit adds some basic implementation of the Pounce function and…
Aug 15, 2020
7d05753
this commit moves SimpleDescriptor to LockDescriptor
Aug 16, 2020
35be35b
this commit adds the new ObjectDescriptor type across the lockclient …
Aug 16, 2020
ed46006
this commit has the POC of lock pouncing, tests will be implemented soon
SUMUKHA-PK Aug 19, 2020
5056971
this commit resolves #17, implements lock-watching and lock-pouncing
SUMUKHA-PK Aug 22, 2020
45c6b67
this commit fixes a race condition on the err variables in the test file
SUMUKHA-PK Aug 22, 2020
c4169b1
this commit fixes a weird test fail
SUMUKHA-PK Aug 22, 2020
a6b1173
this commit fixes failing CI
SUMUKHA-PK Aug 23, 2020
7891b60
this commit moves the timers outside the goroutines to have a determi…
SUMUKHA-PK Aug 23, 2020
3b9edcf
this commit makes watching and pouncing dormant
SUMUKHA-PK Oct 1, 2020
f88a078
minor changes to pass tests
SUMUKHA-PK Oct 1, 2020
64e2cd3
removed unnecessary go mods
SUMUKHA-PK Oct 1, 2020
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
4 changes: 4 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ module github.com/SystemBuilders/LocKey
go 1.14

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/gorilla/mux v1.7.4
github.com/kr/pretty v0.1.0 // indirect
github.com/rs/zerolog v1.19.0
github.com/stretchr/testify v1.6.1
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
)
21 changes: 21 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,14 +1,35 @@
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc=
github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/rs/zerolog v1.19.0 h1:hYz4ZVdUgjXTBUmrkrw55j1nHx68LfOKIQk5IYtyScg=
github.com/rs/zerolog v1.19.0/go.mod h1:IzD0RJ65iWH0w97OQQebJEvTZYvsCUm9WVLWBQrJRjo=
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20190828213141-aed303cbaa74/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
2 changes: 1 addition & 1 deletion internal/cache/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ type Cache interface {
// Getting the object makes it the most recently used object
// in the cache. This function must be implemented in O(1) complexity.
// If the object doesn't exist in the cache, an error is raised.
GetElement(element interface{}) error
GetElement(element interface{}) (string, error)
// PutElement inserts an object into the cache.
// Putting the object makes it the most recently used object
// in the cache. This function must be implemented in O(1) complexity.
Expand Down
4 changes: 3 additions & 1 deletion internal/cache/dll.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ var _ Key = (*SimpleKey)(nil)

// SimpleKey implements a Key interface.
type SimpleKey struct {
Owner string
Value string
}

Expand All @@ -19,9 +20,10 @@ func (sk *SimpleKey) Data() string {
}

// NewSimpleKey returns a new SimpleKey of the given value.
func NewSimpleKey(val string) *SimpleKey {
func NewSimpleKey(val, owner string) *SimpleKey {
return &SimpleKey{
Value: val,
Owner: owner,
}
}

Expand Down
14 changes: 7 additions & 7 deletions internal/cache/dll_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,37 +5,37 @@ import "testing"
func Test_DLL(t *testing.T) {
dllNode := NewDoublyLinkedList()

dllNode.InsertNodeToRight(nil, &SimpleKey{"1"})
dllNode.InsertNodeToRight(nil, &SimpleKey{Value: "1"})
dllNode.PrintLinkedList()

if dllNode.Head.Key().Data() != "1" {
t.Errorf("Required value \"1\", got %s", dllNode.Head.Key())
}

dllNode.InsertNodeToLeft(dllNode.Head, &SimpleKey{"2"})
dllNode.InsertNodeToLeft(dllNode.Head, &SimpleKey{Value: "2"})
dllNode.PrintLinkedList()

dllNode.DeleteNode(dllNode.Head.Right())
dllNode.PrintLinkedList()

dllNode.InsertNodeToLeft(dllNode.Head, &SimpleKey{"1"})
dllNode.InsertNodeToLeft(dllNode.Head, &SimpleKey{Value: "1"})
dllNode.PrintLinkedList()

dllNode.InsertNodeToLeft(dllNode.Head, &SimpleKey{"3"})
dllNode.InsertNodeToLeft(dllNode.Head, &SimpleKey{Value: "3"})
dllNode.PrintLinkedList()

dllNode.DeleteNode(dllNode.Head.Right().Right())
dllNode.PrintLinkedList()

dllNode.InsertNodeToLeft(dllNode.Head, &SimpleKey{"2"})
dllNode.InsertNodeToLeft(dllNode.Head, &SimpleKey{Value: "2"})
dllNode.PrintLinkedList()

dllNode.InsertNodeToLeft(dllNode.Head, &SimpleKey{"4"})
dllNode.InsertNodeToLeft(dllNode.Head, &SimpleKey{Value: "4"})
dllNode.PrintLinkedList()

dllNode.DeleteNode(dllNode.Head.Right().Right().Right())
dllNode.PrintLinkedList()

dllNode.InsertNodeToLeft(dllNode.Head, &SimpleKey{"1"})
dllNode.InsertNodeToLeft(dllNode.Head, &SimpleKey{Value: "1"})
dllNode.PrintLinkedList()
}
1 change: 1 addition & 0 deletions internal/cache/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ func (e Error) Error() string { return string(e) }
const (
ErrElementDoesntExist = Error("element doesn't exist in the cache")
ErrElementAlreadyExists = Error("element already exists in the cache")
ErrCacheDoesntExist = Error("cache doesn't exist")
)
52 changes: 31 additions & 21 deletions internal/cache/lru_cache.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package cache

import "fmt"
import (
"fmt"
"sync"
)

var _ Cache = (*LRUCache)(nil)

Expand Down Expand Up @@ -30,6 +33,7 @@ type LRUCache struct {
tail *DLLNode
m map[interface{}]*DLLNode
dll *DoublyLinkedList
mu sync.Mutex
}

// NewLRUCache creates a new LRUCache of provided size.
Expand All @@ -50,10 +54,14 @@ func NewLRUCache(capacity int) *LRUCache {
//
// The element is removed from the map too because
// it might have stale node values.
func (lru *LRUCache) GetElement(element interface{}) error {
//
// Error is returned only if the element doesn't exist in the cache.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Specify that the owner of the item is being returned by the function
Currently, it says 'GetElement gets an element from the cache'

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we shouldnt be so blunt about it being the owner, it can technically be anything in the future. Ill mention, it returns associated data will be returned along with an error.

func (lru *LRUCache) GetElement(element interface{}) (string, error) {
// Check whether the element exists in the cache.
if _, ok := lru.m[*element.(*SimpleKey)]; ok {
nodeOfKey := lru.m[*element.(*SimpleKey)]
lru.mu.Lock()
defer lru.mu.Unlock()
if node, ok := lru.m[*&element.(*SimpleKey).Value]; ok {
nodeOfKey := lru.m[*&element.(*SimpleKey).Value]
// Check whether the currently accessed element is the
// most recently used element in the cache. If it's not,
// it must be moved to the MRU to accomodate the protocol.
Expand All @@ -65,22 +73,17 @@ func (lru *LRUCache) GetElement(element interface{}) error {

// delete the old key so that it can be moved to MRU.
lru.dll.DeleteNode(nodeOfKey)
lru.deleteElementFromMap(*nodeOfKey.Key().(*SimpleKey))
lru.deleteElementFromMap(*&nodeOfKey.Key().(*SimpleKey).Value)

// Move the currently accessed node to the MRU position.
// The start pointer doesn't change as it still points to
// the LRU element.
lru.dll.InsertNodeToLeft(lru.dll.Head, nodeOfKey.NodeKey)
lru.insertElementIntoMap(*element.(*SimpleKey), lru.dll.Head)

// headRight := lru.dll.Head.Right()
// if headRight != nil {
// lru.insertElementIntoMap(headRight.Key(), headRight)
// }
lru.insertElementIntoMap(*&element.(*SimpleKey).Value, lru.dll.Head)
}
return nil
return node.NodeKey.Owner, nil
}
return ErrElementDoesntExist
return "", ErrElementDoesntExist
}

// PutElement inserts an element in the cache.
Expand All @@ -89,28 +92,31 @@ func (lru *LRUCache) GetElement(element interface{}) error {
// Removal of the LRU is done my deleting the tail node,
// making place for a new node.
func (lru *LRUCache) PutElement(element interface{}) error {
lru.mu.Lock()
defer lru.mu.Unlock()
if !lru.full {
if lru.dll.Head == nil {
lru.dll.InsertNodeToRight(lru.dll.Head, element.(*SimpleKey))
err := lru.insertElementIntoMap(*element.(*SimpleKey), lru.dll.Head)
err := lru.insertElementIntoMap(*&element.(*SimpleKey).Value, lru.dll.Head)
if err != nil {
return err
}
lru.tail = lru.dll.Head.(*DLLNode)
} else {
lru.dll.InsertNodeToLeft(lru.dll.Head, element.(*SimpleKey))
err := lru.insertElementIntoMap(*element.(*SimpleKey), lru.dll.Head)
err := lru.insertElementIntoMap(*&element.(*SimpleKey).Value, lru.dll.Head)
if err != nil {
return err
}
}

lru.size++
if lru.size == lru.capacity {
lru.full = true
}
} else {
lru.dll.InsertNodeToLeft(lru.dll.Head, element.(*SimpleKey))
err := lru.insertElementIntoMap(*element.(*SimpleKey), lru.dll.Head)
err := lru.insertElementIntoMap(*&element.(*SimpleKey).Value, lru.dll.Head)
if err != nil {
return err
}
Expand All @@ -120,18 +126,20 @@ func (lru *LRUCache) PutElement(element interface{}) error {

// Delete the "start" node and make the newly inserted node the MRU node.
lru.dll.DeleteNode(tailNode)
lru.deleteElementFromMap(*tailNode.Key().(*SimpleKey))
lru.deleteElementFromMap(*&tailNode.Key().(*SimpleKey).Value)
}
return nil
}

// RemoveElement() deletes a node from the cache based on a key value
// RemoveElement deletes a node from the cache based on a key value
// If there are multiple nodes with the same value, the node that was
// most recently used will be removed.
func (lru *LRUCache) RemoveElement(element interface{}) error {
lru.mu.Lock()
defer lru.mu.Unlock()
// Check if the node exists in the cache
if _, ok := lru.m[*element.(*SimpleKey)]; ok {
nodeOfKey := lru.m[*element.(*SimpleKey)]
if _, ok := lru.m[*&element.(*SimpleKey).Value]; ok {
nodeOfKey := lru.m[*&element.(*SimpleKey).Value]
// If there is only one element in the linked list, make the
// tail point to nil
//
Expand All @@ -142,8 +150,10 @@ func (lru *LRUCache) RemoveElement(element interface{}) error {
} else if lru.tail == nodeOfKey {
lru.tail = nodeOfKey.LeftNode.(*DLLNode)
}
// lru.PrintCache()
lru.size--
lru.dll.DeleteNode(nodeOfKey)
lru.deleteElementFromMap(*nodeOfKey.Key().(*SimpleKey))
lru.deleteElementFromMap(*&nodeOfKey.Key().(*SimpleKey).Value)
return nil

}
Expand Down
26 changes: 13 additions & 13 deletions internal/cache/lru_cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ import "testing"
func Test_LRUCache(t *testing.T) {
lruCache := NewLRUCache(5)

one := NewSimpleKey("1")
two := NewSimpleKey("2")
three := NewSimpleKey("3")
four := NewSimpleKey("4")
one_1 := NewSimpleKey("1")
five := NewSimpleKey("5")
six := NewSimpleKey("6")
one := NewSimpleKey("1", "owner1")
two := NewSimpleKey("2", "owner1")
three := NewSimpleKey("3", "owner1")
four := NewSimpleKey("4", "owner1")
oneOne := NewSimpleKey("1", "owner1")
five := NewSimpleKey("5", "owner1")
six := NewSimpleKey("6", "owner1")

err := lruCache.PutElement(one)
if err != nil {
Expand All @@ -25,7 +25,7 @@ func Test_LRUCache(t *testing.T) {
}
lruCache.PrintCache()

err = lruCache.GetElement(one)
_, err = lruCache.GetElement(one)
if err != nil {
t.Fatal(err)
}
Expand All @@ -37,7 +37,7 @@ func Test_LRUCache(t *testing.T) {
}
lruCache.PrintCache()

err = lruCache.GetElement(two)
_, err = lruCache.GetElement(two)
if err != nil {
t.Fatal(err)
}
Expand All @@ -49,25 +49,25 @@ func Test_LRUCache(t *testing.T) {
}
lruCache.PrintCache()

err = lruCache.GetElement(one)
_, err = lruCache.GetElement(one)
if err != nil {
t.Fatal(err)
}
lruCache.PrintCache()

err = lruCache.GetElement(three)
_, err = lruCache.GetElement(three)
if err != nil {
t.Fatal(err)
}
lruCache.PrintCache()

err = lruCache.GetElement(four)
_, err = lruCache.GetElement(four)
if err != nil {
t.Fatal(err)
}
lruCache.PrintCache()

err = lruCache.RemoveElement(one_1)
err = lruCache.RemoveElement(oneOne)
if err != nil {
t.Fatal(err)
}
Expand Down
4 changes: 4 additions & 0 deletions internal/lockclient/1.bash
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
for i in {1..10}
do
go test -race -run TestLockService >> logs
done
44 changes: 43 additions & 1 deletion internal/lockclient/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,18 @@ import "github.com/SystemBuilders/LocKey/internal/lockservice"

// Client describes a client that can be used to interact with
// the Lockey lockservice. The client can start the lockservice
// and interact acquire and release locks by making calls to it.
// and interact by making calls to it.
//
// The client has the ability to start the lockservice from its
// in-built function or it can be started separately.
//
// The client offers the user to Acquire a lock, Release a lock,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does ' the client offers the functional of Acquiring and Releasing an object using its descriptor' sound better?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ill go with the client allows the user.

// and Watch or Pounce on any object using it's descriptor.
//
// To acquire a lock on an object, the user is forced to go via
// the Pounce function in order to maintain the order of lock
// acquisition. The Acquire function that is exposed must cleverly
// handle this problem.
type Client interface {
// StartService starts the lockservice Lockey using the given
// configuration. It provides an appropriate error on failing
Expand All @@ -19,6 +30,22 @@ type Client interface {
// implementation interacts with the underlying server and
// provides the service.
Release(lockservice.Descriptors) error
// Watch can be used to watch the state of lock on a descriptor
// continously. When the state of the lock changes, the "watcher"
// will be notified about the change.
// The channel passed as the argument can be used to stop watching
// at any point of time.
watch(lockservice.ObjectDescriptor, chan struct{}) (chan Lock, error)
// Pounce can be used to "pounce" on a lock that has already been
// acquired. This is similar to acquire but once a process has
// opted to pounce, they will be provided first access by having
// a queue of pouncers.
// The second, third and fourth arguments dictate the end of the pouncing
// reign, the owner willing to pounce and allows pouncing on pre-pounced
// objects respectively. True bool allows pouncing on pre-pounced objects.
pounce(lockservice.ObjectDescriptor, string, chan struct{}, bool) error
// Pouncers returns the current pouncers on any particular lock.
pouncers(lockservice.ObjectDescriptor) []string
}

// Config describes the configuration for the lockservice to run on.
Expand All @@ -28,3 +55,18 @@ type Config interface {
// Port provides the port where the server is supposed to run.
Port() string
}

// State describes the state of a lock.
type State int

// These are the states of a lock.
const (
acquire State = iota
Release
)

// Lock includes the state of the lock and the owner.
type Lock struct {
Owner string
LockState State
}
Loading