-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
447 additions
and
71 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
# distrlock | ||
|
||
[![GoDoc Widget]][GoDoc] | ||
|
||
## Overview | ||
|
||
`distrlock` is a Go package that implements distributed locking using SQL databases. | ||
It allows multiple processes or services to coordinate access to shared resources by acquiring and releasing locks stored in a database. | ||
|
||
### Features | ||
- Distributed lock management using SQL databases (PostgreSQL, MySQL are supported now) | ||
- Support for acquiring, releasing, and extending locks | ||
- Configurable lock expiration times | ||
|
||
## How It Works | ||
|
||
`distrlock` uses a relational database to implement distributed locking. When a process acquires a lock, a record is inserted or updated in a designated table within the database. The lock entry includes a unique key, a token for verification, and an expiration time to handle failures or crashes. Other processes attempting to acquire the same lock must wait until it is released or expires. If required, the lock can be extended before expiration to prevent unintended release. | ||
|
||
This approach ensures reliable concurrency control without requiring an external distributed coordination system like Zookeeper or etcd, making it lightweight and easy to integrate into existing systems that already use SQL databases. | ||
|
||
## Usage | ||
|
||
`distlock` provides a simple API for acquiring and releasing locks. | ||
|
||
The following basic example demonstrates how to use `distrlock` to ensure exclusive execution of a critical section of code: | ||
|
||
```go | ||
package distrlock_test | ||
|
||
import ( | ||
"context" | ||
"database/sql" | ||
"log" | ||
"os" | ||
"time" | ||
|
||
"github.com/acronis/go-dbkit" | ||
"github.com/acronis/go-dbkit/distrlock" | ||
) | ||
|
||
func ExampleDoExclusively() { | ||
// Setup database connection | ||
db, err := sql.Open("mysql", os.Getenv("MYSQL_DSN")) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
defer db.Close() | ||
|
||
ctx := context.Background() | ||
|
||
// Create "distributed_locks" table for locks. | ||
createTableSQL, err := distrlock.CreateTableSQL(dbkit.DialectMySQL) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
_, err = db.ExecContext(ctx, createTableSQL) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
// Do some work exclusively. | ||
const lockKey = "test-lock-key-1" // Unique key that will be used to ensure exclusive execution among multiple instances | ||
err = distrlock.DoExclusively(ctx, db, dbkit.DialectMySQL, lockKey, func(ctx context.Context) error { | ||
time.Sleep(10 * time.Second) // Simulate work. | ||
return nil | ||
}) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
} | ||
``` | ||
|
||
If you need more customization or/and control over the lock lifecycle, you can use `DBManager` and `DBLock` objects directly: | ||
|
||
```go | ||
package distrlock_test | ||
|
||
import ( | ||
"context" | ||
"database/sql" | ||
"log" | ||
"os" | ||
"time" | ||
|
||
"github.com/acronis/go-dbkit" | ||
"github.com/acronis/go-dbkit/distrlock" | ||
) | ||
|
||
func ExampleNewDBManager() { | ||
// Setup database connection | ||
db, err := sql.Open("mysql", os.Getenv("MYSQL_DSN")) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
defer db.Close() | ||
|
||
// Create DBManager | ||
lockManager, err := distrlock.NewDBManager(dbkit.DialectMySQL, | ||
distrlock.WithTableName("my_distributed_locks")) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
ctx := context.Background() | ||
|
||
// Create table for locks. | ||
_, err = db.ExecContext(ctx, lockManager.CreateTableSQL()) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
const lockKey = "test-lock-key-2" // Unique key that will be used to ensure exclusive execution among multiple instances | ||
|
||
// Create lock. | ||
lock, err := lockManager.NewLock(ctx, db, lockKey) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
// Acquire lock, do some work and release lock. | ||
const lockTTL = 10 * time.Second | ||
if err = lock.Acquire(ctx, db, lockTTL); err != nil { | ||
log.Fatal(err) | ||
} | ||
defer func() { | ||
if err = lock.Release(ctx, db); err != nil { | ||
log.Fatal(err) | ||
} | ||
}() | ||
|
||
time.Sleep(10 * time.Second) // Simulate work | ||
} | ||
``` | ||
|
||
## License | ||
|
||
Copyright © 2024 Acronis International GmbH. | ||
|
||
Licensed under [MIT License](./../LICENSE). | ||
|
||
[GoDoc]: https://pkg.go.dev/github.com/acronis/go-dbkit/distrlock | ||
[GoDoc Widget]: https://godoc.org/github.com/acronis/go-dbkit/distrlock?status.svg | ||
|
Oops, something went wrong.