Commit 0c9a325d authored by antirez's avatar antirez
Browse files

TCC: protect dictionaries from changing while accessed.

Certain Redis objects may change upon read only access. This is the
case, for instance, of hash tables, that may continue to incrementally
rehash after a rehashing operation. A similar problem also happens with
the PFCOUNT operation and other operations that may write as a side
effect of reading. In the case of PFCOUNT probably the right approach
would be to flag the command in a special way in the command table, so
that the operation is blocked as it if was a write operation.
parent 9bd8f02f
...@@ -1797,6 +1797,17 @@ int lockKey(client *c, robj *key, int locktype, robj **optr) { ...@@ -1797,6 +1797,17 @@ int lockKey(client *c, robj *key, int locktype, robj **optr) {
lk->obj = lookupKeyReadWithFlags(c->db,key,LOOKUP_NOTOUCH); lk->obj = lookupKeyReadWithFlags(c->db,key,LOOKUP_NOTOUCH);
dictAdd(c->db->locked_keys,key,lk); dictAdd(c->db->locked_keys,key,lk);
incrRefCount(key); incrRefCount(key);
/* Make the object immutable. In the trivial case of hash tables
* we just increment the iterators count, to prevent rehashing
* when the object is accessed in read-only. */
if (lk->obj) {
if (lk->obj->encoding == OBJ_ENCODING_HT) {
((dict*)lk->obj->ptr)->iterators++;
} else if (lk->obj->encoding == OBJ_ENCODING_SKIPLIST) {
((zset*)lk->obj->ptr)->dict->iterators++;
}
}
} else { } else {
/* If there is already a lock, it is incompatible with a new lock /* If there is already a lock, it is incompatible with a new lock
* both in the case the lock is of write type, or we want to lock * both in the case the lock is of write type, or we want to lock
...@@ -1948,7 +1959,17 @@ void unlockKey(client *c, robj *key, uint64_t owner_id) { ...@@ -1948,7 +1959,17 @@ void unlockKey(client *c, robj *key, uint64_t owner_id) {
listDelNode(lk->waiting,ln); listDelNode(lk->waiting,ln);
} }
/* Frre the lock state. */ /* If we modified the object in order to make it immutable during
* read operations, restore it in its normal state. */
if (lk->obj) {
if (lk->obj->encoding == OBJ_ENCODING_HT) {
((dict*)lk->obj->ptr)->iterators--;
} else if (lk->obj->encoding == OBJ_ENCODING_SKIPLIST) {
((zset*)lk->obj->ptr)->dict->iterators--;
}
}
/* Free the lock state. */
listRelease(lk->owners); listRelease(lk->owners);
listRelease(lk->waiting); listRelease(lk->waiting);
zfree(lk); zfree(lk);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment