import { scope, Lock } from 'go-go-scope';await using s = scope();const mutex = new Lock(s.signal);await using guard = await mutex.acquire();// Critical section - only one task can be hereawait performCriticalOperation();// Lock automatically released when guard is disposed
import { scope, Lock } from 'go-go-scope';import { RedisAdapter } from '@go-go-scope/persistence-redis';import Redis from 'ioredis';const redis = new Redis();const adapter = new RedisAdapter(redis);await using s = scope();const distLock = new Lock(s.signal, { provider: adapter, key: 'resource:123', ttl: 30000, // 30 seconds owner: process.env.WORKER_ID});await using guard = await distLock.acquire();// Critical section - only one process can be hereawait performCriticalOperation();
import { PostgresAdapter } from '@go-go-scope/persistence-postgres';import { Pool } from 'pg';const pool = new Pool({ connectionString: process.env.DATABASE_URL});const adapter = new PostgresAdapter(pool);
import { MySQLAdapter } from '@go-go-scope/persistence-mysql';import mysql from 'mysql2/promise';const pool = mysql.createPool({ host: process.env.MYSQL_HOST, user: process.env.MYSQL_USER, password: process.env.MYSQL_PASSWORD, database: process.env.MYSQL_DATABASE});const adapter = new MySQLAdapter(pool);
Distributed locks have a time-to-live (TTL) to prevent deadlocks:
const lock = new Lock(s.signal, { provider: adapter, key: 'resource:123', ttl: 30000 // Lock expires after 30 seconds});await using guard = await lock.acquire();// If operation takes > 30s, lock expires and another process can acquire it
Choose a TTL longer than your expected operation duration. If the operation takes too long, the lock may expire and another process could acquire it.
// Bad - long critical sectionawait using guard = await mutex.acquire();const data = await fetchData();await processData(data);await saveData(data);// Good - minimal critical sectionconst data = await fetchData();await processData(data);await using guard = await mutex.acquire();await saveData(data);
Always use timeouts
Prevent indefinite blocking:
await using guard = await mutex.acquire({ timeout: 10000 });
Use read-write locks for read-heavy workloads
If you have many readers and few writers, use read-write locks:
const rwlock = new Lock(s.signal, { allowMultipleReaders: true });// Many concurrent readersawait using r1 = await rwlock.read();await using r2 = await rwlock.read();// Exclusive writerawait using w = await rwlock.write();
Use unique lock keys
For distributed locks, use globally unique keys:
// Goodconst lock = new Lock(s.signal, { key: `user:${userId}:profile`});// Bad - too generic, high contentionconst lock = new Lock(s.signal, { key: 'lock'});