Skip to content

Commit dbf8807

Browse files
bade-ryeforeverest
andauthored
Add ignoreErrors option to run method (#10)
* add ignoreError option * Update src/executor.ts Co-authored-by: Dima Z <dima@rye.com> * change to shouldIgnoreError * update tabbing * updat tests * updated readme and doc string * Update package.json Co-authored-by: Dima Z <dima@rye.com> * format code --------- Co-authored-by: Dima Z <dima@rye.com>
1 parent 18d5e7d commit dbf8807

File tree

5 files changed

+32
-4
lines changed

5 files changed

+32
-4
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ The `run` method is a core function of the `IdempotentExecutor`, responsible for
108108
- `onActionError`: A callback invoked when the action fails during execution.
109109
- `onSuccessReplay`: A callback invoked when a successful action is replayed.
110110
- `onErrorReplay`: A callback invoked when a failed action is replayed.
111+
- `shouldIgnoreError`: A callback invoked when an error is encountered. If it returns `true`, the error will not be cached and will not be replayed.
111112

112113
### Serialization
113114

package-lock.json

Lines changed: 5 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": "idempotency-redis",
3-
"version": "1.2.3",
3+
"version": "1.3.0",
44
"description": "Idempotency guarantee via Redis",
55
"main": "dist/index.js",
66
"types": "dist/index.d.ts",

src/executor.spec.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,20 @@ describe('IdempotentExecutor.run method', () => {
6969
expect(action).toHaveBeenCalledTimes(1);
7070
});
7171

72+
it('should handle action execution failure by caching but not replaying the error if ignoreError is true', async () => {
73+
const error = new Error('action failed');
74+
const action = jest.fn().mockRejectedValue(error);
75+
76+
await expect(
77+
executor.run('key1', action, { shouldIgnoreError: () => true }),
78+
).rejects.toThrow(error);
79+
await expect(
80+
executor.run('key1', action, { shouldIgnoreError: () => true }),
81+
).rejects.toThrow(error);
82+
83+
expect(action).toHaveBeenCalledTimes(2);
84+
});
85+
7286
it('should replay undefined value', async () => {
7387
const action = jest.fn().mockResolvedValue(undefined);
7488

src/executor.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ interface RunOptions<T> {
3434
timeout: number;
3535
valueSerializer: Serializer<T>;
3636
errorSerializer: Serializer<Error>;
37+
shouldIgnoreError?: (error: Error) => boolean;
3738
onActionSuccess: (idempotencyKey: string, value: T) => T;
3839
onActionError: (idempotencyKey: string, error: Error) => Error;
3940
onSuccessReplay: (idempotencyKey: string, value: T) => T;
@@ -70,6 +71,7 @@ export class IdempotentExecutor {
7071
* @property {(idempotencyKey: string, error: Error) => Error} options.onActionError - Optional. A callback that is invoked when the action fails during execution. It receives the idempotency key and the error that occurred, and should return the error to be thrown by the executor.
7172
* @property {(idempotencyKey: string, value: T) => T} options.onSuccessReplay - Optional. A callback that is invoked when a successful action is replayed. It receives the idempotency key and the result of the action, and should return the result to be returned by the executor.
7273
* @property {(idempotencyKey: string, error: Error) => Error} options.onErrorReplay - Optional. A callback that is invoked when a failed action is replayed. It receives the idempotency key and the error that occurred, and should return the error to be thrown by the executor.
74+
* @property {(error: Error) => boolean} options.shouldIgnoreError - Optional. A callback that is invoked when an error is encountered. If it returns `true`, the error will not be cached and will not be replayed.
7375
* @returns {Promise<T>} The result of the executed action.
7476
* @throws {IdempotentExecutorCriticalError} If saving the result to cache fails, potentially leading to non-idempotent executions.
7577
* @throws {IdempotentExecutorCacheError} If retrieving the cached result fails.
@@ -89,7 +91,7 @@ export class IdempotentExecutor {
8991
const errorSerializer =
9092
options?.errorSerializer ?? new DefaultErrorSerializer();
9193
const cacheKey = `idempotent-executor-result:${idempotencyKey}`;
92-
94+
const shouldIgnoreError = options?.shouldIgnoreError ?? (() => false);
9395
try {
9496
return await this.redlock.using<T>(
9597
[idempotencyKey],
@@ -154,6 +156,7 @@ export class IdempotentExecutor {
154156
actionResult,
155157
valueSerializer,
156158
errorSerializer,
159+
shouldIgnoreError,
157160
);
158161

159162
// If the action resulted in an error, throw it.
@@ -287,9 +290,16 @@ export class IdempotentExecutor {
287290
value: T | Error,
288291
valueSerializer: Serializer<T>,
289292
errorSerializer: Serializer<Error>,
293+
shouldIgnoreError: (error: Error) => boolean,
290294
): Promise<void> {
291295
try {
292296
if (value instanceof Error) {
297+
const ignoreError = shouldIgnoreError(value);
298+
299+
if (ignoreError) {
300+
return;
301+
}
302+
293303
await this.cache.set(cacheKey, {
294304
type: 'error',
295305
error: errorSerializer.serialize(value),

0 commit comments

Comments
 (0)