Skip to content

Commit e9ff1c2

Browse files
authored
Merge pull request #1352 from Fl4m3Ph03n1x/master
Added documentation for REDIS optimistic lock system.
2 parents 7e945bb + 7b57578 commit e9ff1c2

File tree

1 file changed

+122
-0
lines changed

1 file changed

+122
-0
lines changed

README.md

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -704,6 +704,128 @@ significantly compared to firing the same commands in a loop without waiting for
704704
the result! See the benchmarks for further comparison. Please remember that all
705705
commands are kept in memory until they are fired.
706706

707+
## Optimistic Locks
708+
709+
Using `multi` you can make sure your modifications run as a transaction, but you
710+
can't be sure you got there first. What if another client modified a key while
711+
you were working with it's data?
712+
713+
To solve this, Redis supports the [WATCH](https://redis.io/topics/transactions)
714+
command, which is meant to be used with MULTI:
715+
716+
```js
717+
var redis = require("redis"),
718+
client = redis.createClient({ ... });
719+
720+
client.watch("foo", function( err ){
721+
if(err) throw err;
722+
723+
client.get("foo", function(err, result) {
724+
if(err) throw err;
725+
726+
// Process result
727+
// Heavy and time consuming operation here
728+
729+
client.multi()
730+
.set("foo", "some heavy computation")
731+
.exec(function(err, results) {
732+
733+
/**
734+
* If err is null, it means Redis successfully attempted
735+
* the operation.
736+
*/
737+
if(err) throw err;
738+
739+
/**
740+
* If results === null, it means that a concurrent client
741+
* changed the key while we were processing it and thus
742+
* the execution of the MULTI command was not performed.
743+
*
744+
* NOTICE: Failing an execution of MULTI is not considered
745+
* an error. So you will have err === null and results === null
746+
*/
747+
748+
});
749+
});
750+
});
751+
```
752+
753+
The above snippet shows the correct usage of `watch` with `multi`. Every time a
754+
watched key is changed before the execution of a `multi` command, the execution
755+
will return `null`. On a normal situation, the execution will return an array of
756+
values with the results of the operations.
757+
758+
As stated in the snippet, failing the execution of a `multi` command being watched
759+
is not considered an error. The execution may return an error if, for example, the
760+
client cannot connect to Redis.
761+
762+
An example where we can see the execution of a `multi` command fail is as follows:
763+
764+
```js
765+
let clients = {};
766+
clients.watcher = redis.createClient({ ... } );
767+
clients.alterer = clients.watcher.duplicate();
768+
769+
clients.watcher.watch('foo',function(err) {
770+
if (err) { throw err; }
771+
//if you comment out the next line, the transaction will work
772+
clients.alterer.set('foo',Math.random(), (err) => {if (err) { throw err; }})
773+
774+
//using a setTimeout here to ensure that the MULTI/EXEC will come after the SET.
775+
//Normally, you would use a callback to ensure order, but I want the above SET command
776+
//to be easily comment-out-able.
777+
setTimeout(function() {
778+
clients.watcher
779+
.multi()
780+
.set('foo','abc')
781+
.set('bar','1234')
782+
.exec((err,results) => {
783+
if (err) { throw err; }
784+
if (results === null) {
785+
console.log('transaction aborted because results were null');
786+
} else {
787+
console.log('transaction worked and returned',results)
788+
}
789+
clients.watcher.quit();
790+
clients.alterer.quit();
791+
});
792+
},1000);
793+
});
794+
```
795+
796+
### WATCH limitations
797+
798+
Redis WATCH works only on *whole* key values. For example, with WATCH you can
799+
watch a hash for modifications, but you cannot watch a specific field of a hash.
800+
801+
The following example would watch the keys `foo` and `hello`, not the field `hello`
802+
of hash `foo`:
803+
804+
```js
805+
var redis = require("redis"),
806+
client = redis.createClient({ ... });
807+
808+
client.hget( "foo", "hello", function(err, result){
809+
810+
//Do some processing with the value from this field and watch it after
811+
812+
client.watch("foo", "hello", function( err ){
813+
if(err) throw err;
814+
815+
/**
816+
* WRONG: This is now watching the keys 'foo' and 'hello'. It is not
817+
* watching the field 'hello' of hash 'foo'. Because the key 'foo'
818+
* refers to a hash, this command is now watching the entire hash
819+
* for modifications.
820+
*/
821+
});
822+
} )
823+
824+
```
825+
826+
This limitation also applies to sets ( cannot watch individual set members )
827+
and any other collections.
828+
707829
## Monitor mode
708830

709831
Redis supports the `MONITOR` command, which lets you see all commands received

0 commit comments

Comments
 (0)