diff --git a/examples/README.md b/examples/README.md index 83bbe75438e..148e5f0b07f 100644 --- a/examples/README.md +++ b/examples/README.md @@ -3,7 +3,7 @@ This folder contains example scripts showing how to use Node Redis in different scenarios. | File Name | Description | -|-----------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------| +| --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | | `blocking-list-pop.js` | Block until an element is pushed to a list | | `bloom-filter.js` | Space efficient set membership checks with a [Bloom Filter](https://en.wikipedia.org/wiki/Bloom_filter) using [RedisBloom](https://redisbloom.io) | | `command-with-modifiers.js` | Define a script that allows to run a command with several modifiers | @@ -11,7 +11,7 @@ This folder contains example scripts showing how to use Node Redis in different | `count-min-sketch.js` | Estimate the frequency of a given event using the [RedisBloom](https://redisbloom.io) Count-Min Sketch | | `cuckoo-filter.js` | Space efficient set membership checks with a [Cuckoo Filter](https://en.wikipedia.org/wiki/Cuckoo_filter) using [RedisBloom](https://redisbloom.io) | | `get-server-time.js` | Get the time from the Redis server | -| `hyperloglog.js` | Showing use of Hyperloglog commands [PFADD, PFCOUNT and PFMERGE](https://redis.io/commands/?group=hyperloglog) | +| `hyperloglog.js` | Showing use of Hyperloglog commands [PFADD, PFCOUNT and PFMERGE](https://redis.io/commands/?group=hyperloglog) | | `lua-multi-incr.js` | Define a custom lua script that allows you to perform INCRBY on multiple keys | | `managing-json.js` | Store, retrieve and manipulate JSON data atomically with [RedisJSON](https://redisjson.io/) | | `pubsub-publisher.js` | Adds multiple messages on 2 different channels messages to Redis | @@ -25,10 +25,11 @@ This folder contains example scripts showing how to use Node Redis in different | `time-series.js` | Create, populate and query timeseries data with [Redis Timeseries](https://redistimeseries.io) | | `topk.js` | Use the [RedisBloom](https://redisbloom.io) TopK to track the most frequently seen items. | | `stream-consumer-group.js` | Reads entties from a [Redis Stream](https://redis.io/topics/streams-intro) as part of a consumer group using the blocking `XREADGROUP` command | +| `transaction-with-watch.js` | An Example of [Redis transaction](https://redis.io/docs/manual/transactions) with `WATCH` command on isolated connection with optimistic locking | ## Contributing -We'd love to see more examples here. If you have an idea that you'd like to see included here, submit a Pull Request and we'll be sure to review it! Don't forget to check out our [contributing guide](../CONTRIBUTING.md). +We'd love to see more examples here. If you have an idea that you'd like to see included here, submit a Pull Request and we'll be sure to review it! Don't forget to check out our [contributing guide](../CONTRIBUTING.md). ## Setup @@ -46,21 +47,21 @@ $ npm install When adding a new example, please follow these guidelines: -* Add your code in a single JavaScript or TypeScript file per example, directly in the `examples` folder -* Do not introduce other dependencies in your example -* Give your `.js` file a meaningful name using `-` separators e.g. `adding-to-a-stream.js` / `adding-to-a-stream.ts` -* Indent your code using 2 spaces -* Use the single line `//` comment style and comment your code -* Add a comment at the top of your `.js` / `.ts` file describing what your example does -* Add a comment at the top of your `.js` / `.ts` file describing any Redis commands that need to be run to set up data for your example (try and keep this minimal) -* Use semicolons -* Use `async` and `await` -* Use single quotes, `'hello'` not `"hello"` -* Use [template literals](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals) when embedding expressions in strings -* Unless your example requires a connection string, assume Redis is on the default localhost port 6379 with no password -* Use meaningful example data, let's not use `foo`, `bar`, `baz` etc! -* Leave an empty line at the end of your `.js` file -* Update this `README.md` file to add your example to the table +- Add your code in a single JavaScript or TypeScript file per example, directly in the `examples` folder +- Do not introduce other dependencies in your example +- Give your `.js` file a meaningful name using `-` separators e.g. `adding-to-a-stream.js` / `adding-to-a-stream.ts` +- Indent your code using 2 spaces +- Use the single line `//` comment style and comment your code +- Add a comment at the top of your `.js` / `.ts` file describing what your example does +- Add a comment at the top of your `.js` / `.ts` file describing any Redis commands that need to be run to set up data for your example (try and keep this minimal) +- Use semicolons +- Use `async` and `await` +- Use single quotes, `'hello'` not `"hello"` +- Use [template literals](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals) when embedding expressions in strings +- Unless your example requires a connection string, assume Redis is on the default localhost port 6379 with no password +- Use meaningful example data, let's not use `foo`, `bar`, `baz` etc! +- Leave an empty line at the end of your `.js` file +- Update this `README.md` file to add your example to the table Use [connect-as-acl-user.js](./connect-as-acl-user.js) as a guide to develop a well formatted example script. @@ -86,5 +87,4 @@ await client.connect(); // Add your example code here... await client.quit(); - ``` diff --git a/examples/transaction-with-watch.js b/examples/transaction-with-watch.js new file mode 100644 index 00000000000..d92b910dfa3 --- /dev/null +++ b/examples/transaction-with-watch.js @@ -0,0 +1,40 @@ +import { createClient, WatchError } from 'redis'; + +const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); +const client = createClient(); +await client.connect(); + +function restrictFunctionCalls(fn, maxCalls) { + let count = 1; + return function (...args) { + return count++ < maxCalls ? fn(...args) : false; + }; +} + +const fn = restrictFunctionCalls(transaction, 4); + +async function transaction() { + try { + await client.executeIsolated(async (isolatedClient) => { + await isolatedClient.watch('paymentId:1259'); + const multi = isolatedClient + .multi() + .set('paymentId:1259', 'Payment Successfully Completed!') + .set('paymentId:1260', 'Refund Processed Successfully!'); + await delay(5000); // Do some changes to the watched key during this time... + await multi.exec(); + console.log('Transaction completed Successfully!'); + client.quit(); + }); + } catch (error) { + if (error instanceof WatchError) { + console.log('Transaction Failed Due To Concurrent Modification!'); + fn(); + } else { + console.log(`Error: ${error}`); + client.quit(); + } + } +} + +transaction();