robj *lookupKey(redisDb *db, robj *key) { dictEntry *de = dictFind(db->dict,key->ptr); if (de) { robj *val = dictGetEntryVal(de); if (server.vm_enabled) { if (val->storage == REDIS_VM_MEMORY || val->storage == REDIS_VM_SWAPPING) { /* If we were swapping the object out, cancel the operation */ if (val->storage == REDIS_VM_SWAPPING) vmCancelThreadedIOJob(val); /* Update the access time for the aging algorithm. */ val->lru = server.lruclock; } else { int notify = (val->storage == REDIS_VM_LOADING); /* Our value was swapped on disk. Bring it at home. */ redisAssert(val->type == REDIS_VMPOINTER); val = vmLoadObject(val); dictGetEntryVal(de) = val; /* Clients blocked by the VM subsystem may be waiting for * this key... */ if (notify) handleClientsBlockedOnSwappedKey(db,key); } } return val; } else { return NULL; } }
static robj *lookupKey(redisDb *db, robj *key) { dictEntry *de = dictFind(db->dict,key); if (de) { robj *key = dictGetEntryKey(de); robj *val = dictGetEntryVal(de); if (server.vm_enabled) { if (key->storage == REDIS_VM_MEMORY || key->storage == REDIS_VM_SWAPPING) { /* If we were swapping the object out, stop it, this key * was requested. */ if (key->storage == REDIS_VM_SWAPPING) vmCancelThreadedIOJob(key); /* Update the access time of the key for the aging algorithm. */ key->vm.atime = server.unixtime; } else { int notify = (key->storage == REDIS_VM_LOADING); /* Our value was swapped on disk. Bring it at home. */ redisAssert(val == NULL); val = vmLoadObject(key); dictGetEntryVal(de) = val; /* Clients blocked by the VM subsystem may be waiting for * this key... */ if (notify) handleClientsBlockedOnSwappedKey(db,key); } } return val; } else { return NULL; } }
/* Plain object loading, from swap to memory. * * 'o' is actually a redisVmPointer structure that will be freed by the call. * The return value is the loaded object. */ robj *vmLoadObject(robj *o) { /* If we are loading the object in background, stop it, we * need to load this object synchronously ASAP. */ if (o->storage == REDIS_VM_LOADING) vmCancelThreadedIOJob(o); return vmGenericLoadObject((vmpointer*)o,0); }
void decrRefCount(void *obj) { robj *o = obj; /* Object is a swapped out value, or in the process of being loaded. */ if (server.vm_enabled && (o->storage == REDIS_VM_SWAPPED || o->storage == REDIS_VM_LOADING)) { vmpointer *vp = obj; if (o->storage == REDIS_VM_LOADING) vmCancelThreadedIOJob(o); vmMarkPagesFree(vp->page,vp->usedpages); server.vm_stats_swapped_objects--; zfree(vp); return; } if (o->refcount <= 0) redisPanic("decrRefCount against refcount <= 0"); /* Object is in memory, or in the process of being swapped out. * * If the object is being swapped out, abort the operation on * decrRefCount even if the refcount does not drop to 0: the object * is referenced at least two times, as value of the key AND as * job->val in the iojob. So if we don't invalidate the iojob, when it is * done but the relevant key was removed in the meantime, the * complete jobs handler will not find the key about the job and the * assert will fail. */ if (server.vm_enabled && o->storage == REDIS_VM_SWAPPING) vmCancelThreadedIOJob(o); if (--(o->refcount) == 0) { switch(o->type) { case REDIS_STRING: freeStringObject(o); break; case REDIS_LIST: freeListObject(o); break; case REDIS_SET: freeSetObject(o); break; case REDIS_ZSET: freeZsetObject(o); break; case REDIS_HASH: freeHashObject(o); break; default: redisPanic("Unknown object type"); break; } o->ptr = NULL; /* defensive programming. We'll see NULL in traces. */ if (server.vm_enabled) pthread_mutex_lock(&server.obj_freelist_mutex); if (listLength(server.objfreelist) > REDIS_OBJFREELIST_MAX || !listAddNodeHead(server.objfreelist,o)) zfree(o); if (server.vm_enabled) pthread_mutex_unlock(&server.obj_freelist_mutex); } }
static void decrRefCount(void *obj) { robj *o = obj; /* Object is a key of a swapped out value, or in the process of being * loaded. */ if (server.vm_enabled && (o->storage == REDIS_VM_SWAPPED || o->storage == REDIS_VM_LOADING)) { if (o->storage == REDIS_VM_SWAPPED || o->storage == REDIS_VM_LOADING) { redisAssert(o->refcount == 1); } if (o->storage == REDIS_VM_LOADING) vmCancelThreadedIOJob(obj); redisAssert(o->type == REDIS_STRING); freeStringObject(o); vmMarkPagesFree(o->vm.page,o->vm.usedpages); pthread_mutex_lock(&server.obj_freelist_mutex); if (listLength(server.objfreelist) > REDIS_OBJFREELIST_MAX || !listAddNodeHead(server.objfreelist,o)) zfree(o); pthread_mutex_unlock(&server.obj_freelist_mutex); server.vm_stats_swapped_objects--; return; } /* Object is in memory, or in the process of being swapped out. */ if (--(o->refcount) == 0) { if (server.vm_enabled && o->storage == REDIS_VM_SWAPPING) vmCancelThreadedIOJob(obj); switch(o->type) { case REDIS_STRING: freeStringObject(o); break; case REDIS_LIST: freeListObject(o); break; case REDIS_SET: freeSetObject(o); break; case REDIS_ZSET: freeZsetObject(o); break; case REDIS_HASH: freeHashObject(o); break; default: redisAssert(0); break; } if (server.vm_enabled) pthread_mutex_lock(&server.obj_freelist_mutex); if (listLength(server.objfreelist) > REDIS_OBJFREELIST_MAX || !listAddNodeHead(server.objfreelist,o)) zfree(o); if (server.vm_enabled) pthread_mutex_unlock(&server.obj_freelist_mutex); } }
robj *lookupKey(redisDb *db, robj *key) { dictEntry *de = dictFind(db->dict,key->ptr); if (de) { robj *val = dictGetEntryVal(de); /* Update the access time for the aging algorithm. * Don't do it if we have a saving child, as this will trigger * a copy on write madness. */ if (server.bgsavechildpid == -1 && server.bgrewritechildpid == -1) val->lru = server.lruclock; if (server.vm_enabled) { if (val->storage == REDIS_VM_MEMORY || val->storage == REDIS_VM_SWAPPING) { /* If we were swapping the object out, cancel the operation */ if (val->storage == REDIS_VM_SWAPPING) vmCancelThreadedIOJob(val); } else { int notify = (val->storage == REDIS_VM_LOADING); /* Our value was swapped on disk. Bring it at home. */ redisAssert(val->type == REDIS_VMPOINTER); val = vmLoadObject(val); dictGetEntryVal(de) = val; /* Clients blocked by the VM subsystem may be waiting for * this key... */ if (notify) handleClientsBlockedOnSwappedKey(db,key); } } server.stat_keyspace_hits++; return val; } else { server.stat_keyspace_misses++; return NULL; } }
/* This function makes the clinet 'c' waiting for the key 'key' to be loaded. * If there is not already a job loading the key, it is craeted. * The key is added to the io_keys list in the client structure, and also * in the hash table mapping swapped keys to waiting clients, that is, * server.io_waited_keys. */ int waitForSwappedKey(redisClient *c, robj *key) { struct dictEntry *de; robj *o; list *l; /* If the key does not exist or is already in RAM we don't need to * block the client at all. */ de = dictFind(c->db->dict,key->ptr); if (de == NULL) return 0; o = dictGetEntryVal(de); if (o->storage == REDIS_VM_MEMORY) { return 0; } else if (o->storage == REDIS_VM_SWAPPING) { /* We were swapping the key, undo it! */ vmCancelThreadedIOJob(o); return 0; } /* OK: the key is either swapped, or being loaded just now. */ /* Add the key to the list of keys this client is waiting for. * This maps clients to keys they are waiting for. */ listAddNodeTail(c->io_keys,key); incrRefCount(key); /* Add the client to the swapped keys => clients waiting map. */ de = dictFind(c->db->io_keys,key); if (de == NULL) { int retval; /* For every key we take a list of clients blocked for it */ l = listCreate(); retval = dictAdd(c->db->io_keys,key,l); incrRefCount(key); redisAssert(retval == DICT_OK); } else { l = dictGetEntryVal(de); } listAddNodeTail(l,c); /* Are we already loading the key from disk? If not create a job */ if (o->storage == REDIS_VM_SWAPPED) { iojob *j; vmpointer *vp = (vmpointer*)o; o->storage = REDIS_VM_LOADING; j = zmalloc(sizeof(*j)); j->type = REDIS_IOJOB_LOAD; j->db = c->db; j->id = (robj*)vp; j->key = key; incrRefCount(key); j->page = vp->page; j->val = NULL; j->canceled = 0; j->thread = (pthread_t) -1; lockThreadedIO(); queueIOJob(j); unlockThreadedIO(); } return 1; }