Introducing Socket Firewall: Free, Proactive Protection for Your Software Supply Chain.Learn More
Socket
Book a DemoInstallSign in
Socket

@evermind-sh/sdk

Package Overview
Dependencies
Maintainers
0
Versions
19
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@evermind-sh/sdk

Evermind - Distributed Lock as a Service

latest
Source
npmnpm
Version
0.10.2
Version published
Maintainers
0
Created
Source

Evermind Logo

Evermind - Distributed Lock as a Service

Evermind Polar.sh Bluesky

Table of contents

1. About Evermind.sh

Evermind, translated from the Rohanese word Simbelmynë—the name of a small white flower symbolizing "everlasting memory"—is a simple, reliable, and scalable Distributed Lock-as-a-Service (LaaS).

Locks (sometimes referred to as a Mutex or a Semaphore) empowers developers to maintain consistency across distributed systems effortlessly by offering tools to acquire, extend, and release locks on shared resources.

Evermind simplifies complex coordination challenges, enabling robust, fault-tolerant applications that ensure atomic operations on resources across distributed systems.

2. Evermind Lock

Evermind eliminates the complexities of building and managing distributed locking mechanisms. Our service provides:

2.1. Features

  • 🔄 Configurable Lock Acquisitions: Fine-tune lock behavior with options for lease duration, retries, and delay intervals.
  • ⏱️ Automatic Expirations and Releases: Prevent deadlocks and resource starvation.
  • Lock Extensions: Extend a lock without releasing it.
  • ☁️ HTTP API & TypeScript SDK: Flexible integrations with multiple environments that is serverless ready.
  • 🤝 Soft Fail Option: Handle errors gracefully with inline responses.

2.2. Future Features

  • 📜 FIFO Locks: First In, First Out locks, allowing you to ensure that acquisition order is honoured.
  • 🛠️ Lock Acquisition Optimization: Tools to help you optimize your lock durations and retry delays to reduce Lock Acquisition Attempts (See Pricing below)

2.3. Pricing

Evermind is billed based on how much usage (i.e. how many locks you will be acquiring), based on Lock Acquisition Attempts (LAAs).

A Lock Acquisition Attempt (LAA) is where a call to the Evermind Lock API attempts to acquire a lock on a resource, if that resource can be acquired on the first attempt and then a subsequent call to the Evermind API releases it, that is 1 LAA.

If that resource is unable to be acquired on the first attempt and must retry N times then the total number of LAAs used would be M, where M is 1 + the number of retires.

Lock Acquisition Attempts approximately measure usage in the system, although notably Lock Releases and Lock extensions are not billed and do not count towards your Licence Key usage.

Check our the pricing plans and subscribe via our storefront.

2.3.1. Pay as you go Pricing

In the future Evermind will offer Pay as you go pricing, currently you must sign up for a subscription.

2.3.2. Enterprise Pricing

Need unlimited LAAs or a higher level of support/isolation? Reach out to hello@evermind.sh!

3. Setup: Acquiring an API Key

To interact with Evermind's lock API, you first need to exchange your Polar.sh license key for an API key. Licence Keys can be purchased from our Polar.sh storefront.

  • Visit the store: Polar.sh
  • Subscribe to a plan, each plan comes with 1x Licence Key as a Benefit that can be exchanged for 1 or more API Keys
  • Go to your benefits for your subscription and use the Licence Key to create an API key (below)

Licence Keys are automatically managed by Polar and will expire and have their usage allocations tracked within the Polar system, but you manage your own API Keys.

There is a 1:M relationship between Licence Keys and API Keys, all API Keys created for a Licence Key will contribute to that Licence Keys usage allocation.

3.1. Using the CLI

Install the CLI globally:

npm install -g evermind

Or, run the command using npx, bunx, etc.

Create an API key:

npx evermind keys create --licenceKey <licence-key>

