Skip to main content

Semaphore Plugins

withSemaphorePrefix plugin​

The Semaphore prefix plugin intercepts calls to a semaphore adapter and transparently prefixes all semaphore keys with a configurable string. This enables logical key namespacing without modifying the adapter implementation.

Use cases​

  • Multi-tenant semaphores — Prefix semaphore keys with a tenant identifier to isolate concurrency limits between tenants
  • Resource scoping — Organize semaphores by resource type to avoid key collisions
  • Environment isolation — Separate development, staging, and production semaphore state
  • Pool differentiation — Prefix keys with a pool name to manage independent semaphore pools

How it works​

The withSemaphorePrefix function returns a PluginFn that calls enhance on each adapter method that accepts a semaphore key. When an enhanced method is invoked, the plugin intercepts the call, prepends the configured prefix to the key argument, and forwards the modified arguments to the original method.

The plugin prefixes keys for the following methods:

MethodKey argumentPattern
acquirekey within settings objectprefix + key
forceReleaseAllFirst argument (key)prefix + key
getStateFirst argument (key)prefix + key
refreshFirst argument (key)prefix + key
releaseFirst argument (key)prefix + key

Usage​

./samples/with-semaphore-prefix.ts
import { withPlugin } from "eridu-tech/middleware";
import { MemorySemaphoreAdapter } from "eridu-tech/semaphore/memory-semaphore-adapter";
import { withSemaphorePrefix } from "eridu-tech/semaphore/plugins";

const adapter = new MemorySemaphoreAdapter();

// Apply the prefix plugin to the adapter
const prefixedAdapter = withPlugin(adapter, withSemaphorePrefix("pool-1:"));

Before/after behavior​

Before — Semaphore keys are used as-is:

./samples/unprefixed-acquire.ts
import { MemorySemaphoreAdapter } from "eridu-tech/semaphore/memory-semaphore-adapter";

const adapter = new MemorySemaphoreAdapter();

await adapter.acquire({
key: "connections",
slotId: "slot-1",
limit: 1,
ttl: null,
});
// -> acquires slot on "connections"

After — Semaphore keys are automatically prefixed:

./samples/prefixed-acquire.ts
import { withPlugin } from "eridu-tech/middleware";
import { MemorySemaphoreAdapter } from "eridu-tech/semaphore/memory-semaphore-adapter";
import { withSemaphorePrefix } from "eridu-tech/semaphore/plugins";

const adapter = new MemorySemaphoreAdapter();

// Apply the prefix plugin to the adapter
const prefixedAdapter = withPlugin(adapter, withSemaphorePrefix("pool-1:"));

await prefixedAdapter.acquire({
key: "connections",
slotId: "slot-1",
limit: 1,
ttl: null,
});
// -> acquires slot on "pool-1:connections"
danger

Because withPlugin uses enhance under the hood, the same edge case applies: if one enhanced method internally calls another enhanced method via this, the middleware will apply twice. Be mindful of inter-method calls when applying plugins that enhance multiple methods on the same instance.

info

For more information about the withPlugin function and applying plugins to adapters, see the Middleware plugin documentation.