Token导航 LogoToken导航TokenDH.com
前端设计权限需确认github未标认证来源可访问许可证需确认审计异常

distributed-lock分布式锁

Agent Skill

distributed-lock 用于处理 GitHub 仓库、Issue、Pull Request 和代码协作信息,适合在 Codex、Claude、Cursor、Gemini CLI 中需要围绕仓库状态、代码变更或协作事项进行整理时使用。可结合来源仓库、安装命令和原始 README 继续核验具体用法。安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写。

总安装

559

周安装

24

GitHub Stars

777

下载量

196
CodexClaudeCursorGemini CLI

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

GitHub

来源数

2

许可证

unknown

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:distributed-lock(分布式锁)
来源仓库:https://github.com/dadbodgeoff/drift
仓库路径:skills/distributed-lock
安装命令:
npx skills add https://github.com/dadbodgeoff/drift --skill distributed-lock
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 npx skills 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

skills.shnpx skills
npx skills add https://github.com/dadbodgeoff/drift --skill distributed-lock

简介

防止多实例竞争条件的分布式锁实现方案。

  • 支持 Redis、PostgreSQL 与 fencing token 多种类型。
  • 提供原子获取、TTL 过期与持有者 ID 追踪机制。
  • 适用于 cron 任务去重与临界区保护等场景。
  • distributed-lock 属于前端设计类 Skill,可作为该场景下的辅助能力补充。

SKILL.md

Distributed Locking

Prevent race conditions across multiple instances.

When to Use This Skill

  • Multiple instances processing the same job
  • Need to prevent duplicate operations
  • Singleton cron jobs across instances
  • Critical sections in distributed systems

Core Concepts

  1. Atomic acquisition - Only one holder at a time
  2. TTL expiration - Prevents deadlocks if holder crashes
  3. Lock extension - Renew for long-running operations
  4. Holder ID - Track who owns the lock

TypeScript Implementation

Types

// types.ts
export interface LockInfo {
  lockName: string;
  holderId: string;
  acquiredAt: Date;
  expiresAt: Date;
  metadata?: Record<string, unknown>;
}

export interface LockOptions {
  timeoutSeconds?: number;
  blocking?: boolean;
  blockingTimeoutSeconds?: number;
  metadata?: Record<string, unknown>;
}

export interface LockResult {
  acquired: boolean;
  lock?: LockInfo;
  error?: string;
}

In-Memory Lock (Single Instance)

// distributed-lock.ts
import { LockInfo, LockOptions, LockResult } from './types';

const lockStore = new Map<string, LockInfo>();

export class DistributedLock {
  private holderId: string;
  private heldLocks = new Map<string, LockInfo>();

  constructor() {
    this.holderId = `worker_${Date.now()}_${Math.random().toString(36).slice(2)}`;
  }

  async acquire(lockName: string, options: LockOptions = {}): Promise<LockResult> {
    const { timeoutSeconds = 30, blocking = false, blockingTimeoutSeconds = 10 } = options;

    const now = new Date();
    const expiresAt = new Date(now.getTime() + timeoutSeconds * 1000);

    // Clean expired locks
    for (const [name, lock] of lockStore) {
      if (lock.expiresAt <= now) lockStore.delete(name);
    }

    const existing = lockStore.get(lockName);

    if (existing && existing.expiresAt > now) {
      if (existing.holderId === this.holderId) {
        // Extend our own lock
        existing.expiresAt = expiresAt;
        return { acquired: true, lock: existing };
      }

      if (blocking) {
        return this.waitForLock(lockName, options);
      }

      return { acquired: false, error: `Lock held by ${existing.holderId}` };
    }

    const lock: LockInfo = {
      lockName,
      holderId: this.holderId,
      acquiredAt: now,
      expiresAt,
      metadata: options.metadata,
    };

    lockStore.set(lockName, lock);
    this.heldLocks.set(lockName, lock);

    return { acquired: true, lock };
  }

  async release(lockName: string): Promise<boolean> {
    const lock = lockStore.get(lockName);
    if (!lock || lock.holderId !== this.holderId) return false;

    lockStore.delete(lockName);
    this.heldLocks.delete(lockName);
    return true;
  }

