Configuring Semaphore adapters
MemorySemaphoreAdapter
To use the MemorySemaphoreAdapter you only need to create instance of it:
import { MemorySemaphoreAdapter } from "eridu-tech/semaphore/memory-semaphore-adapter";
export const memorySemaphoreAdapter = new MemorySemaphoreAdapter();
You can also provide an Map that will be used for storing the data in memory:
import { MemorySemaphoreAdapter } from "eridu-tech/semaphore/memory-semaphore-adapter";
const map = new Map<any, any>();
const memorySemaphoreAdapter = new MemorySemaphoreAdapter(map);
MemorySemaphoreAdapter lets you test your app without external dependencies like Redis, ideal for local development, unit tests, integration tests and fast E2E test for the backend application.
Note the MemorySemaphoreAdapter is limited to single process usage and cannot be shared across multiple servers or processes.
Settings
To clean up expired semaphore keys, call removeAllExpired at a regular interval (for example, using a cron job):
import { MemorySemaphoreAdapter } from "eridu-tech/semaphore/memory-semaphore-adapter";
const memorySemaphoreAdapter = new MemorySemaphoreAdapter();
// Remove all expired semaphore keys manually.
await memorySemaphoreAdapter.removeAllExpired();
To remove the semaphore map and all stored semaphore data, use deInit method:
import { memorySemaphoreAdapter } from "./memory-semaphore-adapter.js";
await memorySemaphoreAdapter.deInit();
MongodbSemaphoreAdapter
To use the MongodbSemaphoreAdapter, you'll need to:
- Install the required dependency:
mongodbpackage.
Setup
Connect to MongoDB:
import { MongoClient } from "mongodb";
const client = await MongoClient.connect("YOUR_MONGODB_CONNECTION_STRING");
export const database = client.db("database");
The database setting also accepts a TransactionContext, which makes the adapter transaction aware. Adapters given the same instance share the same transaction when available.
Usage
Create the adapter:
import { MongodbSemaphoreAdapter } from "eridu-tech/semaphore/mongodb-semaphore-adapter";
import { database } from "./mongodb-semaphore-adapter-setup.js";
export const mongodbSemaphoreAdapter = new MongodbSemaphoreAdapter({
database,
});
// You need initialize the adapter once before using it.
// During the initialization the indexes will be created
await mongodbSemaphoreAdapter.init();
You can change the collection name:
import { MongodbSemaphoreAdapter } from "eridu-tech/semaphore/mongodb-semaphore-adapter";
import { database } from "./mongodb-semaphore-adapter-setup.js";
const mongodbSemaphoreAdapter = new MongodbSemaphoreAdapter({
database,
// By default "semaphore" is used as collection name
collectionName: "my-semaphore",
});
await mongodbSemaphoreAdapter.init();
You can change the collection settings:
import { MongodbSemaphoreAdapter } from "eridu-tech/semaphore/mongodb-semaphore-adapter";
import { database } from "./mongodb-semaphore-adapter-setup.js";
const mongodbSemaphoreAdapter = new MongodbSemaphoreAdapter({
database,
// You configure additional collection settings
collectionSettings: {},
});
await mongodbSemaphoreAdapter.init();
To remove the semaphore collection and all stored semaphore data, use deInit method:
import { mongodbSemaphoreAdapter } from "./mongodb-semaphore-adapter.js";
await mongodbSemaphoreAdapter.deInit();
Note in order to use MongodbSemaphoreAdapter correctly, ensure you use a single, consistent database across all server instances or processes.
RedisSemaphoreAdapter
To use the RedisSemaphoreAdapter, you'll need to:
- Install the required dependency:
ioredispackage.
Setup
Connect to Redis:
import { Redis } from "ioredis";
export const database = new Redis("YOUR_REDIS_CONNECTION_STRING");
Usage
Create the adapter:
import { RedisSemaphoreAdapter } from "eridu-tech/semaphore/redis-semaphore-adapter";
import { database } from "./redis-semaphore-adapter-setup.js";
const redisSemaphoreAdapter = new RedisSemaphoreAdapter(database);
Note in order to use RedisSemaphoreAdapter correctly, ensure you use a single, consistent database across all server instances or processes.
KyselySemaphoreAdapter
To use the KyselySemaphoreAdapter, you'll need to:
- Use database provider that has support for transactions.
- Install the required dependency:
kyselypackage.
Setup
Create a function that creates the TransactionContext by wrapping a Kysely instance in a KyselyTransactionAdapter:
import { ExecutionContext } from "eridu-tech/execution-context";
import { AlsExecutionContextAdapter } from "eridu-tech/execution-context/als-execution-context-adapter";
import { contextToken } from "eridu-tech/execution-context/contracts";
import { TransactionContext } from "eridu-tech/transaction-context";
import { KyselyTransactionAdapter } from "eridu-tech/transaction-context/kysely-transaction-adapter";
import type { ITransactionContext } from "eridu-tech/transaction-context/contracts";
import type { Kysely } from "kysely";
const executionContext = new ExecutionContext(new AlsExecutionContextAdapter());
// `KyselySemaphoreAdapter` is transaction aware: it runs every semaphore
// operation through the `current` client of this context, which is the
// transaction-scoped client while a transaction is active and the base client
// otherwise.
export function createTransactionContext(
kysely: Kysely<any>,
): ITransactionContext<Kysely<any>> {
return new TransactionContext<Kysely<any>>({
token: contextToken("kysely"),
adapter: new KyselyTransactionAdapter({
database: kysely,
}),
executionContext,
});
}
The transactionContext setting makes the adapter transaction aware. Adapters given the same instance share the same transaction when available.
With Sqlite
You will need to install better-sqlite3 package:
import { KyselySemaphoreAdapter } from "eridu-tech/semaphore/kysely-semaphore-adapter";
import Sqlite from "better-sqlite3";
import { Kysely, SqliteDialect } from "kysely";
import { createTransactionContext } from "./kysely-semaphore-adapter-setup.js";
const database = new Sqlite("DATABASE_NAME.db");
const kysely = new Kysely<any>({
dialect: new SqliteDialect({
database,
}),
});
const transactionContext = createTransactionContext(kysely);
export const kyselySemaphoreAdapter = new KyselySemaphoreAdapter({
transactionContext,
});
// You need initialize the adapter once before using it.
// During the initialization the schema will be created
await kyselySemaphoreAdapter.init();
Note using KyselySemaphoreAdapter with sqlite is limited to single server usage and cannot be shared across multiple servers but it can be shared between different processes. To use it correctly, ensure all process instances access the same persisted database.
With Postgres
You will need to install pg package:
import { KyselySemaphoreAdapter } from "eridu-tech/semaphore/kysely-semaphore-adapter";
import { Pool } from "pg";
import { Kysely, PostgresDialect } from "kysely";
import { createTransactionContext } from "./kysely-semaphore-adapter-setup.js";
const database = new Pool({
database: "DATABASE_NAME",
host: "DATABASE_HOST",
user: "DATABASE_USER",
// DATABASE port
port: 5432,
password: "DATABASE_PASSWORD",
max: 10,
});
const kysely = new Kysely<any>({
dialect: new PostgresDialect({
pool: database,
}),
});
const transactionContext = createTransactionContext(kysely);
const kyselySemaphoreAdapter = new KyselySemaphoreAdapter({
transactionContext,
});
// You need initialize the adapter once before using it.
// During the initialization the schema will be created
await kyselySemaphoreAdapter.init();
Note in order to use KyselySemaphoreAdapter with postgres correctly, ensure you use a single, consistent database across all server instances. This means you can't use replication.
With Mysql
You will need to install mysql2 package:
import { KyselySemaphoreAdapter } from "eridu-tech/semaphore/kysely-semaphore-adapter";
import { createPool } from "mysql2";
import { Kysely, MysqlDialect } from "kysely";
import { createTransactionContext } from "./kysely-semaphore-adapter-setup.js";
const database = createPool({
host: "DATABASE_HOST",
// Database port
port: 3306,
database: "DATABASE_NAME",
user: "DATABASE_USER",
password: "DATABASE_PASSWORD",
connectionLimit: 10,
});
const kysely = new Kysely<any>({
dialect: new MysqlDialect({
pool: database,
}),
});
const transactionContext = createTransactionContext(kysely);
const kyselySemaphoreAdapter = new KyselySemaphoreAdapter({
transactionContext,
});
// You need initialize the adapter once before using it.
// During the initialization the schema will be created
await kyselySemaphoreAdapter.init();
Note in order to use KyselySemaphoreAdapter with mysql correctly, ensure you use a single, consistent database across all server instances. This means you can't use replication.
With Libsql
You will need to install @libsql/kysely-libsql package:
import { KyselySemaphoreAdapter } from "eridu-tech/semaphore/kysely-semaphore-adapter";
import { LibsqlDialect } from "@libsql/kysely-libsql";
import { Kysely } from "kysely";
import { createTransactionContext } from "./kysely-semaphore-adapter-setup.js";
const kysely = new Kysely<any>({
dialect: new LibsqlDialect({
url: "DATABASE_URL",
}),
});
const transactionContext = createTransactionContext(kysely);
const kyselySemaphoreAdapter = new KyselySemaphoreAdapter({
transactionContext,
});
// You need initialize the adapter once before using it.
// During the initialization the schema will be created
await kyselySemaphoreAdapter.init();
Note in order to use KyselySemaphoreAdapter with libsql correctly, ensure you use a single, consistent database across all server instances. This means you can't use libsql embedded replicas.
Settings
To clean up expired semaphore keys, call removeAllExpired at a regular interval (for example, using a cron job):
import { kyselySemaphoreAdapter } from "./kysely-semaphore-sqlite.js";
// Remove all expired semaphore keys manually.
await kyselySemaphoreAdapter.removeAllExpired();
To remove the semaphore table and all stored semaphore data, use deInit method:
import { kyselySemaphoreAdapter } from "./kysely-semaphore-sqlite.js";
await kyselySemaphoreAdapter.deInit();
NoOpSemaphoreAdapter
The NoOpSemaphoreAdapter is a no-operation implementation, it performs no actions when called:
import { NoOpSemaphoreAdapter } from "eridu-tech/semaphore/no-op-semaphore-adapter";
const noOpSemaphoreAdapter = new NoOpSemaphoreAdapter();
The NoOpSemaphoreAdapter is useful when you want to mock out or disable your SemaphoreFactory instance.
Further information
For further information refer to eridu-tech/semaphore API docs.