Skip to content

Commit a9da152

Browse files
committed
Bump version to v3.0.7. README improvements: Adding a Table of Contents for easier navigation, and a clarification regarding the O(1) complexity of the getter methods
1 parent 5f388ad commit a9da152

File tree

3 files changed

+42
-23
lines changed

3 files changed

+42
-23
lines changed

README.md

Lines changed: 39 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,24 @@ The design addresses the two primary semaphore use cases in Node.js:
1111

1212
Each use case necessitates distinct handling capabilities, which will be discussed separately with accompanying examples.
1313

14-
## Key Features :sparkles:
14+
## Table of Contents
15+
16+
* [Key Features](#key-features)
17+
* [Modern API Design](#modern-api-design)
18+
* [API](#api)
19+
* [Getter Methods](#getter-methods)
20+
* [1st use-case: Multiple Jobs Execution](#first-use-case)
21+
* [2nd use-case: Single Job Execution](#second-use-case)
22+
* [Graceful Termination](#graceful-termination)
23+
* [Error Handling for Background Jobs](#error-handling)
24+
* [Unavoidable / Implicit Backpressure](#unavoidable-backpressure)
25+
* [Promise Semaphores Are Not Promise Pools](#not-promise-pool)
26+
* [Breaking Change in Version 3.0.0](#breaking-change-3)
27+
* [Breaking Change in Version 2.0.0](#breaking-change-2)
28+
* [Naming Convention](#naming-convention)
29+
* [License](#license)
30+
31+
## Key Features :sparkles:<a id="key-features"></a>
1532

1633
- __Backpressure Control__: Ideal for job workers and background services. Concurrency control alone isn't sufficient to ensure stability and performance if backpressure control is overlooked. Without backpressure control, the heap can become overloaded, resulting in space complexity of O(*semaphore-slots* + *pending-jobs*) instead of O(*semaphore-slots*).
1734
- __Graceful Termination__: Await the completion of all currently executing jobs via the `waitForAllExecutingJobsToComplete` method.
@@ -25,15 +42,15 @@ Each use case necessitates distinct handling capabilities, which will be discuss
2542
- ES2020 Compatibility: The `tsconfig` target is set to ES2020, ensuring compatibility with ES2020 environments.
2643
- TypeScript support.
2744

28-
## Modern API Design :rocket:
45+
## Modern API Design :rocket:<a id="modern-api-design"></a>
2946

3047
Traditional semaphore APIs require explicit *acquire* and *release* steps, adding overhead and responsibility for the user. Additionally, they introduce the risk of deadlocking the application if one forgets to *release*, for example, due to a thrown exception.
3148

3249
In contrast, `ZeroBackpressureSemaphore` manages job execution, abstracting away these details and reducing user responsibility. The *acquire* and *release* steps are handled implicitly by the execution methods, reminiscent of the RAII idiom in C++.
3350

3451
Method names are chosen to clearly convey their functionality.
3552

36-
## API :globe_with_meridians:
53+
## API :globe_with_meridians:<a id="api"></a>
3754

3855
The `ZeroBackpressureSemaphore` class provides the following methods:
3956

@@ -45,7 +62,7 @@ The `ZeroBackpressureSemaphore` class provides the following methods:
4562

4663
If needed, refer to the code documentation for a more comprehensive description of each method.
4764

48-
## Getter Methods :mag:
65+
## Getter Methods :mag:<a id="getter-methods"></a>
4966

5067
The `ZeroBackpressureSemaphore` class provides the following getter methods to reflect the current semaphore's state:
5168

@@ -54,7 +71,9 @@ The `ZeroBackpressureSemaphore` class provides the following getter methods to r
5471
* __amountOfCurrentlyExecutingJobs__: The number of jobs currently being executed by the semaphore.
5572
* __amountOfUncaughtErrors__: The number of uncaught errors from background jobs triggered by `startExecution`, that are currently stored by the instance. These errors have not yet been extracted using `extractUncaughtErrors`.
5673

57-
## 1st use-case: Multiple Jobs Execution :man_technologist:
74+
To eliminate any ambiguity, all getter methods have **O(1)** time and space complexity, meaning they do **not** iterate through all currently executing jobs with each call. The metrics are maintained by the jobs themselves.
75+
76+
## 1st use-case: Multiple Jobs Execution :man_technologist:<a id="first-use-case"></a>
5877

5978
This semaphore variant excels in eliminating backpressure when dispatching multiple concurrent jobs from the same caller. This pattern is typically observed in **background job services**, such as:
6079
- Log File analysis.
@@ -260,7 +279,7 @@ On the other hand, given that the timeout is 30 seconds and a typical job durati
260279

261280
As a general rule, `waitForAvailability` is advisable whenever a timeout mechanism is involved, and the timeout period begins **before** the job starts execution. Note that the same effect can be achieved with `startExecution` alone, if the timeout-triggering logic is included in the job itself (such as, consuming a message). Both approaches are valid.
262281

263-
## 2nd use-case: Single Job Execution :man_technologist:
282+
## 2nd use-case: Single Job Execution :man_technologist:<a id="second-use-case"></a>
264283

265284
The `waitForCompletion` method is useful for executing a sub-procedure, for which the caller must wait before proceeding with its work.
266285

@@ -295,15 +314,15 @@ app.get('/user/', async (req, res) => {
295314
});
296315
```
297316

298-
## Graceful Termination :hourglass:
317+
## Graceful Termination :hourglass:<a id="graceful-termination"></a>
299318

300319
The `waitForAllExecutingJobsToComplete` method is essential for scenarios where it is necessary to wait for all ongoing jobs to finish, such as logging a success message or executing subsequent logic.
301320

302321
A key use case for this method is ensuring stable unit tests. Each test should start with a clean state, independent of others, to avoid interference. This prevents scenarios where a job from Test A inadvertently continues to execute during Test B.
303322

304323
If your component has a termination method (`stop`, `terminate`, or similar), keep that in mind.
305324

306-
## Error Handling for Background Jobs :warning:
325+
## Error Handling for Background Jobs :warning:<a id="error-handling"></a>
307326

308327
Background jobs triggered by `startExecution` may throw errors. Unlike the `waitForCompletion` case, the caller has no reference to the corresponding job promise which executes in the background.
309328

@@ -320,34 +339,34 @@ However, there are a few exceptional cases where the user can safely avoid extra
320339
- The number of jobs is relatively small and the process is short-lived.
321340
- The jobs never throw errors, thus no uncaught errors are possible.
322341

323-
## Unavoidable / Implicit Backpressure
342+
## Unavoidable / Implicit Backpressure<a id="unavoidable-backpressure"></a>
324343

325344
Mitigating backpressure is primarily associated with the `startExecution` method, particularly in scenarios involving multiple jobs. However, the single-job use case may certainly inflict backpressure on the Node.js micro-tasks queue.
326345

327346
For instance, consider a situation where 1K concurrently executing route handlers are each awaiting the completion of their own `waitForCompletion` execution, while the semaphore is unavailable. In such cases, all handlers will internally wait on the semaphore's `_availableSlotExists` private property, competing to acquire the semaphore once it becomes available.
328347

329-
## Promise Semaphores Are Not Promise Pools
348+
## Promise Semaphores Are Not Promise Pools<a id="not-promise-pool"></a>
330349

331350
The term "promise pool" is commonly used in the JavaScript community to describe promise semaphores.
332351
However, this terminology can be misleading. The term "pool" typically implies the **reuse of resources**, as in "thread pools" or "connection pools," where a fixed set of resources is used and **recycled**. In contrast, a promise semaphore’s primary goal is to **control concurrency** by limiting the number of jobs executing concurrently, with each job represented by a **distinct promise instance**.
333352

334353
Using the term "promise pool" may cause confusion, as it suggests resource reuse rather than concurrency management.
335354

336-
## Naming Convention :memo:
337-
338-
To improve readability and maintainability, it is highly recommended to assign a use-case-specific name to your semaphore instances. This practice helps in clearly identifying the purpose of each semaphore in the codebase. Examples include:
339-
- dbAccessSemaphore
340-
- tokenGenerationSemaphore
341-
- azureStorageSemaphore
342-
343-
## Breaking Change in Version 3.0.0 :boom:
355+
## Breaking Change in Version 3.0.0 :boom:<a id="breaking-change-3"></a>
344356

345357
In version 3.0.0, the target compatibility has been upgraded from ES6 to ES2020. This change was made to leverage the widespread adoption of ES2020, its native support for async/await, and the use of `Promise.allSettled` within the semaphore.
346358

347-
## Breaking Change in Version 2.0.0 :boom:
359+
## Breaking Change in Version 2.0.0 :boom:<a id="breaking-change-2"></a>
348360

349361
The only breaking change in this release is the renaming of the method `waitTillAllExecutingJobsAreSettled` to `waitForAllExecutingJobsToComplete` for improved readability. No other changes have been introduced.
350362

351-
## License :scroll:
363+
## Naming Convention :memo:<a id="naming-convention"></a>
364+
365+
To improve readability and maintainability, it is highly recommended to assign a use-case-specific name to your semaphore instances. This practice helps in clearly identifying the purpose of each semaphore in the codebase. Examples include:
366+
- dbAccessSemaphore
367+
- tokenGenerationSemaphore
368+
- azureStorageSemaphore
369+
370+
## License :scroll:<a id="license"></a>
352371

353372
[MIT](LICENSE)

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "zero-backpressure-semaphore-typescript",
3-
"version": "3.0.6",
3+
"version": "3.0.7",
44
"description": "A modern Promise-semaphore for Node.js projects, enabling users to limit the number of concurrently executing promises. Offering backpressure control for enhanced efficiency, utilizing a communicative API that signals availability, promoting a just-in-time approach. Additionally, it incorporates mechanisms for graceful termination and error handling, making it suitable for complex scenarios.",
55
"repository": {
66
"type": "git",

0 commit comments

Comments
 (0)