Skip to main content

ai-core-locks

LockStore, InMemoryLockStore, LocksCapability and withLocks for multi-instance coordination in TanStack AI. Ships in @tanstack/ai — NOT in @tanstack/ai-persistence. Separate from AIPersistence state stores — not a stores key, not composable. InMemoryLockStore vs a distributed (e.g. Cloudflare Durable Object) lock, lease recovery, AbortSignal in critical sections. Use when sandbox or other middleware needs cross-worker mutual exclusion — NOT for storing messages/runs (use withPersistence).

소스 정보

저장소
TanStack/ai
최근 소스 활동
2026년 9월 9일 06:08
감지된 SKILL.md 언어
영어
스타
3,199
포크
366

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
ai-core/locks
description
LockStore, InMemoryLockStore, LocksCapability and withLocks for multi-instance coordination in TanStack AI. Ships in @tanstack/ai — NOT in @tanstack/ai-persistence. Separate from AIPersistence state stores — not a stores key, not composable. InMemoryLockStore vs a distributed (e.g. Cloudflare Durable Object) lock, lease recovery, AbortSignal in critical sections. Use when sandbox or other middleware needs cross-worker mutual exclusion — NOT for storing messages/runs (use withPersistence).
type
sub-skill
library
tanstack-ai
library_version
0.42.0
sources
["TanStack/ai:docs/advanced/locks.md","TanStack/ai:packages/ai/src/activities/chat/middleware/locks.ts"]
# Locks (coordination — not persistence) > **Dependency note:** This skill builds on ai-core and ai-core/middleware. > `withLocks` is a ChatMiddleware that provides a capability. Locks are **not** > part of `AIPersistence.stores` and are **not** composed with > `composePersistence` — they ship in `@tanstack/ai`, independent of > `@tanstack/ai-persistence`. ## Why separate? State stores answer "what is durable chat data?" Locks answer "who may run this critical section right now?" `withPersistence` does **not** automatically lock a whole turn. Take a per-thread (or other) lock yourself when multi-writer races matter. ## Wire locks ```ts import { chat } from '@tanstack/ai' import { withLocks, InMemoryLockStore } from '@tanstack/ai/locks' import { openaiText } from '@tanstack/ai-openai' const messages = [{ role: 'user' as const, content: 'Hello' }] chat({ adapter: openaiText('gpt-5.6'), messages, middleware: [ withLocks(new InMemoryLockStore()), // single process ], }) ``` Alongside persistence — optional, locks do not require it: ```ts import { chat } from '@tanstack/ai' import { withLocks, InMemoryLockStore } from '@tanstack/ai/locks' import { openaiText } from '@tanstack/ai-openai' import { memoryPersistence, withPersistence } from '@tanstack/ai-persistence' const persistence = memoryPersistence() const messages = [{ role: 'user' as const, content: 'Hello' }] chat({ adapter: openaiText('gpt-5.6'), messages, middleware: [ withPersistence(persistence), withLocks(new InMemoryLockStore()), ], }) ``` `withLocks` provides `LocksCapability` for downstream middleware (e.g. sandbox). Order: usually state first, locks alongside or after depending on who consumes the capability. ## The contract ```ts interface LockStore { withLock<T>(key: string, fn: (signal: AbortSignal) => Promise<T>): Promise<T> } ``` `InMemoryLockStore` ships in **`@tanstack/ai/locks`**: a per-key promise chain, correct **within a single process only**. Multi-instance deployments need a distributed implementation — you write it. The Cloudflare Durable Object recipe is in **ai-persistence/build-cloudflare-adapter** (`@tanstack/ai-persistence`). Type your own store with `defineLock` (autocomplete, no `: LockStore` annotation), then hand it to `withLocks`. Acquire the key, run `fn`, release when `fn` settles: ```ts import { chat } from '@tanstack/ai' import { defineLock, withLocks } from '@tanstack/ai/locks' import { openaiText } from '@tanstack/ai-openai' import { acquire } from './my-lock-backend' const locks = defineLock({ async withLock(key, fn) { const { release, signal } = await acquire(key) try { return await fn(signal) } finally { release() } }, }) const messages = [{ role: 'user' as const, content: 'Hello' }] chat({ adapter: openaiText('gpt-5.6'), messages, middleware: [withLocks(locks)], }) ``` ## Lease semantics A good `LockStore`: - Serializes owners per key, - Uses **leases** (or equivalent) so a crashed owner cannot block forever, - Passes an `AbortSignal` into the critical section via `withLock`; when the lease is lost, abort so work stops starting external mutations. Callbacks must honor the signal and pass it to cancellable dependencies. `InMemoryLockStore` never aborts its signal — within one process, ownership cannot be lost. ## Capability identity The `'locks'` capability token lives in `@tanstack/ai/locks`. Capability identity is by **object reference**, so one shared token means a `withLocks` in the chain reaches `withSandbox` automatically. ## Common mistakes ### HIGH: Importing locks from `@tanstack/ai-persistence` They are not exported there. Use `@tanstack/ai`. ### HIGH: Putting `locks` on `AIPersistence.stores` Not supported. `stores` accepts only `messages`, `runs`, `interrupts`, `metadata` — never `locks`. Use `withLocks`. ### HIGH: Passing `locks` to `composePersistence` overrides Same rejection, at the override layer. Locks are not state. ### HIGH: Passing `'locks'` to the conformance testkit's `skip` `skip` accepts only chat state store keys. The suite does not cover locks at all — test lease expiry and abort separately. ### HIGH: `InMemoryLockStore` across multiple processes No mutual exclusion between machines — use a distributed lock store. ### MEDIUM: Ignoring lease abort Continuing work after losing the lease races other owners. ## Cross-references - See also: **ai-core/middleware/SKILL.md** -- the middleware chain and capability plumbing - See also: **`@tanstack/ai-persistence` skills** (`skills/ai-persistence/SKILL.md` in that package) -- `ai-persistence/server` (state middleware) and `ai-persistence/build-cloudflare-adapter` (Durable Object lock recipe)
GitHub에서 보기