examples/

directory
v3.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 9, 2026 License: MIT

README ΒΆ

πŸ’‘ go-pglock Examples

This directory contains practical examples demonstrating various use cases for go-pglock.

πŸ“‹ Prerequisites

  1. 🐘 PostgreSQL running and accessible
  2. πŸ—„οΈ Database created (default: pglock)
  3. πŸ”§ Set DATABASE_URL environment variable (optional)
# Using docker-compose from the project root
docker-compose up -d

# Or set custom database URL
export DATABASE_URL='postgres://user:pass@localhost:5432/dbname?sslmode=disable'

πŸš€ Running Examples

πŸ”° Basic Lock Usage

Demonstrates the fundamental lock acquire and release pattern.

cd examples/basic
go run main.go

What it demonstrates:

  • πŸ”’ Creating a lock
  • βœ… Acquiring a lock with Lock() (non-blocking)
  • βš™οΈ Performing work in a critical section
  • πŸ”“ Releasing the lock

Try it: Run two instances in different terminals to see one acquire the lock while the other is blocked.

πŸ‘· Concurrent Workers

Shows how multiple workers coordinate access to a shared resource.

cd examples/workers
go run main.go

What it demonstrates:

  • πŸƒ Multiple goroutines competing for the same lock
  • ⏳ WaitAndLock() blocking behavior
  • πŸ“Š Sequential execution enforced by the lock
  • 🧹 Proper cleanup in concurrent scenarios

Expected output:

  • ⏸️ Workers start and wait for the lock
  • πŸ”„ Workers execute one at a time (serialized)
  • ⏱️ Each worker holds the lock for ~1 second
πŸ‘‘ Leader Election

Implements a simple leader election mechanism.

cd examples/leader-election
go run main.go

What it demonstrates:

  • πŸ—³οΈ Leader election pattern
  • βœ… Only one instance becomes the leader
  • πŸ“… Leader performing periodic tasks
  • πŸ‘₯ Followers waiting/skipping work

Use cases:

  • 🌐 Distributed systems where only one instance should perform certain tasks
  • πŸ”„ Active-passive failover scenarios
  • 🀝 Coordinating cluster-wide operations
⏱️ Timeout Handling

Shows how to use context timeouts with lock operations.

cd examples/timeout
go run main.go

What it demonstrates:

  • ⏰ Creating locks with timeout contexts
  • ⚠️ Handling context.DeadlineExceeded errors
  • 🎚️ Different timeout values and their effects
  • 🧹 Proper cleanup when timeouts occur

Expected output:

  • ❌ First attempt fails due to short timeout
  • βœ… Second attempt succeeds with longer timeout
βš™οΈ Task Processing

Demonstrates distributed task processing with lock-based coordination.

cd examples/task-processing
go run main.go

What it demonstrates:

  • 🏷️ Using task IDs to generate lock IDs
  • 🚫 Preventing duplicate task execution
  • #️⃣ Hash-based lock ID generation
  • ⚠️ Handling already-processing scenarios

Use cases:

  • πŸ“¬ Job queues in distributed systems
  • ♻️ Idempotent task processing
  • πŸ”’ Preventing race conditions in task execution
πŸ“– Read-Write Locks

Demonstrates shared (read) and exclusive (write) locks for concurrent reader scenarios.

cd examples/read-write-locks
go run main.go

What it demonstrates:

  • πŸ‘₯ Shared locks (RLock()) allowing multiple concurrent readers
  • πŸ”’ Exclusive locks (Lock()) blocking all other operations
  • πŸ”„ How readers and writers interact
  • πŸ“Š Three scenarios: multiple readers, writer blocking readers, readers blocking writer

Use cases:

  • πŸ“š Read-heavy workloads with occasional writes
  • πŸ’Ύ Cache management with multiple readers
  • βš™οΈ Configuration management
  • πŸ“Š Report viewing with generation locks
  • πŸ—„οΈ Database record access coordination

Expected output:

  • βœ… Scenario 1: Multiple readers acquire locks simultaneously
  • ⏳ Scenario 2: Writer blocks readers until write completes
  • ⏸️ Scenario 3: Readers block writer until all reads complete

πŸ”„ Running Multiple Instances

To see the distributed locking in action, run the same example in multiple terminal windows:

# πŸ’» Terminal 1
cd examples/basic
go run main.go

# πŸ’» Terminal 2 (run simultaneously)
cd examples/basic
go run main.go

You'll see that only one instance acquires the lock while others wait or skip.

πŸ› οΈ Customizing Examples

All examples use the same database connection pattern:

dsn := os.Getenv("DATABASE_URL")
if dsn == "" {
    dsn = "postgres://test:test@localhost:5432/pglock?sslmode=disable"
}

You can customize the connection by:

  1. πŸ”§ Setting the DATABASE_URL environment variable
  2. ✏️ Modifying the default DSN in the code
  3. πŸ”Œ Using connection parameters in the DSN string

πŸ’‘ Common Patterns

Pattern 1: Try Exclusive Lock (Non-blocking)

Use when you want to skip work if another instance is already doing it:

acquired, err := lock.Lock(ctx)
if !acquired {
    return // ⏭️ Skip work
}
defer lock.Unlock(ctx)
// βœ… Do work
Pattern 2: Try Shared Lock (Non-blocking)

Use when you want to read data but skip if a writer is active:

acquired, err := lock.RLock(ctx)
if !acquired {
    return // ⏭️ Skip reading, writer is active
}
defer lock.RUnlock(ctx)
// πŸ“– Read data (multiple readers can do this concurrently)
Pattern 3: Wait for Exclusive Lock (Blocking)

Use when work must be done eventually:

if err := lock.WaitAndLock(ctx); err != nil {
    return err
}
defer lock.Unlock(ctx)
// βœ… Do work
Pattern 4: Wait for Shared Lock (Blocking)

Use when you must read data eventually:

if err := lock.WaitAndRLock(ctx); err != nil {
    return err
}
defer lock.RUnlock(ctx)
// πŸ“– Read data
Pattern 5: Lock with Timeout

Use when you want to wait but not indefinitely:

ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()

if err := lock.WaitAndLock(ctx); err != nil {
    // ⚠️ Handle timeout or other errors
}
defer lock.Unlock(context.Background())
// βœ… Do work

πŸ—οΈ Building Examples

To build all examples:

# From the examples directory
for dir in */; do
    echo "Building ${dir%/}..."
    (cd "$dir" && go build -o "../../bin/${dir%/}" .)
done

This creates binaries in the bin/ directory.

πŸ”§ Troubleshooting

❌ "DATABASE_URL environment variable not set"

Either set the environment variable or use the default connection string in the code.

⚠️ "pq: database does not exist"

Create the database:

CREATE DATABASE pglock;
πŸ”Œ "connection refused"

Make sure PostgreSQL is running:

docker-compose up -d
πŸ”“ Lock Never Released

Check that:

  • βœ… defer lock.Close() is present
  • 🚫 Program doesn't panic before cleanup
  • ⏱️ Context isn't cancelled prematurely

πŸ“š Further Reading

Directories ΒΆ

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL