You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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
*[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:<aid="key-features"></a>
15
32
16
33
-__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*).
17
34
-__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
25
42
- ES2020 Compatibility: The `tsconfig` target is set to ES2020, ensuring compatibility with ES2020 environments.
26
43
- TypeScript support.
27
44
28
-
## Modern API Design :rocket:
45
+
## Modern API Design :rocket:<aid="modern-api-design"></a>
29
46
30
47
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.
31
48
32
49
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++.
33
50
34
51
Method names are chosen to clearly convey their functionality.
35
52
36
-
## API :globe_with_meridians:
53
+
## API :globe_with_meridians:<aid="api"></a>
37
54
38
55
The `ZeroBackpressureSemaphore` class provides the following methods:
39
56
@@ -45,7 +62,7 @@ The `ZeroBackpressureSemaphore` class provides the following methods:
45
62
46
63
If needed, refer to the code documentation for a more comprehensive description of each method.
47
64
48
-
## Getter Methods :mag:
65
+
## Getter Methods :mag:<aid="getter-methods"></a>
49
66
50
67
The `ZeroBackpressureSemaphore` class provides the following getter methods to reflect the current semaphore's state:
51
68
@@ -54,7 +71,9 @@ The `ZeroBackpressureSemaphore` class provides the following getter methods to r
54
71
*__amountOfCurrentlyExecutingJobs__: The number of jobs currently being executed by the semaphore.
55
72
*__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`.
56
73
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:<aid="first-use-case"></a>
58
77
59
78
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:
60
79
- Log File analysis.
@@ -260,7 +279,7 @@ On the other hand, given that the timeout is 30 seconds and a typical job durati
260
279
261
280
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.
262
281
263
-
## 2nd use-case: Single Job Execution :man_technologist:
282
+
## 2nd use-case: Single Job Execution :man_technologist:<aid="second-use-case"></a>
264
283
265
284
The `waitForCompletion` method is useful for executing a sub-procedure, for which the caller must wait before proceeding with its work.
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.
301
320
302
321
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.
303
322
304
323
If your component has a termination method (`stop`, `terminate`, or similar), keep that in mind.
305
324
306
-
## Error Handling for Background Jobs :warning:
325
+
## Error Handling for Background Jobs :warning:<aid="error-handling"></a>
307
326
308
327
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.
309
328
@@ -320,34 +339,34 @@ However, there are a few exceptional cases where the user can safely avoid extra
320
339
- The number of jobs is relatively small and the process is short-lived.
321
340
- The jobs never throw errors, thus no uncaught errors are possible.
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.
326
345
327
346
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.
328
347
329
-
## Promise Semaphores Are Not Promise Pools
348
+
## Promise Semaphores Are Not Promise Pools<aid="not-promise-pool"></a>
330
349
331
350
The term "promise pool" is commonly used in the JavaScript community to describe promise semaphores.
332
351
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**.
333
352
334
353
Using the term "promise pool" may cause confusion, as it suggests resource reuse rather than concurrency management.
335
354
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:<aid="breaking-change-3"></a>
344
356
345
357
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.
346
358
347
-
## Breaking Change in Version 2.0.0 :boom:
359
+
## Breaking Change in Version 2.0.0 :boom:<aid="breaking-change-2"></a>
348
360
349
361
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.
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:
Copy file name to clipboardExpand all lines: package.json
+1-1Lines changed: 1 addition & 1 deletion
Original file line number
Diff line number
Diff line change
@@ -1,6 +1,6 @@
1
1
{
2
2
"name": "zero-backpressure-semaphore-typescript",
3
-
"version": "3.0.6",
3
+
"version": "3.0.7",
4
4
"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.",
0 commit comments