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.
This commit is contained in:
antirez 2020-06-10 11:06:24 +02:00
parent 9bd8f02fe1
commit 0c9a325d08
1 changed files with 22 additions and 1 deletions

View File

@ -1797,6 +1797,17 @@ int lockKey(client *c, robj *key, int locktype, robj **optr) {
lk->obj = lookupKeyReadWithFlags(c->db,key,LOOKUP_NOTOUCH);
dictAdd(c->db->locked_keys,key,lk);
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 {
/* 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
@ -1948,7 +1959,17 @@ void unlockKey(client *c, robj *key, uint64_t owner_id) {
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->waiting);
zfree(lk);