Skip to content
This repository has been archived by the owner on Jul 21, 2021. It is now read-only.

added a validation to check whether lock exists in case of network pa… #230

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
Open
Changes from 5 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
65 changes: 61 additions & 4 deletions zk/lock.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ type Lock struct {
seq int
}

// Initializing a map using the built-in make() function
// This map stores the lock_path of last successfully requested sequential ephemeral znode queued
// In case of any conflict, the sequence number is used to check whether lock has been acquired
var lockPathsByPath = make(map[string]string)

// NewLock creates a new lock instance using the provided connection, path, and acl.
// The path must be a node that is only used by this lock. A lock instances starts
// unlocked until Lock() is called.
Expand All @@ -47,13 +52,24 @@ func (l *Lock) Lock() error {
return ErrDeadlock
}

if seqZnodePath, ok := lockPathsByPath[l.path]; ok && seqZnodePath != "" {
// Check whether lock has been acquired previously and it still exists
if lockExists(l.c, l.path, seqZnodePath) {
return nil
}
}

prefix := fmt.Sprintf("%s/lock-", l.path)

path := ""
var err error
tryLock:
for i := 0; i < 3; i++ {
path, err = l.c.CreateProtectedEphemeralSequential(prefix, []byte{}, l.acl)
if err == ErrNoNode {
// Store the path of newly created sequential ephemeral znode against the parent znode path
lockPathsByPath[l.path] = path
switch err {
case ErrNoNode:
// Create parent node.
parts := strings.Split(l.path, "/")
pth := ""
Expand All @@ -72,9 +88,9 @@ func (l *Lock) Lock() error {
return err
}
}
} else if err == nil {
break
} else {
case nil:
continuum-Nikhil-Bhide marked this conversation as resolved.
Show resolved Hide resolved
break tryLock
default:
return err
}
}
Expand Down Expand Up @@ -146,5 +162,46 @@ func (l *Lock) Unlock() error {
}
l.lockPath = ""
l.seq = 0
// Remove the entry of path of newly created sequential ephemeral znode
// this was stored against the parent znode path
delete(lockPathsByPath, l.path)
return nil
}

//Check whether lock got created and response was lost because of network partition failure.
//It queries zookeeper and scans existing sequential ephemeral znodes under the parent path
//It finds out that previously requested sequence number corresponds to child having lowest sequence number
func lockExists(c *Conn, rootPath string, znodePath string) bool {
seq, err := parseSeq(znodePath)
if err != nil {
return false
}

//Scan the existing znodes if there are any
children, _, err := c.Children(rootPath)
if err != nil {
return false
}

lowestSeq := seq
prevSeq := -1
for _, p := range children {
s, err := parseSeq(p)
if err != nil {
return false
}
if s < lowestSeq {
lowestSeq = s
}
if s < seq && s > prevSeq {
prevSeq = s
}
}

if seq == lowestSeq {
// Acquired the lock
return true
} else {
continuum-Nikhil-Bhide marked this conversation as resolved.
Show resolved Hide resolved
return false
}
}