npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

ryoiki

v1.1.0

Published

A range-based locking library for JavaScript, enabling concurrent read locks and exclusive write locks with seamless management of overlapping ranges.

Downloads

82

Readme

Ryoiki

Node.js workflow

Ryoiki is a JavaScript library that provides read/write locks based on specific ranges.
This library helps maintain consistency and stability by controlling concurrent access to data.

📦 Installation

npm install ryoiki

or

import { Ryoiki } from 'https://cdn.jsdelivr.net/npm/ryoiki@1/+esm'

🚀 Usage

Basic Example

import { Ryoiki } from 'ryoiki'

const ryoiki = new Ryoiki()

let lockId: string; // Store lock ID externally
await ryoiki.readLock([0, 10], async (_lockId) => {
  lockId = _lockId
  console.log('Reading from range [0, 10]')
}).finally(() => ryoiki.readUnlock(lockId)) // Always unlock

Key Concepts

1. Default Lock Range

  • If the first parameter of readLock or writeLock is omitted, it defaults to [-Infinity, Infinity].

  • Example:

    let lockId: string
    await ryoiki.readLock(async (_lockId) => {
      lockId = _lockId
      console.log('Read lock applied to the entire range')
    }).finally(() => ryoiki.readUnlock(lockId))

2. Lock Waiting Behavior

  • Read Lock:
    • Can execute immediately if overlapping with other read locks.
    • Waits if overlapping with a write lock.
  • Write Lock:
    • Waits if overlapping with other read or write locks.

3. Timeout Behavior

  • Both readLock and writeLock now support an optional timeout parameter.
  • Timeout: The lock request will wait for the specified time in milliseconds before throwing an error if the lock cannot be acquired.
    • Defaults to Infinity, meaning it will wait indefinitely unless otherwise specified.
    • If the lock cannot be acquired within the given timeout period, a timeout error is thrown.

4. Unlocking

  • Always use finally to release locks, even if an error occurs in the callback.

  • Correct Usage:

    let lockId: string
    await ryoiki.writeLock([0, 10], async (_lockId) => {
      lockId = _lockId
      throw new Error('Exception occurred')
    }).finally(() => ryoiki.writeUnlock(lockId)) // Always unlock

5. Locks, Deadlocks, and Caution

  • readLock and writeLock are used to manage access to data by locking specific ranges.

    • A read lock allows multiple readers but waits if a write lock exists.
    • A write lock prevents both read and write locks in the same range, ensuring exclusive access.
  • Deadlock occurs when two or more processes are unable to proceed because each is waiting for the other to release a lock.
    In the context of Ryoiki, this could happen if:

    • A readLock is waiting for a writeLock to release, and the writeLock is waiting for a readLock to release.

    To prevent deadlocks:

    • Ensure that locks are always released as soon as they are no longer needed.
    • Use timeout to avoid waiting indefinitely.
  • For a deeper understanding of these concepts, you can refer to these Wikipedia articles:

📖 API

readLock(range?: [number, number], callback: (lockId: string) => Promise<T>, timeout?: number): Promise<T>

  • Description: Requests a read lock for the specified range.
  • Parameters:
    • range: Range to lock as [start, end]. Defaults to [-Infinity, Infinity].
    • callback: Async function executed after lock is acquired.
      • lockId: Unique ID for the current lock.
    • timeout: Optional timeout in milliseconds. If the lock cannot be acquired within the specified time, the operation will throw a timeout error.
      • Defaults to undefined (wait indefinitely).
  • Returns: The result of the callback function.

writeLock(range?: [number, number], callback: (lockId: string) => Promise<T>, timeout?: number): Promise<T>

  • Description: Requests a write lock for the specified range.
  • Parameters:
    • range: Range to lock as [start, end]. Defaults to [-Infinity, Infinity].
    • callback: Async function executed after lock is acquired.
      • lockId: Unique ID for the current lock.
    • timeout: Optional timeout in milliseconds. If the lock cannot be acquired within the specified time, the operation will throw a timeout error.
      • Defaults to undefined (wait indefinitely).
  • Returns: The result of the callback function.

readUnlock(lockId: string): void

  • Description: Releases a read lock.

writeUnlock(lockId: string): void

  • Description: Releases a write lock.

range(start: number, length: number): [number, number]

  • Description: Creates a tuple [start, start + length] based on the given start and length.

  • Usage Example:

    const r = ryoiki.range(0, 10) // [0, 10]
    const s = ryoiki.range(5, 10) // [5, 15]

🌟 Examples

Read and Write Operations with Timeout

const ryoiki = new Ryoiki()

let lockId: string

// Read Lock with timeout
await ryoiki.readLock([0, 10], async (_lockId) => {
  lockId = _lockId
  console.log('Reading from range [0, 10]')
}, 1000).finally(() => ryoiki.readUnlock(lockId)) // Always unlock

// Write Lock with timeout
await ryoiki.writeLock([5, 15], async (_lockId) => {
  lockId = _lockId
  console.log('Writing to range [5, 15]')
}, 1000).finally(() => ryoiki.writeUnlock(lockId)) // Always unlock

📜 License

MIT License. Feel free to use and contribute! 🙌