Or let the Evermind CLI read the licence key from the EVERMIND_LICENCE_KEY environment variable:

export EVERMIND_LICENCE_KEY=YOUR_POLAR_SH_LICENCE_KEY

npx evermind keys create

Delete an API key:

npx evermind keys delete YOUR_API_KEY --licenceKey YOUR_POLAR_SH_LICENCE_KEY

Or use an environment variable:

export EVERMIND_LICENCE_KEY=YOUR_POLAR_SH_LICENCE_KEY

npx evermind keys delete YOUR_API_KEY

3.2. Direct HTTP API Calls

3.2.1 Create API Key

  • Endpoint: https://api.evermind.sh/api-key
  • Method: POST
  • Body:
    {
      "licenceKey": "YOUR_POLAR_SH_LICENCE_KEY"
    }
    
  • Response:
    {
      "apiKey": "YOUR_API_KEY"
    }
    

cURL Example:

curl -X POST https://api.evermind.sh/api-key \
-H "Content-Type: application/json" \
-d '{
  "licenceKey": "YOUR_POLAR_SH_LICENCE_KEY"
}'

3.2.2. Delete API Key

  • Endpoint: https://api.evermind.sh/api-key
  • Method: DELETE
  • Body:
    {
      "licenceKey": "YOUR_POLAR_SH_LICENCE_KEY",
      "apiKey": "YOUR_API_KEY"
    }
    
  • Response: 204 No Content (if successful)

cURL Example:

curl -X DELETE https://api.evermind.sh/api-key \
-H "Content-Type: application/json" \
-d '{
  "licenceKey": "YOUR_POLAR_SH_LICENCE_KEY",
  "apiKey": "YOUR_API_KEY"
}'

4. Using Evermind

Once you have an API key, you can interact with the lock service to acquire, extend, and release locks. The following sections describe how to use the system directly via HTTP methods and with the TypeScript SDK.

4.1. TypeScript SDK Usage

Install the SDK:

npm

npm install @evermind/sdk

Example Usage:

import { Evermind } from 'evermind';

const evermind = new Evermind({ apiKey: 'YOUR_API_KEY' });

const key = 'my_resource';

// Acquire a lock directly
const acquireResponse = await evermind.acquire({ key, lease: 5000 });
console.log('Acquire:', acquireResponse);

// Extend a lock that has been acquired
if (acquireResponse.acquired) {
	const extendResponse = await evermind.extend({ key, uuid: acquireResponse.config.uuid, extendBy: 2000 });
	console.log('Extend:', extendResponse);

	// Release the lock
	const releaseResponse = await evermind.release({ key, uuid: acquireResponse.config.uuid, softFail: true });
	console.log('Release:', releaseResponse);
}

// Using `withLock` to simplify lock management, will automatically release the lock once the routine is finished.
const result = await evermind.withLock({ key }, async () => {
	// Do some work while holding the lock
	return 'some result';
});

4.2. HTTP API Examples

4.2.1. Acquire Lock

  • Endpoint: https://lock.evermind.sh/lock/acquire
  • Method: POST
  • Body:
    {
      "key": "<your resource key>",
      "lease": 5000,
      "retryAttempts": 5,
      "retryDelay": 500
    }
    
  • Response:
    {
      "acquired": true,
      "lockAcquisitionAttempts": 1,
      "config": {
        "key": "<your resource key>",
        "lease": 5000,
        "retryAttempts": 5,
        "retryDelay": 500,
        "uuid": "<UUIDv7>"
      },
      "message": null
    }
    

cURL Example:

curl -X POST https://lock.evermind.sh/lock/acquire \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
  "key": "<your resource key>",
  "lease": 5000,
  "retryAttempts": 5,
  "retryDelay": 500
}'

4.2.2. Extend Lock

  • Endpoint: https://lock.evermind.sh/lock/extend
  • Method: POST
  • Body:
    {
      "key": "<your resource key>",
      "uuid": "<UUIDv7>",
      "extendBy": 3000,
      "softFail": true
    }
    
  • Response:
    {
      "extended": true,
      "message": null
    }
    

