Skip to content

Commit

Permalink
Update AcquireLock function to return existence status and modify REA…
Browse files Browse the repository at this point in the history
…DME.md accordingly
  • Loading branch information
youssefsiam38 committed Nov 13, 2024
1 parent 7a9c750 commit 71d8869
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 11 deletions.
21 changes: 16 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,13 @@ func main() {
runTask := func(taskID int) {
defer wg.Done()
postfix := fmt.Sprintf("%d", taskID)
success, err := tasklocker.AcquireLock(ctx, client, prefix, postfix, allowedConcurrentTasks, timeout)
success, exist, err := tasklocker.AcquireLock(ctx, client, prefix, postfix, allowedConcurrentTasks, timeout)
if err != nil {
log.Fatalf("failed to acquire lock for task %d: %v", taskID, err)
}
if success {
if exist {
fmt.Printf("Task %d could not acquire the lock because it already exists\n", taskID)
} else if success {
fmt.Printf("Task %d acquired the lock\n", taskID)
// Simulate task duration
time.Sleep(timeout / 2) // Run task for half of the timeout duration
Expand Down Expand Up @@ -100,10 +102,14 @@ func main() {
### `AcquireLock`

```go
func AcquireLock(ctx context.Context, client *redis.Client, prefix, postfix string, allowedConcurrentTasks int, timeout time.Duration) (bool, error)
func AcquireLock(ctx context.Context, client *redis.Client, prefix, postfix string, allowedConcurrentTasks int, timeout time.Duration) (bool, bool, error)
```

Attempts to acquire a lock for concurrent tasks using Redis. Returns `true` if the lock is successfully acquired, otherwise `false`.
Attempts to acquire a lock for concurrent tasks using Redis. Returns:
- `true` if the lock is successfully acquired,
- `false` if the lock cannot be acquired,
- `true` for the second return value if the key already exists (indicating that the task is already running or locked),
- `nil` if no error occurs or an error if the operation fails.

- **Parameters**:
- `ctx`: The context for the Redis operations.
Expand All @@ -113,6 +119,11 @@ Attempts to acquire a lock for concurrent tasks using Redis. Returns `true` if t
- `allowedConcurrentTasks`: The maximum number of concurrent tasks allowed.
- `timeout`: The duration after which the lock should be automatically released.

- **Return Values**:
- `success` (`bool`): Indicates whether the lock was successfully acquired.
- `exist` (`bool`): Indicates whether the key already exists (`true` if the key exists, `false` otherwise).
- `err` (`error`): The error encountered, if any.

### `ReleaseLock`

```go
Expand All @@ -137,4 +148,4 @@ Contributions are welcome! Please open an issue or submit a pull request with yo

## Author

Youssef Siam - [GitHub](https://github.com/youssefsiam38)
Youssef Siam - [GitHub](https://github.com/youssefsiam38)
23 changes: 17 additions & 6 deletions tasklocker.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,36 +9,47 @@ import (
)

// AcquireLock tries to acquire a lock for concurrent tasks using Redis.
// It returns true if the lock is successfully acquired, otherwise false.
// It returns a boolean indicating whether the lock is acquired, a boolean indicating whether the key exists,
// and an error if something goes wrong.
// Parameters:
// - ctx: The context for the Redis operations.
// - client: The Redis client instance.
// - prefix: The prefix for the task key.
// - postfix: The unique identifier for the task (e.g., task id).
// - allowedConcurrentTasks: The maximum number of concurrent tasks allowed.
// - timeout: The duration after which the lock should be automatically released.
func AcquireLock(ctx context.Context, client *redis.Client, prefix, postfix string, allowedConcurrentTasks int, timeout time.Duration) (bool, error) {
func AcquireLock(ctx context.Context, client *redis.Client, prefix, postfix string, allowedConcurrentTasks int, timeout time.Duration) (bool, bool, error) {
// Create the task-specific key using the prefix and postfix (e.g., google_places_brands_processor:1)
taskKey := fmt.Sprintf("%s:%s", prefix, postfix)

// Check if the specific key already exists
exists, err := client.Exists(ctx, taskKey).Result()
if err != nil {
return false, false, fmt.Errorf("failed to check if key exists: %v", err)
}
if exists > 0 {
// The key exists, return true for "exist"
return false, true, nil
}

// Count how many tasks are currently active (matching the prefix)
keys, err := client.Keys(ctx, fmt.Sprintf("%s:*", prefix)).Result()
if err != nil {
return false, fmt.Errorf("failed to get keys with prefix: %v", err)
return false, false, fmt.Errorf("failed to get keys with prefix: %v", err)
}

// If the number of active tasks exceeds the allowedConcurrentTasks, do not acquire the lock
if len(keys) >= allowedConcurrentTasks {
return false, nil // Lock cannot be acquired
return false, false, nil // Lock cannot be acquired
}

// Try to acquire the lock for the task by setting a key with an expiration time
err = client.SetEx(ctx, taskKey, 1, timeout).Err()
if err != nil {
return false, fmt.Errorf("failed to set key: %v", err)
return false, false, fmt.Errorf("failed to set key: %v", err)
}

return true, nil // Lock acquired successfully
return true, false, nil // Lock acquired successfully
}

// ReleaseLock releases the lock for concurrent tasks by decrementing the counter in Redis.
Expand Down

0 comments on commit 71d8869

Please sign in to comment.