UNPKG

@daiso-tech/core

Version:

The library offers flexible, framework-agnostic solutions for modern web applications, built on adaptable components that integrate seamlessly with popular frameworks like Next Js.

179 lines (178 loc) 7.67 kB
/** * @module Semaphore */ import { type IReadableContext } from "../../execution-context/contracts/_module.js"; import { type InvokableFn } from "../../utilities/_module.js"; /** * Expiration configuration for a single semaphore slot. * Tracks when a slot becomes available again (expires). * Null expiration means the slot never expires automatically. * * IMPORT_PATH: `"@daiso-tech/core/semaphore/contracts"` * @group Contracts */ export type ISemaphoreSlotExpirationData = { /** * The expiration date and time of the slot. * After this time, the slot is automatically released and becomes available for reuse. * Null indicates the slot does not expire (persists until explicitly removed). */ expiration: Date | null; }; /** * Single semaphore slot data persisted in the database. * Represents one unit/position in a limited resource pool. * * Each slot has a unique `id` and tracks expiration time. * When a request acquires a slot, the slot is marked with expiration. * When expired or explicitly removed, the slot becomes available again. * * IMPORT_PATH: `"@daiso-tech/core/semaphore/contracts"` * @group Contracts */ export type ISemaphoreSlotData = ISemaphoreSlotExpirationData & { /** * Unique identifier for this slot within the semaphore. * Generated when a request acquires a slot. * Used to release or update the specific slot later. */ id: string; }; /** * Semaphore configuration persisted in the database. * Defines the constraint for the resource pool. * * A semaphore with limit=5 allows 5 concurrent requests. * Additional requests must wait for slots to become available (expire or be released). * * IMPORT_PATH: `"@daiso-tech/core/semaphore/contracts"` * @group Contracts */ export type ISemaphoreData = { /** * Maximum number of concurrent units/slots available. * Number of concurrent requests that can hold slots simultaneously. */ limit: number; }; /** * Transaction context for atomic semaphore operations. * Provides methods to read and modify semaphore state within a database transaction. * * All transaction methods must be executed together atomically. * The database guarantees isolation (race-condition free) for all operations. * * IMPORT_PATH: `"@daiso-tech/core/semaphore/contracts"` * @group Contracts */ export type IDatabaseSemaphoreTransaction = { /** * Retrieves semaphore configuration by key. * Returns the limit (max concurrent slots) or null if semaphore doesn't exist. * * @param context - Readable execution context for the operation * @param key - The semaphore identifier (e.g., "api-rate-limit", "db-connection-pool") * @returns Semaphore config with limit, or null if not found */ findSemaphore(context: IReadableContext, key: string): Promise<ISemaphoreData | null>; /** * Retrieves all active slots (acquired requests) for a semaphore. * Returns empty array if semaphore has no slots or doesn't exist. * Includes expired slots (not auto-cleaned until explicitly removed). * * @param context - Readable execution context for the operation * @param key - The semaphore identifier * @returns Array of slot data with ids and expirations */ findSlots(context: IReadableContext, key: string): Promise<Array<ISemaphoreSlotData>>; /** * Creates new semaphore or updates existing semaphore limit. * Idempotent: calling multiple times with same limit is safe. * * @param context - Readable execution context for the operation * @param key - The semaphore identifier * @param limit - Maximum number of concurrent slots * @returns Void (always succeeds) */ upsertSemaphore(context: IReadableContext, key: string, limit: number): Promise<void>; /** * Creates new slot or updates existing slot expiration. * Called when a request acquires a slot (with expiration time). * Idempotent: updating same slot's expiration is safe. * * @param context - Readable execution context for the operation * @param key - The semaphore identifier * @param slotId - Unique slot identifier (generated by consumer) * @param expiration - When slot expires and becomes available again, or null for no expiration * @returns Void (always succeeds) */ upsertSlot(context: IReadableContext, key: string, slotId: string, expiration: Date | null): Promise<void>; }; /** * Database adapter contract for managing semaphores in SQL/document databases. * Simplifies semaphore implementation using transactional CRUD patterns. * * Designed for persistent semaphores stored in databases like: * - SQL databases (PostgreSQL, MySQL) with TypeORM or MikroORM * - Document databases with transaction support * - Any backend with atomic transaction capability * * IMPORT_PATH: `"@daiso-tech/core/semaphore/contracts"` * @group Contracts */ export type IDatabaseSemaphoreAdapter = { /** * Executes a function within a database transaction. * Ensures all operations in the function are atomic and isolated. * * Implementations must use the strictest transaction isolation level available * (SERIALIZABLE for SQL, highest for document databases) to prevent race conditions. * * @param context - Readable execution context for the operation * @param fn - Async function receiving transaction object, returns value of any type * @returns The value returned by the function * @throws Error if transaction fails or is rolled back * * @template TValue - Return type of the transaction function */ transaction<TValue>(context: IReadableContext, fn: InvokableFn<[ transaction: IDatabaseSemaphoreTransaction ], Promise<TValue>>): Promise<TValue>; /** * Removes a specific slot, making that resource unit available again. * Returns the slot's expiration info. * Returns null if slot doesn't exist (idempotent - safe to call multiple times). * * @param context - Readable execution context for the operation * @param key - The semaphore identifier * @param slotId - The slot to remove * @returns Expiration info of the removed slot, or null if not found */ removeSlot(context: IReadableContext, key: string, slotId: string): Promise<ISemaphoreSlotExpirationData | null>; /** * Removes all slots from a semaphore. * Used for cleanup or reset operations. * Returns expiration info for all removed slots. * * @param context - Readable execution context for the operation * @param key - The semaphore identifier * @returns Array of expiration info for all removed slots */ removeAllSlots(context: IReadableContext, key: string): Promise<Array<ISemaphoreSlotExpirationData>>; /** * Updates a slot's expiration time if it's currently valid (not expired). * Useful for extending a held slot's lifetime (e.g., heartbeat/keep-alive). * * Conditions for success: * - Slot must exist * - Slot must not be expired (expiration time >= current time or null) * - New expiration must be in the future * * @param context - Readable execution context for the operation * @param key - The semaphore identifier * @param slotId - The slot to update * @param expiration - New expiration time (or null for persistent) * @returns Number of rows affected (1 if updated, 0 if slot not found or expired) */ updateExpiration(context: IReadableContext, key: string, slotId: string, expiration: Date): Promise<number>; };