cURL Example:

curl -X POST https://lock.evermind.sh/lock/extend \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
  "key": "<your resource key>",
  "extendBy": 3000,
  "uuid": "<UUIDv7>"
}'

4.2.3. Release Lock

  • Endpoint: https://lock.evermind.sh/lock/release
  • Method: POST
  • Body:
    {
      "key": "<your resource key>",
      "uuid": "<UUIDv7>",
      "softFail": true
    }
    
  • Response:
    {
      "released": true,
      "message": null
    }
    

cURL Example:

curl -X POST https://lock.evermind.sh/lock/release \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
  "key": "<your resource key>",
  "uuid": "<UUIDv7>"
}'

Here’s the expanded DTOs section with endpoint details, Evermind SDK method names, descriptions, caveats, and additional explanations.

5. Lock API

5.1. Lock Operations

The Lock API is used for acquiring, extending, and releasing locks. Below are the DTOs for each operation, along with their corresponding HTTP endpoints and Evermind SDK method names.

The resulting Resource Key (the globally unique key to acquire the lock on) is in the following format.

<GlobalPrefix>:<IdentityId>:<Key>
  • GlobalPrefix: Will always be Evermind unless you have your own deployment or isolated environment of the Evermind system. This key will be the same across all locks in the system.
  • IdentityId: A unique ID that corresponds to the account that the API key belongs to, if purchased via the Polar.sh storefront this will be your userId.
  • Key: The key you pass in for the resource, this can be anything you want it to be.

The only parts you have control over are the Key, the other parts of the lock key are managed by the system itself and are there to prevent locks from clashing across users of Evermind.

5.1.1. Acquire Lock

FieldTypeRequiredDescription
keystringYesThe resource to acquire a lock on.
leasenumberNoLock duration in milliseconds. Default: 10000.
retryAttemptsnumberNoNumber of retry attempts. Default: 10.
retryDelaynumberNoMilliseconds between retries. Default: 500.
retryJitternumberNoAn amount of time in milliseconds to randomly add to the retry delay to help in times of high lock contention. See: https://www.awsarchitectureblog.com/2015/03/backoff.html. Default: 0.
uuidstringNoOptional custom UUID for the lock. If not provided a UUIDv7 is used by default. It is recommended you use the default.
softFailbooleanNoReturn errors inline in an HTTP 200 instead of throwing errors.
fifobooleanNoWhether this lock should be acquired in FIFO or FCFS order.
  • Endpoint: POST https://lock.evermind.sh/lock/acquire
  • SDK Method: evermind.acquire(options: AcquireOptions)

Description: Acquires a lock on a given resource (key). By default, if the lock cannot be acquired immediately, the system will retry the specified number of times (retryAttempts) with the provided delay (retryDelay).

Caveats:

  • Locks are tied to a unique uuid. If a custom UUID is not provided, the system generates one.
  • Using softFail: true ensures errors are returned inline instead of throwing exceptions. This can simplify error handling in some scenarios.
  • When multiple processes are trying to acquire a lock on the same resource key ensure you are using the right fifo value otherwise locks may get into an unrecoverable state since some will be trying to acquire in FIFO order and some in FCFS order.

5.1.2. Extend Lock

FieldTypeRequiredDescription
keystringYesThe resource key to extend the lock on.
uuidstringYesUUID (or custom value) of the lock acquisition to extend.
extendBynumberYesMilliseconds to extend the lock.
softFailbooleanNoReturn errors inline in an HTTP 200 instead of throwing errors.
  • Endpoint: POST https://lock.evermind.sh/lock/extend
  • SDK Method: evermind.extend(options: ExtendOptions)

Description: Extends the duration of an existing lock. The uuid must match the UUID of the currently held lock, and the extendBy value specifies how much additional time (in milliseconds) to add to the lock duration.

