-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add experimental locking options (#130)
* Add nested and map lockers * Add constructors * Wire in session manager * Update session logic in locker * Race condition patch * Add options to nested locker * Add cool-off locker * Change to TryLock * Switch to TryLock * Remove unused interface * Refactor map locker * Refactor session manager * Linter * Add options * Linter * Add options * Wire in useTryLock
- Loading branch information
Showing
12 changed files
with
695 additions
and
236 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
package lock | ||
|
||
import ( | ||
"fmt" | ||
"sync" | ||
"time" | ||
) | ||
|
||
const ( | ||
coolOffErrFormat = "cooloff expires in %s" | ||
) | ||
|
||
// NewCoolOffLocker creates a simple locker that will prevent a lock from | ||
// being obtained if a previous attempt (successful or not) was made within | ||
// the specified expiration period. It is intended to be used with other lockers | ||
// to prevent excessive locking using more expensive resources (e.g. etcd). It is | ||
// theoretically possible for two callers to obtain the same lock, if the cooloff | ||
// period expires before the first caller releases the lock; therefore this locker | ||
// needs to be used with a nested locker to prevent two callers from accessing the | ||
// same protected resource. | ||
func NewCoolOffLocker(expiration time.Duration) RuleLocker { | ||
locker := coolOffLocker{ | ||
coolOffDuration: expiration, | ||
locks: make(map[string]time.Time), | ||
mutex: &sync.Mutex{}, | ||
} | ||
return locker | ||
} | ||
|
||
type coolOffLocker struct { | ||
locks map[string]time.Time | ||
mutex *sync.Mutex | ||
coolOffDuration time.Duration | ||
} | ||
|
||
func (col coolOffLocker) Lock(key string, options ...Option) (RuleLock, error) { | ||
col.mutex.Lock() | ||
defer col.mutex.Unlock() | ||
now := time.Now() | ||
// Remove any expired keys | ||
var toDelete []string | ||
for k, v := range col.locks { | ||
if now.After(v) { | ||
toDelete = append(toDelete, k) | ||
} | ||
} | ||
for _, key := range toDelete { | ||
delete(col.locks, key) | ||
} | ||
var err error | ||
if _, ok := col.locks[key]; ok { | ||
err = fmt.Errorf(coolOffErrFormat, col.coolOffDuration) | ||
} | ||
// Failed attempts to get the lock should also update the cooloff, | ||
// so always add the key regardless of success or failure. | ||
col.locks[key] = now.Add(col.coolOffDuration) | ||
|
||
if err != nil { | ||
return nil, err | ||
} | ||
return coolOffLock{}, nil | ||
} | ||
|
||
type coolOffLock struct { | ||
} | ||
|
||
func (coolOffLock) Unlock() error { | ||
return nil | ||
} |
Oops, something went wrong.