  async extend(lockName: string, additionalSeconds: number): Promise<boolean> {
    const lock = lockStore.get(lockName);
    if (!lock || lock.holderId !== this.holderId) return false;

    lock.expiresAt = new Date(Date.now() + additionalSeconds * 1000);
    return true;
  }

  async withLock<T>(
    lockName: string,
    fn: () => Promise<T>,
    options: LockOptions = {}
  ): Promise<T> {
    const result = await this.acquire(lockName, options);
    if (!result.acquired) {
      throw new Error(`Failed to acquire lock: ${lockName}`);
    }

    try {
      return await fn();
    } finally {
      await this.release(lockName);
    }
  }

  private async waitForLock(lockName: string, options: LockOptions): Promise<LockResult> {
    const timeout = (options.blockingTimeoutSeconds || 10) * 1000;
    const startTime = Date.now();

    while (Date.now() - startTime < timeout) {
      await new Promise(r => setTimeout(r, 100));
      const result = await this.acquire(lockName, { ...options, blocking: false });
      if (result.acquired) return result;
    }

    return { acquired: false, error: 'Timeout waiting for lock' };
  }

  async releaseAll(): Promise<void> {
    for (const lockName of this.heldLocks.keys()) {
      await this.release(lockName);
    }
  }
}

// Singleton
let instance: DistributedLock | null = null;
export function getDistributedLock(): DistributedLock {
  if (!instance) instance = new DistributedLock();
  return instance;
}

PostgreSQL Lock (Multi-Instance)

-- migrations/distributed_locks.sql

CREATE TABLE distributed_locks (
    lock_name VARCHAR(255) PRIMARY KEY,
    holder_id VARCHAR(255) NOT NULL,
    acquired_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    expires_at TIMESTAMPTZ NOT NULL,
    metadata JSONB DEFAULT '{}'
);

CREATE INDEX idx_locks_expires ON distributed_locks(expires_at);

-- Atomic lock acquisition
CREATE OR REPLACE FUNCTION acquire_lock(
    p_lock_name VARCHAR(255),
    p_holder_id VARCHAR(255),
    p_ttl_seconds INTEGER DEFAULT 30,
    p_metadata JSONB DEFAULT '{}'
) RETURNS BOOLEAN AS $$
DECLARE
    v_now TIMESTAMPTZ := NOW();
    v_expires_at TIMESTAMPTZ := v_now + (p_ttl_seconds || ' seconds')::INTERVAL;
BEGIN
    -- Clean expired
    DELETE FROM distributed_locks
    WHERE lock_name = p_lock_name AND expires_at < v_now;

    -- Try to acquire
    INSERT INTO distributed_locks (lock_name, holder_id, acquired_at, expires_at, metadata)
    VALUES (p_lock_name, p_holder_id, v_now, v_expires_at, p_metadata)
    ON CONFLICT (lock_name) DO UPDATE
    SET holder_id = EXCLUDED.holder_id,
        acquired_at = EXCLUDED.acquired_at,
        expires_at = EXCLUDED.expires_at
    WHERE distributed_locks.holder_id = p_holder_id
       OR distributed_locks.expires_at < v_now;

    RETURN EXISTS (
        SELECT 1 FROM distributed_locks
        WHERE lock_name = p_lock_name AND holder_id = p_holder_id
    );
END;
$$ LANGUAGE plpgsql;

-- Release lock
CREATE OR REPLACE FUNCTION release_lock(
    p_lock_name VARCHAR(255),
    p_holder_id VARCHAR(255)
) RETURNS BOOLEAN AS $$
BEGIN
    DELETE FROM distributed_locks
    WHERE lock_name = p_lock_name AND holder_id = p_holder_id;
    RETURN FOUND;
END;
$$ LANGUAGE plpgsql;

Python Implementation

# distributed_lock.py
import time
import uuid
from dataclasses import dataclass
from typing import Optional, Callable, TypeVar
from contextlib import asynccontextmanager

T = TypeVar('T')

@dataclass
class LockInfo:
    lock_name: str
    holder_id: str
    acquired_at: float
    expires_at: float

class DistributedLock:
    def __init__(self, db):
        self.db = db
        self.holder_id = f"worker_{uuid.uuid4().hex[:8]}"

    async def acquire(
        self,
        lock_name: str,
        timeout_seconds: int = 30
    ) -> bool:
        result = await self.db.execute(
            "SELECT acquire_lock($1, $2, $3)",
            lock_name, self.holder_id, timeout_seconds
        )
        return result[0][0]

    async def release(self, lock_name: str) -> bool:
        result = await self.db.execute(
            "SELECT release_lock($1, $2)",
            lock_name, self.holder_id
        )
        return result[0][0]

    @asynccontextmanager
    async def lock(self, lock_name: str, timeout_seconds: int = 30):
        acquired = await self.acquire(lock_name, timeout_seconds)
        if not acquired:
            raise Exception(f"Failed to acquire lock: {lock_name}")
        try:
            yield
        finally:
            await self.release(lock_name)

# Usage
async with lock.lock("job:123"):
    await process_job("123")

Usage Examples

Basic Lock

const lock = getDistributedLock();

const result = await lock.acquire('process-payment:order-123');

if (result.acquired) {
  try {
    await processPayment('order-123');
  } finally {
    await lock.release('process-payment:order-123');
  }
} else {
  console.log('Another instance is processing this order');
}

With Context Manager

await lock.withLock('daily-report', async () => {
  await generateDailyReport();
}, { timeoutSeconds: 300 });

Long-Running Job with Extension

async function processJob(jobId: string) {
  const lock = getDistributedLock();

  const result = await lock.acquire(`job:${jobId}`, { timeoutSeconds: 60 });
  if (!result.acquired) return;

  try {
    // Extend lock periodically for long jobs
    const extendInterval = setInterval(async () => {
      await lock.extend(`job:${jobId}`, 60);
    }, 30000);

    await doExpensiveWork(jobId);

    clearInterval(extendInterval);
  } finally {
    await lock.release(`job:${jobId}`);
  }
}

Singleton Cron Job

async function runScheduledTask() {
  const lock = getDistributedLock();

  const result = await lock.acquire('cron:daily-cleanup', {
    timeoutSeconds: 3600,
  });

  if (!result.acquired) {
    console.log('Another instance is running daily cleanup');
    return;
  }

  try {
    await performDailyCleanup();
  } finally {
    await lock.release('cron:daily-cleanup');
  }
}

Lock Naming Conventions

// Resource-based
`lock:user:${userId}:profile-update`
`lock:order:${orderId}:process`

// Job-based
`job:${jobType}:${jobId}`

// Singleton operations
`cron:${taskName}`
`singleton:cache-refresh`

Best Practices

  1. Set appropriate TTL - Match operation duration
  2. Extend for long ops - Don't let locks expire mid-operation
  3. Unique holder IDs - Per instance, not per request
  4. Release in finally - Always release, even on error
  5. Use withLock - Automatic acquire/release

Common Mistakes

  • TTL too short for operation
  • Not extending long-running locks
  • Forgetting to release on error
  • Same holder ID across instances
  • Not handling acquisition failure

Related Skills

适合场景

01

用户想查找某类 Agent Skill 时

02

需要根据任务场景推荐可安装能力包时

03

需要对比不同来源的安装命令和来源信息时

能力概览

能力 1

按任务关键词查找相关 Skills

能力 2

展示可复制的安装命令

能力 3

保留来源站点、仓库和原始说明,方便继续核验

能力 4

展示第三方安全扫描或审计结果

安装后应在对应宿主中按原始 README 的触发条件使用;具体调用方式请以来源页面和 README 为准。

平台分布

Codex

37.91%
按下载量换算74

Claude

32.9%
按下载量换算64

Cursor

17.68%
按下载量换算35

Gemini CLI

8.95%
按下载量换算18

安全审计

Gen Agent Trust Hub

未通过

Socket

通过

Snyk

通过

权限和风险

权限需确认

当前来源未能明确判断权限范围,默认进入异常复核队列。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。来源安全扫描存在 warning/failed 结果,不能写成本站确认安全。当前只有一个来源,正式发布前建议补源仓库或其他目录站核验。

来源信息

继续浏览同类 Skills