Caveats:

  • Attempting to extend a non-existent or expired lock will fail unless softFail is set to true.
  • The extension is relative to the current lock's expiration time and not to the time of the request.

5.1.3. Release Lock

FieldTypeRequiredDescription
keystringYesThe resource key to release the lock on.
uuidstringYesUUID (or custom value) of the lock acquisition to release.
softFailbooleanNoReturn errors inline in an HTTP 200 instead of throwing errors.
  • Endpoint: POST https://lock.evermind.sh/lock/release
  • SDK Method: evermind.release(options: ReleaseOptions)

Description: Releases a lock on a resource. The uuid must match the UUID used to acquire the lock. If the lock is already released or does not exist, the response will indicate this unless softFail is set to true.

Caveats:

  • Ensure that the uuid provided matches the one used during acquisition; otherwise, the release will fail.
  • Using softFail: true can help avoid exceptions when the lock does not exist or has already been released.

5.2. Summary of SDK Methods and HTTP Endpoints

OperationHTTP EndpointSDK MethodDescription
Acquire LockPOST /lock/acquireevermind.acquireAttempts to acquire a lock on the given resource.
Extend LockPOST /lock/extendevermind.extendExtends the duration of a held lock.
Release LockPOST /lock/releaseevermind.releaseReleases the lock on the specified resource.

5.3. Additional Notes

  • Error Handling: By default, errors are returned as HTTP status codes. Setting softFail: true in any request results in errors being returned inline within a 200 OK response.
  • UUID Management: For optimal operation, UUIDs are used as a lock value to uniquely identify a lock acquisition. If you generate lock values manually, ensure they are truly unique to prevent collisions.
  • Retry Logic: Retry attempts and delays are configurable for acquisitions. This is particularly useful in scenarios with high contention for a resource.

6. Discussion & Alternatives

Evermind offers a managed DLaaS solution. However, various alternatives exist for concurrency control. Here's a comparison to help you choose the right tool:

6.1. Distributed Locks vs. Local Mutexes

  • Local Mutexes: Efficient for single-process applications. Libraries like sync.Mutex (Go), asyncio.Lock (Python), and @synchronized (Objective-C/Swift) operate within a single process's memory.
  • Distributed Locks: Necessary for coordinating shared resources across multiple processes or machines. This is where Evermind excels.

6.2. Alternatives to Evermind

  • Redlock Algorithm (e.g., npm package redlock): Fault-tolerant distributed locking using multiple Redis instances. However, managing Redis infrastructure introduces additional complexity.
  • Database-based Locking: Simple to implement but may cause performance bottlenecks in high-contention scenarios.
  • Distributed Consensus Systems (e.g., etcd, ZooKeeper): Offer robust locking with higher operational overhead.

6.3. When to Use Evermind

  • Simplifying distributed locking without managing infrastructure.
  • Seamless integration with serverless environments.
  • Rapid development with minimal setup.

6.4. When Not to Use Evermind

  • Single-process applications (use local mutexes instead).
  • Applications already using robust Redis infrastructure and comfortable with Redlock.
  • Extremely low-latency scenarios that require finely-tuned, in-house solutions.

7. Regions and Deployment

Evermind.sh is hosted on Fly.io and has servers in the following regions:

  • yyz - Toronto, Canada
  • yul - Montreal, Canada
  • sea - Seattle, Washington (US)

If you are wanting to use Evermind in a region closer to where your servers are going to be, feel free to open a pull request here with your new region request. You can find the available regions here.

You can choose to send your traffic to a specific region using this method. Only the regions above are supported.

If you are looking for your own deployment on infrastructure isolated from the rest of the Evermind platform or are wanting to run all services (Lock API, Database and Cache) in the same region as the server, feel free to reach out to hello@evermind.sh.

FAQs

Package last updated on 07 Dec 2024

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts