Giải phápKhách hàng
Tài liệu

29/9/2026

Tóm tắt

blog.share_blog

API reference @cogover/sdk: Project state và lock

Trang này thuộc API reference của @cogover/sdk.

Project state

State là JSON bền vững, có phạm vi theo workspace, project, namespace và key. State không gắn với một version nên vẫn còn khi publish version project mới.

interface StateEntry<T> {
  readonly value: T;
  readonly version: number;
  readonly expiresAt?: number;
  readonly createdAt: number;
  readonly updatedAt: number;
}
 
interface StateWriteOptions {
  readonly expectedVersion?: number;
  readonly ttlSeconds?: number;
}
 
interface StateDeleteOptions {
  readonly expectedVersion?: number;
}
 
interface StateNamespace {
  readonly name: string;
  get<T>(key: string): Promise<StateEntry<T> | null>;
  set<T>(key: string, value: T, options?: StateWriteOptions): Promise<StateEntry<T>>;
  delete(key: string, options?: StateDeleteOptions): Promise<boolean>;
}
 
interface ProjectState {
  namespace(name: string): StateNamespace;
}
const progress = state.namespace("inventory-sync");
const current = await progress.get<{ cursor: string }>("cursor");
 
const saved = await progress.set("cursor", { cursor: "next-page" }, {
  expectedVersion: current?.version ?? 0,
  ttlSeconds: 3600,
});
 
await progress.delete("cursor", { expectedVersion: saved.version });

state.namespace(name): StateNamespace

Tạo client immutable cho namespace. Namespace dài tối đa 128 ký tự; key dài tối đa 255 ký tự. Cả hai phải bắt đầu bằng chữ hoặc số ASCII và chỉ chứa chữ, số, ., _, :, /, -.

namespace.get<T>(key): Promise<StateEntry<T> | null>

Trả null khi không có hoặc đã hết hạn. Entry gồm value, version, createdAt, updatedAt và expiresAt tuỳ chọn; timestamp là Unix milliseconds.

namespace.set<T>(key, value, options?): Promise<StateEntry<T>>

Ghi một giá trị JSON tối đa 32 KiB: 32.768 byte UTF-8 của dạng JSON.stringify, trong đó dấu ngoặc kép, dấu gạch chéo ngược hoặc ký tự điều khiển được tính theo chuỗi escape của nó. ttlSeconds từ 1 giây đến 365 ngày; bỏ qua để state không tự hết hạn. Hai option phải là safe integer. expectedVersion áp dụng compare-and-set: 0 chỉ tạo khi chưa tồn tại, số dương chỉ ghi đúng version, bỏ qua để upsert vô điều kiện. Xung đột ném StateConflictError; entry trả về chứa version mới. Mỗi project giữ tối đa 100.000 entry chưa hết hạn: set tạo thêm entry mới khi đã đủ sẽ ném RateLimitError và không ghi gì, còn cập nhật key đã có vẫn hoạt động bình thường. Entry hết hạn được tự động xoá và không tính vào giới hạn.

Version đếm số lần ghi kể từ khi entry được tạo. Sau delete hoặc khi hết hạn, lần set tiếp theo tạo entry mới có version bắt đầu lại từ 1, nên một version đã đọc trước khi xoá có thể khớp với một entry mới không liên quan (vấn đề ABA). Khi điều này quan trọng, hãy lưu thêm một token duy nhất của bạn trong value và so sánh cả token đó.

namespace.delete(key, options?): Promise<boolean>

Xoá và trả true nếu entry tồn tại. Có thể dùng expectedVersion như khi set. State hết hạn được xem như không tồn tại. State API không tạo transaction với Record API; không dùng để lưu secret, file hoặc dữ liệu lớn.

Distributed locks

Lock là lease phân tán theo workspace, project, namespace và key, dùng để ngăn các execution đồng thời chạy cùng một đoạn xử lý. Cách an toàn mặc định là withLock:

interface LockOptions {
  readonly namespace?: string;
  readonly waitMs?: number;
  readonly leaseMs?: number;
}
 
interface LockLease {
  readonly id: string;
  readonly key: string;
  readonly namespace: string;
  readonly fencingToken: number;
  readonly expiresAt: number;
  renew(leaseMs?: number): Promise<void>;
  release(): Promise<void>;
}
 
interface DistributedLocks {
  acquire(key: string, options?: LockOptions): Promise<LockLease | null>;
  withLock<T>(key: string, callback: (lease: LockLease) => T | Promise<T>): Promise<T>;
  withLock<T>(key: string, options: LockOptions,
    callback: (lease: LockLease) => T | Promise<T>): Promise<T>;
}
await locks.withLock("INV-1", { namespace: "invoice", waitMs: 500 }, async lease => {
  await processInvoice("INV-1", lease.fencingToken);
  // Chỉ cần renew khi công việc có thể tiến gần thời điểm expiresAt.
  // await lease.renew(30_000);
});

locks.acquire(key, options?): Promise<LockLease | null>

Trả lease hoặc null khi không lấy được trong waitMs. Namespace mặc định là default; waitMs từ 0 đến 10.000 ms; leaseMs từ 5.000 đến 60.000 ms và mặc định 30.000 ms. Mỗi invocation giữ tối đa 16 lock; lấy lock thứ 17 ném ValidationError. Key dài tối đa 255 ký tự, namespace tối đa 128 và dùng cùng quy tắc ký tự như tên state. Numeric option phải là safe integer.

Thời gian chờ được tính vào wall-clock của lần thực thi. Trong HTTP route, mặc định có tổng cộng 8 giây (xem Giới hạn của một lần thực thi), waitMs gần bằng hoặc lớn hơn thời gian còn lại sẽ kết thúc toàn bộ lần thực thi với response HTTP 422 chung thay vì trả null; hãy giữ waitMs nhỏ hơn nhiều so với thời gian còn lại.

Lease có id, key, namespace, fencingToken, expiresAt, cùng renew(leaseMs?) và release(). Cogover renew và release lease theo id trong phạm vi project đã lấy lock: mọi lần thực thi của cùng project biết id đều có thể renew hoặc release lease đó, còn project khác thì không. Hãy giữ id riêng tư và không trả nó cho caller. Lease mất hoặc hết hạn ném LockLostError. release() lặp lại trên cùng object lease là no-op sau lần thành công.

locks.withLock(key, callback) / locks.withLock(key, options, callback)

Lấy lock, chạy callback rồi release lease. Nếu không lấy được lock, hàm ném LockUnavailableError. Khi callback ném lỗi, lease được release và lỗi của callback được ném lại; nếu việc release đó cũng lỗi thì lỗi release bị bỏ qua và lease hết hạn sau leaseMs. Khi callback thành công nhưng release lỗi, ví dụ LockLostError vì lease đã hết hạn trong lúc callback chạy, lỗi đó được ném thay cho kết quả của callback. Hàm không tự renew; callback dài phải gọi lease.renew() trước khi expiresAt.

Lock chỉ bảo đảm một lease hợp lệ tại một thời điểm; timeout, pause hoặc lỗi mạng có thể làm execution cũ tiếp tục sau khi lease hết hạn. Khi hệ thống đích hỗ trợ, hãy lưu/kiểm tra fencingToken và từ chối token cũ. Lock không thay thế idempotency, không tạo exactly-once và không gộp Record API với state thành một transaction.

avatar

blog.blogs_related

Giải pháp tùy biến và hợp nhất

Số hóa và tự động hóa hoàn toàn công tác vận hành và quản trị doanh nghiệp với Cogover!

Bắt đầu đổi mới phương thức vận hành và tự chủ hệ thống quản trị công việc của bạn

Dùng thử ngay
CogoverCogover

CÔNG TY CỔ PHẦN STRINGEE

GPKD số 0107938504, đăng ký ngày 27-07-2017, nơi cấp Sở Tài chính Thành phố Hà Nội
Địa chỉ: Tầng 19, Leadvisors Tower, số 643 Phạm Văn Đồng, Phường Nghĩa Đô, Thành phố Hà Nội, Việt Nam

Văn phòng

Hà Nội: Tầng 17, VP2, tòa Sun Square, 21 Lê Đức Thọ, phường Từ Liêm, TP Hà Nội
TP Hồ Chí Minh: Tòa nhà Lô A, Khu chung cư Tân Phước, 153 Lý Thường Kiệt, phường Minh Phụng, TP Hồ Chí Minh
Mỹ: 838 Walker Road, Suite 21-2 Dover, Delaware 19904, United States
Ấn Độ: 804/805, Kosha Kommercial Komplex, Primal Nagar, Malad East, Mumbai - 400097
Dubai: CWS-1V-792740, Amber Gem Tower, Ajman, United Arab Emirates
Đã xác nhận
© 2026 Cogover LLC