Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
ruanhaishen
redis
Commits
37ab76c9
Commit
37ab76c9
authored
May 25, 2010
by
antirez
Browse files
WATCH for MULTI/EXEC (CAS alike concurrency)
parent
75a190ca
Changes
1
Hide whitespace changes
Inline
Side-by-side
redis.c
View file @
37ab76c9
...
@@ -190,6 +190,7 @@ static char* strencoding[] = {
...
@@ -190,6 +190,7 @@ static char* strencoding[] = {
#define REDIS_MULTI 8
/* This client is in a MULTI context */
#define REDIS_MULTI 8
/* This client is in a MULTI context */
#define REDIS_BLOCKED 16
/* The client is waiting in a blocking operation */
#define REDIS_BLOCKED 16
/* The client is waiting in a blocking operation */
#define REDIS_IO_WAIT 32
/* The client is waiting for Virtual Memory I/O */
#define REDIS_IO_WAIT 32
/* The client is waiting for Virtual Memory I/O */
#define REDIS_DIRTY_CAS 64
/* Watched keys modified. EXEC will fail. */
/* Slave replication state - slave side */
/* Slave replication state - slave side */
#define REDIS_REPL_NONE 0
/* No active replication */
#define REDIS_REPL_NONE 0
/* No active replication */
...
@@ -285,8 +286,9 @@ typedef struct redisObject {
...
@@ -285,8 +286,9 @@ typedef struct redisObject {
typedef
struct
redisDb
{
typedef
struct
redisDb
{
dict
*
dict
;
/* The keyspace for this DB */
dict
*
dict
;
/* The keyspace for this DB */
dict
*
expires
;
/* Timeout of keys with a timeout set */
dict
*
expires
;
/* Timeout of keys with a timeout set */
dict
*
blockingkeys
;
/* Keys with clients waiting for data (BLPOP) */
dict
*
blocking
_
keys
;
/* Keys with clients waiting for data (BLPOP) */
dict
*
io_keys
;
/* Keys with clients waiting for VM I/O */
dict
*
io_keys
;
/* Keys with clients waiting for VM I/O */
dict
*
watched_keys
;
/* WATCHED keys for MULTI/EXEC CAS */
int
id
;
int
id
;
}
redisDb
;
}
redisDb
;
...
@@ -324,13 +326,14 @@ typedef struct redisClient {
...
@@ -324,13 +326,14 @@ typedef struct redisClient {
long
repldboff
;
/* replication DB file offset */
long
repldboff
;
/* replication DB file offset */
off_t
repldbsize
;
/* replication DB file size */
off_t
repldbsize
;
/* replication DB file size */
multiState
mstate
;
/* MULTI/EXEC state */
multiState
mstate
;
/* MULTI/EXEC state */
robj
**
blockingkeys
;
/* The key we are waiting to terminate a blocking
robj
**
blocking
_
keys
;
/* The key we are waiting to terminate a blocking
* operation such as BLPOP. Otherwise NULL. */
* operation such as BLPOP. Otherwise NULL. */
int
blockingkeysnum
;
/* Number of blocking keys */
int
blocking
_
keys
_
num
;
/* Number of blocking keys */
time_t
blockingto
;
/* Blocking operation timeout. If UNIX current time
time_t
blockingto
;
/* Blocking operation timeout. If UNIX current time
* is >= blockingto then the operation timed out. */
* is >= blockingto then the operation timed out. */
list
*
io_keys
;
/* Keys this client is waiting to be loaded from the
list
*
io_keys
;
/* Keys this client is waiting to be loaded from the
* swap file in order to continue. */
* swap file in order to continue. */
list
*
watched_keys
;
/* Keys WATCHED for MULTI/EXEC CAS */
dict
*
pubsub_channels
;
/* channels a client is interested in (SUBSCRIBE) */
dict
*
pubsub_channels
;
/* channels a client is interested in (SUBSCRIBE) */
list
*
pubsub_patterns
;
/* patterns a client is interested in (SUBSCRIBE) */
list
*
pubsub_patterns
;
/* patterns a client is interested in (SUBSCRIBE) */
}
redisClient
;
}
redisClient
;
...
@@ -633,6 +636,8 @@ static void usage();
...
@@ -633,6 +636,8 @@ static void usage();
static
int
rewriteAppendOnlyFileBackground
(
void
);
static
int
rewriteAppendOnlyFileBackground
(
void
);
static
int
vmSwapObjectBlocking
(
robj
*
key
,
robj
*
val
);
static
int
vmSwapObjectBlocking
(
robj
*
key
,
robj
*
val
);
static
int
prepareForShutdown
();
static
int
prepareForShutdown
();
static
void
touchWatchedKey
(
redisDb
*
db
,
robj
*
key
);
static
void
unwatchAllKeys
(
redisClient
*
c
);
static
void
authCommand
(
redisClient
*
c
);
static
void
authCommand
(
redisClient
*
c
);
static
void
pingCommand
(
redisClient
*
c
);
static
void
pingCommand
(
redisClient
*
c
);
...
@@ -739,6 +744,8 @@ static void unsubscribeCommand(redisClient *c);
...
@@ -739,6 +744,8 @@ static void unsubscribeCommand(redisClient *c);
static
void
psubscribeCommand
(
redisClient
*
c
);
static
void
psubscribeCommand
(
redisClient
*
c
);
static
void
punsubscribeCommand
(
redisClient
*
c
);
static
void
punsubscribeCommand
(
redisClient
*
c
);
static
void
publishCommand
(
redisClient
*
c
);
static
void
publishCommand
(
redisClient
*
c
);
static
void
watchCommand
(
redisClient
*
c
);
static
void
unwatchCommand
(
redisClient
*
c
);
/*================================= Globals ================================= */
/*================================= Globals ================================= */
...
@@ -851,6 +858,8 @@ static struct redisCommand cmdTable[] = {
...
@@ -851,6 +858,8 @@ static struct redisCommand cmdTable[] = {
{
"psubscribe"
,
psubscribeCommand
,
-
2
,
REDIS_CMD_INLINE
,
NULL
,
0
,
0
,
0
},
{
"psubscribe"
,
psubscribeCommand
,
-
2
,
REDIS_CMD_INLINE
,
NULL
,
0
,
0
,
0
},
{
"punsubscribe"
,
punsubscribeCommand
,
-
1
,
REDIS_CMD_INLINE
,
NULL
,
0
,
0
,
0
},
{
"punsubscribe"
,
punsubscribeCommand
,
-
1
,
REDIS_CMD_INLINE
,
NULL
,
0
,
0
,
0
},
{
"publish"
,
publishCommand
,
3
,
REDIS_CMD_BULK
|
REDIS_CMD_FORCE_REPLICATION
,
NULL
,
0
,
0
,
0
},
{
"publish"
,
publishCommand
,
3
,
REDIS_CMD_BULK
|
REDIS_CMD_FORCE_REPLICATION
,
NULL
,
0
,
0
,
0
},
{
"watch"
,
watchCommand
,
-
2
,
REDIS_CMD_INLINE
,
NULL
,
0
,
0
,
0
},
{
"unwatch"
,
unwatchCommand
,
1
,
REDIS_CMD_INLINE
,
NULL
,
0
,
0
,
0
},
{
NULL
,
NULL
,
0
,
0
,
NULL
,
0
,
0
,
0
}
{
NULL
,
NULL
,
0
,
0
,
NULL
,
0
,
0
,
0
}
};
};
...
@@ -1745,7 +1754,8 @@ static void initServer() {
...
@@ -1745,7 +1754,8 @@ static void initServer() {
for
(
j
=
0
;
j
<
server
.
dbnum
;
j
++
)
{
for
(
j
=
0
;
j
<
server
.
dbnum
;
j
++
)
{
server
.
db
[
j
].
dict
=
dictCreate
(
&
dbDictType
,
NULL
);
server
.
db
[
j
].
dict
=
dictCreate
(
&
dbDictType
,
NULL
);
server
.
db
[
j
].
expires
=
dictCreate
(
&
keyptrDictType
,
NULL
);
server
.
db
[
j
].
expires
=
dictCreate
(
&
keyptrDictType
,
NULL
);
server
.
db
[
j
].
blockingkeys
=
dictCreate
(
&
keylistDictType
,
NULL
);
server
.
db
[
j
].
blocking_keys
=
dictCreate
(
&
keylistDictType
,
NULL
);
server
.
db
[
j
].
watched_keys
=
dictCreate
(
&
keylistDictType
,
NULL
);
if
(
server
.
vm_enabled
)
if
(
server
.
vm_enabled
)
server
.
db
[
j
].
io_keys
=
dictCreate
(
&
keylistDictType
,
NULL
);
server
.
db
[
j
].
io_keys
=
dictCreate
(
&
keylistDictType
,
NULL
);
server
.
db
[
j
].
id
=
j
;
server
.
db
[
j
].
id
=
j
;
...
@@ -2011,6 +2021,9 @@ static void freeClient(redisClient *c) {
...
@@ -2011,6 +2021,9 @@ static void freeClient(redisClient *c) {
if
(
c
->
flags
&
REDIS_BLOCKED
)
if
(
c
->
flags
&
REDIS_BLOCKED
)
unblockClientWaitingData
(
c
);
unblockClientWaitingData
(
c
);
/* UNWATCH all the keys */
unwatchAllKeys
(
c
);
listRelease
(
c
->
watched_keys
);
/* Unsubscribe from all the pubsub channels */
/* Unsubscribe from all the pubsub channels */
pubsubUnsubscribeAllChannels
(
c
,
0
);
pubsubUnsubscribeAllChannels
(
c
,
0
);
pubsubUnsubscribeAllPatterns
(
c
,
0
);
pubsubUnsubscribeAllPatterns
(
c
,
0
);
...
@@ -2026,7 +2039,8 @@ static void freeClient(redisClient *c) {
...
@@ -2026,7 +2039,8 @@ static void freeClient(redisClient *c) {
ln
=
listSearchKey
(
server
.
clients
,
c
);
ln
=
listSearchKey
(
server
.
clients
,
c
);
redisAssert
(
ln
!=
NULL
);
redisAssert
(
ln
!=
NULL
);
listDelNode
(
server
.
clients
,
ln
);
listDelNode
(
server
.
clients
,
ln
);
/* Remove from the list of clients waiting for swapped keys */
/* Remove from the list of clients that are now ready to be restarted
* after waiting for swapped keys */
if
(
c
->
flags
&
REDIS_IO_WAIT
&&
listLength
(
c
->
io_keys
)
==
0
)
{
if
(
c
->
flags
&
REDIS_IO_WAIT
&&
listLength
(
c
->
io_keys
)
==
0
)
{
ln
=
listSearchKey
(
server
.
io_ready_clients
,
c
);
ln
=
listSearchKey
(
server
.
io_ready_clients
,
c
);
if
(
ln
)
{
if
(
ln
)
{
...
@@ -2034,6 +2048,7 @@ static void freeClient(redisClient *c) {
...
@@ -2034,6 +2048,7 @@ static void freeClient(redisClient *c) {
server
.
vm_blocked_clients
--
;
server
.
vm_blocked_clients
--
;
}
}
}
}
/* Remove from the list of clients waiting for swapped keys */
while
(
server
.
vm_enabled
&&
listLength
(
c
->
io_keys
))
{
while
(
server
.
vm_enabled
&&
listLength
(
c
->
io_keys
))
{
ln
=
listFirst
(
c
->
io_keys
);
ln
=
listFirst
(
c
->
io_keys
);
dontWaitForSwappedKey
(
c
,
ln
->
value
);
dontWaitForSwappedKey
(
c
,
ln
->
value
);
...
@@ -2725,8 +2740,8 @@ static redisClient *createClient(int fd) {
...
@@ -2725,8 +2740,8 @@ static redisClient *createClient(int fd) {
c
->
reply
=
listCreate
();
c
->
reply
=
listCreate
();
listSetFreeMethod
(
c
->
reply
,
decrRefCount
);
listSetFreeMethod
(
c
->
reply
,
decrRefCount
);
listSetDupMethod
(
c
->
reply
,
dupClientReplyValue
);
listSetDupMethod
(
c
->
reply
,
dupClientReplyValue
);
c
->
blockingkeys
=
NULL
;
c
->
blocking
_
keys
=
NULL
;
c
->
blockingkeysnum
=
0
;
c
->
blocking
_
keys
_
num
=
0
;
c
->
io_keys
=
listCreate
();
c
->
io_keys
=
listCreate
();
listSetFreeMethod
(
c
->
io_keys
,
decrRefCount
);
listSetFreeMethod
(
c
->
io_keys
,
decrRefCount
);
c
->
pubsub_channels
=
dictCreate
(
&
setDictType
,
NULL
);
c
->
pubsub_channels
=
dictCreate
(
&
setDictType
,
NULL
);
...
@@ -3096,6 +3111,7 @@ static robj *lookupKeyRead(redisDb *db, robj *key) {
...
@@ -3096,6 +3111,7 @@ static robj *lookupKeyRead(redisDb *db, robj *key) {
static
robj
*
lookupKeyWrite
(
redisDb
*
db
,
robj
*
key
)
{
static
robj
*
lookupKeyWrite
(
redisDb
*
db
,
robj
*
key
)
{
deleteIfVolatile
(
db
,
key
);
deleteIfVolatile
(
db
,
key
);
touchWatchedKey
(
db
,
key
);
return
lookupKey
(
db
,
key
);
return
lookupKey
(
db
,
key
);
}
}
...
@@ -4229,6 +4245,7 @@ static void setGenericCommand(redisClient *c, int nx, robj *key, robj *val, robj
...
@@ -4229,6 +4245,7 @@ static void setGenericCommand(redisClient *c, int nx, robj *key, robj *val, robj
}
}
}
}
touchWatchedKey
(
c
->
db
,
key
);
if
(
nx
)
deleteIfVolatile
(
c
->
db
,
key
);
if
(
nx
)
deleteIfVolatile
(
c
->
db
,
key
);
retval
=
dictAdd
(
c
->
db
->
dict
,
key
,
val
);
retval
=
dictAdd
(
c
->
db
->
dict
,
key
,
val
);
if
(
retval
==
DICT_ERR
)
{
if
(
retval
==
DICT_ERR
)
{
...
@@ -4499,6 +4516,7 @@ static void delCommand(redisClient *c) {
...
@@ -4499,6 +4516,7 @@ static void delCommand(redisClient *c) {
for
(
j
=
1
;
j
<
c
->
argc
;
j
++
)
{
for
(
j
=
1
;
j
<
c
->
argc
;
j
++
)
{
if
(
deleteKey
(
c
->
db
,
c
->
argv
[
j
]))
{
if
(
deleteKey
(
c
->
db
,
c
->
argv
[
j
]))
{
touchWatchedKey
(
c
->
db
,
c
->
argv
[
j
]);
server
.
dirty
++
;
server
.
dirty
++
;
deleted
++
;
deleted
++
;
}
}
...
@@ -7511,6 +7529,17 @@ static void execCommand(redisClient *c) {
...
@@ -7511,6 +7529,17 @@ static void execCommand(redisClient *c) {
return
;
return
;
}
}
/* Check if we need to abort the EXEC if some WATCHed key was touched.
* A failed EXEC will return a multi bulk nil object. */
if
(
c
->
flags
&
REDIS_DIRTY_CAS
)
{
freeClientMultiState
(
c
);
initClientMultiState
(
c
);
c
->
flags
&=
~
(
REDIS_MULTI
|
REDIS_DIRTY_CAS
);
unwatchAllKeys
(
c
);
addReply
(
c
,
shared
.
nullmultibulk
);
return
;
}
/* Replicate a MULTI request now that we are sure the block is executed.
/* Replicate a MULTI request now that we are sure the block is executed.
* This way we'll deliver the MULTI/..../EXEC block as a whole and
* This way we'll deliver the MULTI/..../EXEC block as a whole and
* both the AOF and the replication link will have the same consistency
* both the AOF and the replication link will have the same consistency
...
@@ -7531,6 +7560,7 @@ static void execCommand(redisClient *c) {
...
@@ -7531,6 +7560,7 @@ static void execCommand(redisClient *c) {
freeClientMultiState
(
c
);
freeClientMultiState
(
c
);
initClientMultiState
(
c
);
initClientMultiState
(
c
);
c
->
flags
&=
(
~
REDIS_MULTI
);
c
->
flags
&=
(
~
REDIS_MULTI
);
unwatchAllKeys
(
c
);
/* Make sure the EXEC command is always replicated / AOF, since we
/* Make sure the EXEC command is always replicated / AOF, since we
* always send the MULTI command (we can't know beforehand if the
* always send the MULTI command (we can't know beforehand if the
* next operations will contain at least a modification to the DB). */
* next operations will contain at least a modification to the DB). */
...
@@ -7557,7 +7587,7 @@ static void execCommand(redisClient *c) {
...
@@ -7557,7 +7587,7 @@ static void execCommand(redisClient *c) {
* empty we need to block. In order to do so we remove the notification for
* empty we need to block. In order to do so we remove the notification for
* new data to read in the client socket (so that we'll not serve new
* new data to read in the client socket (so that we'll not serve new
* requests if the blocking request is not served). Also we put the client
* requests if the blocking request is not served). Also we put the client
* in a dictionary (db->blockingkeys) mapping keys to a list of clients
* in a dictionary (db->blocking
_
keys) mapping keys to a list of clients
* blocking for this keys.
* blocking for this keys.
* - If a PUSH operation against a key with blocked clients waiting is
* - If a PUSH operation against a key with blocked clients waiting is
* performed, we serve the first in the list: basically instead to push
* performed, we serve the first in the list: basically instead to push
...
@@ -7575,22 +7605,22 @@ static void blockForKeys(redisClient *c, robj **keys, int numkeys, time_t timeou
...
@@ -7575,22 +7605,22 @@ static void blockForKeys(redisClient *c, robj **keys, int numkeys, time_t timeou
list
*
l
;
list
*
l
;
int
j
;
int
j
;
c
->
blockingkeys
=
zmalloc
(
sizeof
(
robj
*
)
*
numkeys
);
c
->
blocking
_
keys
=
zmalloc
(
sizeof
(
robj
*
)
*
numkeys
);
c
->
blockingkeysnum
=
numkeys
;
c
->
blocking
_
keys
_
num
=
numkeys
;
c
->
blockingto
=
timeout
;
c
->
blockingto
=
timeout
;
for
(
j
=
0
;
j
<
numkeys
;
j
++
)
{
for
(
j
=
0
;
j
<
numkeys
;
j
++
)
{
/* Add the key in the client structure, to map clients -> keys */
/* Add the key in the client structure, to map clients -> keys */
c
->
blockingkeys
[
j
]
=
keys
[
j
];
c
->
blocking
_
keys
[
j
]
=
keys
[
j
];
incrRefCount
(
keys
[
j
]);
incrRefCount
(
keys
[
j
]);
/* And in the other "side", to map keys -> clients */
/* And in the other "side", to map keys -> clients */
de
=
dictFind
(
c
->
db
->
blockingkeys
,
keys
[
j
]);
de
=
dictFind
(
c
->
db
->
blocking
_
keys
,
keys
[
j
]);
if
(
de
==
NULL
)
{
if
(
de
==
NULL
)
{
int
retval
;
int
retval
;
/* For every key we take a list of clients blocked for it */
/* For every key we take a list of clients blocked for it */
l
=
listCreate
();
l
=
listCreate
();
retval
=
dictAdd
(
c
->
db
->
blockingkeys
,
keys
[
j
],
l
);
retval
=
dictAdd
(
c
->
db
->
blocking
_
keys
,
keys
[
j
],
l
);
incrRefCount
(
keys
[
j
]);
incrRefCount
(
keys
[
j
]);
assert
(
retval
==
DICT_OK
);
assert
(
retval
==
DICT_OK
);
}
else
{
}
else
{
...
@@ -7609,22 +7639,22 @@ static void unblockClientWaitingData(redisClient *c) {
...
@@ -7609,22 +7639,22 @@ static void unblockClientWaitingData(redisClient *c) {
list
*
l
;
list
*
l
;
int
j
;
int
j
;
assert
(
c
->
blockingkeys
!=
NULL
);
assert
(
c
->
blocking
_
keys
!=
NULL
);
/* The client may wait for multiple keys, so unblock it for every key. */
/* The client may wait for multiple keys, so unblock it for every key. */
for
(
j
=
0
;
j
<
c
->
blockingkeysnum
;
j
++
)
{
for
(
j
=
0
;
j
<
c
->
blocking
_
keys
_
num
;
j
++
)
{
/* Remove this client from the list of clients waiting for this key. */
/* Remove this client from the list of clients waiting for this key. */
de
=
dictFind
(
c
->
db
->
blockingkeys
,
c
->
blockingkeys
[
j
]);
de
=
dictFind
(
c
->
db
->
blocking
_
keys
,
c
->
blocking
_
keys
[
j
]);
assert
(
de
!=
NULL
);
assert
(
de
!=
NULL
);
l
=
dictGetEntryVal
(
de
);
l
=
dictGetEntryVal
(
de
);
listDelNode
(
l
,
listSearchKey
(
l
,
c
));
listDelNode
(
l
,
listSearchKey
(
l
,
c
));
/* If the list is empty we need to remove it to avoid wasting memory */
/* If the list is empty we need to remove it to avoid wasting memory */
if
(
listLength
(
l
)
==
0
)
if
(
listLength
(
l
)
==
0
)
dictDelete
(
c
->
db
->
blockingkeys
,
c
->
blockingkeys
[
j
]);
dictDelete
(
c
->
db
->
blocking
_
keys
,
c
->
blocking
_
keys
[
j
]);
decrRefCount
(
c
->
blockingkeys
[
j
]);
decrRefCount
(
c
->
blocking
_
keys
[
j
]);
}
}
/* Cleanup the client structure */
/* Cleanup the client structure */
zfree
(
c
->
blockingkeys
);
zfree
(
c
->
blocking
_
keys
);
c
->
blockingkeys
=
NULL
;
c
->
blocking
_
keys
=
NULL
;
c
->
flags
&=
(
~
REDIS_BLOCKED
);
c
->
flags
&=
(
~
REDIS_BLOCKED
);
server
.
blpop_blocked_clients
--
;
server
.
blpop_blocked_clients
--
;
/* We want to process data if there is some command waiting
/* We want to process data if there is some command waiting
...
@@ -7651,7 +7681,7 @@ static int handleClientsWaitingListPush(redisClient *c, robj *key, robj *ele) {
...
@@ -7651,7 +7681,7 @@ static int handleClientsWaitingListPush(redisClient *c, robj *key, robj *ele) {
list
*
l
;
list
*
l
;
listNode
*
ln
;
listNode
*
ln
;
de
=
dictFind
(
c
->
db
->
blockingkeys
,
key
);
de
=
dictFind
(
c
->
db
->
blocking
_
keys
,
key
);
if
(
de
==
NULL
)
return
0
;
if
(
de
==
NULL
)
return
0
;
l
=
dictGetEntryVal
(
de
);
l
=
dictGetEntryVal
(
de
);
ln
=
listFirst
(
l
);
ln
=
listFirst
(
l
);
...
@@ -10348,6 +10378,116 @@ static void publishCommand(redisClient *c) {
...
@@ -10348,6 +10378,116 @@ static void publishCommand(redisClient *c) {
addReplyLongLong
(
c
,
receivers
);
addReplyLongLong
(
c
,
receivers
);
}
}
/* ===================== WATCH (CAS alike for MULTI/EXEC) ===================
*
* The implementation uses a per-DB hash table mapping keys to list of clients
* WATCHing those keys, so that given a key that is going to be modified
* we can mark all the associated clients as dirty.
*
* Also every client contains a list of WATCHed keys so that's possible to
* un-watch such keys when the client is freed or when UNWATCH is called. */
/* In the client->watched_keys list we need to use watchedKey structures
* as in order to identify a key in Redis we need both the key name and the
* DB */
typedef
struct
watchedKey
{
robj
*
key
;
redisDb
*
db
;
}
watchedKey
;
/* Watch for the specified key */
static
void
watchForKey
(
redisClient
*
c
,
robj
*
key
)
{
list
*
clients
=
NULL
;
listIter
li
;
listNode
*
ln
;
watchedKey
*
wk
;
/* Check if we are already watching for this key */
listRewind
(
c
->
watched_keys
,
&
li
);
while
((
ln
=
listNext
(
&
li
)))
{
wk
=
listNodeValue
(
ln
);
if
(
wk
->
db
==
c
->
db
&&
equalStringObjects
(
key
,
wk
->
key
))
return
;
/* Key already watched */
}
/* This key is not already watched in this DB. Let's add it */
clients
=
dictFetchValue
(
c
->
db
->
watched_keys
,
key
);
if
(
!
clients
)
{
clients
=
listCreate
();
dictAdd
(
c
->
db
->
watched_keys
,
key
,
clients
);
incrRefCount
(
key
);
}
listAddNodeTail
(
clients
,
c
);
/* Add the new key to the lits of keys watched by this client */
wk
=
zmalloc
(
sizeof
(
*
wk
));
wk
->
key
=
key
;
wk
->
db
=
c
->
db
;
incrRefCount
(
key
);
listAddNodeTail
(
c
->
watched_keys
,
wk
);
}
/* Unwatch all the keys watched by this client. To clean the EXEC dirty
* flag is up to the caller. */
static
void
unwatchAllKeys
(
redisClient
*
c
)
{
listIter
li
;
listNode
*
ln
;
if
(
listLength
(
c
->
watched_keys
)
==
0
)
return
;
listRewind
(
c
->
watched_keys
,
&
li
);
while
((
ln
=
listNext
(
&
li
)))
{
list
*
clients
;
watchedKey
*
wk
;
/* Lookup the watched key -> clients list and remove the client
* from the list */
wk
=
listNodeValue
(
ln
);
clients
=
dictFetchValue
(
wk
->
db
->
watched_keys
,
wk
->
key
);
assert
(
clients
!=
NULL
);
listDelNode
(
clients
,
listSearchKey
(
clients
,
c
));
/* Kill the entry at all if this was the only client */
if
(
listLength
(
clients
)
==
0
)
dictDelete
(
wk
->
db
->
watched_keys
,
wk
->
key
);
/* Remove this watched key from the client->watched list */
listDelNode
(
c
->
watched_keys
,
ln
);
decrRefCount
(
wk
->
key
);
zfree
(
wk
);
}
}
/* "Touch" a key, so that if this key is being WATCHed by soem client the
* next EXEC will fail. */
static
void
touchWatchedKey
(
redisDb
*
db
,
robj
*
key
)
{
list
*
clients
;
listIter
li
;
listNode
*
ln
;
if
(
dictSize
(
db
->
watched_keys
)
==
0
)
return
;
clients
=
dictFetchValue
(
db
->
watched_keys
,
key
);
if
(
!
clients
)
return
;
/* Mark all the clients watching this key as REDIS_DIRTY_CAS */
/* Check if we are already watching for this key */
listRewind
(
clients
,
&
li
);
while
((
ln
=
listNext
(
&
li
)))
{
redisClient
*
c
=
listNodeValue
(
ln
);
c
->
flags
|=
REDIS_DIRTY_CAS
;
}
}
static
void
watchCommand
(
redisClient
*
c
)
{
int
j
;
for
(
j
=
1
;
j
<
c
->
argc
;
j
++
)
watchForKey
(
c
,
c
->
argv
[
j
]);
addReply
(
c
,
shared
.
ok
);
}
static
void
unwatchCommand
(
redisClient
*
c
)
{
unwatchAllKeys
(
c
);
c
->
flags
&=
(
~
REDIS_DIRTY_CAS
);
addReply
(
c
,
shared
.
ok
);
}
/* ================================= Debugging ============================== */
/* ================================= Debugging ============================== */
/* Compute the sha1 of string at 's' with 'len' bytes long.
/* Compute the sha1 of string at 's' with 'len' bytes long.
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment