Redis distributed lock implementation with Lua scripting to enable atomic operations.
🔐 Atomic Lock Operations: Lua script-based lock acquisition and release to prevent race conditions
⚡ Smart Session Management: UUID-based session tracking with ownership validation
🔄 Auto Repeat Mechanism: Built-in repeat logic with progressive backoff in high-contention scenarios
🛡️ Lifecycle Management: Guaranteed lock cleanup with panic restoration and timeout handling
📊 Flexible Logging: Pluggable logging interface with custom implementation support
🎯 Two-Part Design: Core lock operations (redissuo
) with high-grade enclosure (redissuorun
)
go get github.com/go-xlan/redis-go-suo
package main
import (
"context"
"fmt"
"time"
"github.com/alicebob/miniredis/v2"
"github.com/go-xlan/redis-go-suo/redissuo"
"github.com/redis/go-redis/v9"
"github.com/yyle88/rese"
)
func main() {
// Start Redis instance to show demo
mrd := rese.P1(miniredis.Run())
defer mrd.Close()
// Setup Redis connection
rdb := redis.NewClient(&redis.Options{
Addr: mrd.Addr(),
})
defer rese.F0(rdb.Close)
// Init shared lock
lock := redissuo.NewSuo(rdb, "demo-lock", time.Minute*5)
// Get lock
ctx := context.Background()
session, err := lock.Acquire(ctx)
if err != nil {
panic(err)
}
if session == nil {
fmt.Println("Lock taken - used in different app")
return
}
fmt.Printf("Lock acquired! Session: %s\n", session.SessionUUID())
fmt.Printf("Lock timeout at: %s\n", session.Expire().Format(time.RFC3339))
// Run protected code
fmt.Println("Running protected zone...")
time.Sleep(time.Second * 2) // Mock task
// Free lock
success, err := lock.Release(ctx, session)
if err != nil {
panic(err)
}
if success {
fmt.Println("Lock freed!")
} else {
fmt.Println("Lock release failed - might be freed via timeout in different session")
}
}
⬆️ Source: Source
package main
import (
"context"
"fmt"
"time"
"github.com/alicebob/miniredis/v2"
"github.com/go-xlan/redis-go-suo/redissuo"
"github.com/go-xlan/redis-go-suo/redissuorun"
"github.com/redis/go-redis/v9"
"github.com/yyle88/rese"
)
func main() {
// Start Redis instance to show demo
mrd := rese.P1(miniredis.Run())
defer mrd.Close()
// Setup Redis connection
rdb := redis.NewClient(&redis.Options{
Addr: mrd.Addr(),
})
defer rese.F0(rdb.Close)
// Init shared lock
lock := redissuo.NewSuo(rdb, "app-lock", time.Minute*2)
fmt.Println("Beginning top-grade lock action...")
// Run function with auto lock handling
err := redissuorun.SuoLockRun(context.Background(), lock, func(ctx context.Context) error {
fmt.Println("Running protected zone with lock shield")
fmt.Println("Handling main business code...")
// Mock task that needs exclusive access
for i := 1; i <= 5; i++ {
fmt.Printf("Phase %d/5 working...\n", i)
time.Sleep(time.Millisecond * 300)
}
fmt.Println("Business code finished!")
return nil
}, time.Millisecond*100) // Wait time
if err != nil {
fmt.Printf("Lock action failed: %v\n", err)
return
}
fmt.Println("Lock action finished!")
}
⬆️ Source: Source
The foundation package providing core distributed lock operations:
Suo
: Main lock structure with Redis client, name, and TTL configurationXin
: Lock session representation with UUID and expiration tracking- Atomic Operations: Lua script-based acquire/release to maintain data coherence
High-grade enclosure providing lifecycle management:
SuoLockRun
: Execute function within lock boundaries with auto repeatSuoLockXqt
: Extended version with custom logging support- Panic Restoration: Automatic panic handling and lock cleanup
- Context Management: Timeout and cancellation support
// Extend existing lock session
extendedSession, err := lock.AcquireAgainExtendLock(ctx, session)
if err != nil {
// Handle extension failure
}
// Use extendedSession to enable continued operations
// Create custom logger
customLogger := logging.NewZapLogger(yourZapLogger)
// Use with custom logger
err := redissuorun.SuoLockXqt(ctx, lock, businessLogic, retryInterval, customLogger)
// Acquire with specific session UUID
sessionUUID := "your-custom-session-id"
session, err := lock.AcquireLockWithSession(ctx, sessionUUID)
// Access session information
fmt.Printf("Session UUID: %s\n", session.SessionUUID())
fmt.Printf("Expires at: %s\n", session.Expire())
rdb := redis.NewClusterClient(&redis.ClusterOptions{
Addrs: []string{"localhost:7000", "localhost:7001", "localhost:7002"},
})
lock := redissuo.NewSuo(rdb, "cluster-lock", time.Minute*10)
import "go.uber.org/zap"
// Create custom zap logger
logger, _ := zap.NewProduction()
customLogger := logging.NewZapLogger(logger)
// Use with lock operations
lock := redissuo.NewSuo(rdb, "logged-lock", time.Minute).
WithLogger(customLogger)
The project includes comprehensive tests using in-RAM Redis (miniredis):
# Run tests
go test ./...
# Run with verbose output
go test -v ./...
# Run with race detection
go test -race ./...
# Generate coverage report
go test -coverage ./...
redis-go-suo/
├── redissuo/ # Core lock implementation
│ ├── redis_suo.go # Main lock operations
│ └── redis_suo_test.go
├── redissuorun/ # High-grade enclosure
│ ├── redis_suo_run.go # Lifecycle management
│ └── redis_suo_run_test.go
└── internal/ # Private utilities
├── logging/ # Pluggable logging interface
└── utils/ # UUID generation utilities
Extend existing lock duration:
extendedSession, err := lock.AcquireAgainExtendLock(ctx, session)
if err != nil {
log.Printf("Failed to extend lock: %v", err)
}
Custom session UUID:
sessionUUID := "my-custom-session-123"
session, err := lock.AcquireLockWithSession(ctx, sessionUUID)
Silent logging in tests:
nopLogger := logging.NewNopLogger()
lock := redissuo.NewSuo(rdb, "test-lock", time.Minute).
WithLogger(nopLogger)
Custom Zap logging:
logger, _ := zap.NewDevelopment()
customLogger := logging.NewZapLogger(logger)
err := redissuorun.SuoLockXqt(ctx, lock, businessFunc, retryInterval, customLogger)
Handle lock acquisition timeout:
ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
defer cancel()
session, err := lock.Acquire(ctx)
if err != nil {
if errors.Is(err, context.DeadlineExceeded) {
fmt.Println("Lock acquisition timed out")
return
}
// Handle more errors
}
Lock release handling:
defer func() {
if session != nil {
if success, err := lock.Release(context.Background(), session); err != nil {
log.Printf("Warning: Failed to release lock: %v", err)
} else if !success {
log.Println("Warning: Lock was released")
}
}
}()
- Always Release Locks: Use defer with guaranteed cleanup mechanisms
- Handle Lock Failures: Check session state and handle as needed
- Set Appropriate TTLs: Balance between protection and performance
- Use Repeat Logic: Implement backoff strategies in high-contention scenarios
- Watch Lock Usage: Implement logging and metrics in production systems
MIT License. See LICENSE.
Contributions are welcome! Report bugs, suggest features, and contribute code:
- 🐛 Found a bug? Open an issue on GitHub with reproduction steps
- 💡 Have a feature idea? Create an issue to discuss the suggestion
- 📖 Documentation confusing? Report it so we can improve
- 🚀 Need new features? Share the use cases to help us understand requirements
- ⚡ Performance issue? Help us optimize through reporting slow operations
- 🔧 Configuration problem? Ask questions about complex setups
- 📢 Follow project progress? Watch the repo to get new releases and features
- 🌟 Success stories? Share how this package improved the workflow
- 💬 Feedback? We welcome suggestions and comments
New code contributions, follow this process:
- Fork: Fork the repo on GitHub (using the webpage UI).
- Clone: Clone the forked project (
git clone https://github.com/yourname/repo-name.git
). - Navigate: Navigate to the cloned project (
cd repo-name
) - Branch: Create a feature branch (
git checkout -b feature/xxx
). - Code: Implement the changes with comprehensive tests
- Testing: (Golang project) Ensure tests pass (
go test ./...
) and follow Go code style conventions - Documentation: Update documentation to support client-facing changes and use significant commit messages
- Stage: Stage changes (
git add .
) - Commit: Commit changes (
git commit -m "Add feature xxx"
) ensuring backward compatible code - Push: Push to the branch (
git push origin feature/xxx
). - PR: Open a pull request on GitHub (on the GitHub webpage) with detailed description.
Please ensure tests pass and include relevant documentation updates.
Welcome to contribute to this project via submitting merge requests and reporting issues.
Project Support:
- ⭐ Give GitHub stars if this project helps you
- 🤝 Share with teammates and (golang) programming friends
- 📝 Write tech blogs about development tools and workflows - we provide content writing support
- 🌟 Join the ecosystem - committed to supporting open source and the (golang) development scene
Have Fun Coding with this package! 🎉