int createContinuum( proxyContext *p, redisAddr *addr, redisContext **c, int cont ) { float pct = ((float)1/p->max_count); unsigned int ks = floorf( pct * 40.0 * (float)p->max_count ); for( unsigned int k = 0; k < ks; k++ ) { /* 40 hashes, 4 numbers per hash = 160 points per server */ char ss[128]; unsigned char digest[16]; sprintf( ss, "%s:%d-%d", addr->ip, addr->port, k ); ketama_md5_digest( ss, digest ); /* Use successive 4-bytes from hash as numbers * for the points on the circle: */ for( int h = 0; h < 4; h++ ) { p->mcs[cont].point = ( digest[3+h*4] << 24 ) | ( digest[2+h*4] << 16 ) | ( digest[1+h*4] << 8 ) | digest[h*4]; p->mcs[cont].c = c; p->mcs[cont].ip = addr->ip; p->mcs[cont].port = addr->port; cont++; } } return cont; }
static int lketama_md5digest(lua_State *L){ const char *str = luaL_checkstring(L, 2); unsigned char md5pword[16]; ketama_md5_digest((char*)str, md5pword); lua_pushfstring(L, "%p", md5pword); return 1; }
unsigned int ketama_hashi( const char* key) { unsigned char digest[16]; ketama_md5_digest( key, digest ); return (unsigned int)(( digest[3] << 24 ) | ( digest[2] << 16 ) | ( digest[1] << 8 ) | digest[0] ); }
unsigned int ketama_hashi( char* inString ) { unsigned char digest[16]; ketama_md5_digest( inString, digest ); return (unsigned int)(( digest[3] << 24 ) | ( digest[2] << 16 ) | ( digest[1] << 8 ) | digest[0] ); }
unsigned int ketama_hashi( char* inString ) { #if defined(ENABLE_FNV_HASH) return fnv_32a_str( inString, FNV1_32_INIT ); #else unsigned char digest[16]; ketama_md5_digest( inString, digest ); return (unsigned int)(( digest[3] << 24 ) | ( digest[2] << 16 ) | ( digest[1] << 8 ) | digest[0] ); #endif }
/** \brief Generates the continuum of servers (each server as many points on a circle). * \param key Shared memory key for storing the newly created continuum. * \param filename Server definition file, which will be parsed to create this continuum. * \return 0 on failure, 1 on success. */ static int ketama_create_continuum( key_t key, char* filename ) { int shmid; int* data; /* Pointer to shmem location */ unsigned int numservers = 0; unsigned long memory; serverinfo* slist; slist = read_server_definitions( filename, &numservers, &memory ); /* Check numservers first; if it is zero then there is no error message * and we need to set one. */ if ( numservers < 1 ) { sprintf( k_error, "No valid server definitions in file %s", filename ); return 0; } else if ( slist == 0 ) { /* read_server_definitions must've set error message. */ return 0; } #ifdef DEBUG syslog( LOG_INFO, "Server definitions read: %u servers, total memory: %lu.\n", numservers, memory ); #endif /* Continuum will hold one mcs for each point on the circle: */ mcs continuum[ numservers * 160 ]; unsigned int i, k, cont = 0; for( i = 0; i < numservers; i++ ) { float pct = (float)slist[i].memory / (float)memory; unsigned int ks = floorf( pct * 40.0 * (float)numservers ); #ifdef DEBUG int hpct = floorf( pct * 100.0 ); syslog( LOG_INFO, "Server no. %d: %s (mem: %lu = %u%% or %d of %d)\n", i, slist[i].addr, slist[i].memory, hpct, ks, numservers * 40 ); #endif for( k = 0; k < ks; k++ ) { /* 40 hashes, 4 numbers per hash = 160 points per server */ char ss[30]; unsigned char digest[16]; sprintf( ss, "%s-%d", slist[i].addr, k ); ketama_md5_digest( ss, digest ); /* Use successive 4-bytes from hash as numbers * for the points on the circle: */ int h; for( h = 0; h < 4; h++ ) { continuum[cont].point = ( digest[3+h*4] << 24 ) | ( digest[2+h*4] << 16 ) | ( digest[1+h*4] << 8 ) | digest[h*4]; memcpy( continuum[cont].ip, slist[i].addr, 22 ); cont++; } } } free( slist ); /* Sorts in ascending order of "point" */ qsort( (void*) &continuum, cont, sizeof( mcs ), (compfn)ketama_compare ); /* Add data to shmmem */ shmid = shmget( key, MC_SHMSIZE, 0644 | IPC_CREAT ); data = shmat( shmid, (void *)0, 0 ); if ( data == (void *)(-1) ) { strcpy( k_error, "Can't open shmmem for writing." ); return 0; } time_t modtime = file_modtime( filename ); int nump = cont; memcpy( data, &nump, sizeof( int ) ); memcpy( data + 1, &modtime, sizeof( time_t ) ); memcpy( data + 1 + sizeof( void* ), &continuum, sizeof( mcs ) * nump ); /* We detatch here because we will re-attach in read-only * mode to actually use it. */ if ( shmdt( data ) == -1 ) strcpy( k_error, "Error detatching from shared memory!" ); return 1; }
/** \brief Adds a specified server to the continuum that has just been added to the server list * \param addr The address of the server that you want to add points for. * \param newmemory The amount of allocated memory from the new server to be added to the cluster * \param cont Pointer to the continuum which we will refresh. * \param key Shared memory key for storing the newly created continuum. * \param slist The address of the list of servers that your building the continuum from. * \param numservers Number of servers available * \param memory Amount of memory available accross all servers * \param fmodtime File modtime * \return 0 on failure, 1 on success. */ int add_server_to_continuum(char* addr, unsigned long newmemory, continuum* cont, key_t key, serverinfo* slist, int numservers, unsigned long memory, time_t fmodtime) { int maxpoints = POINTS_PER_SERVER * numservers; // maximum number of ring points (HASH_COUNT * POINTS_PER_HASH * numservers) int numoldpoints = cont->numpoints; // total number of points in the ring prior to adding the server // Continuum will hold one mcs for each point on the circle: mcs *ring = (mcs*) malloc( maxpoints * sizeof(mcs) ); int i, k, indx = -1, numpoints = 0; // determine the number of hashes to create float ratio = (float)newmemory / (float)memory; // ratio of hashes for this server to total hashes int numhashes = floorf( ratio * HASH_COUNT * (float)numservers ); // number of hashes for this server //given unevenly distributed memory we need to limit number of hashes int max_hashcount = POINTS_PER_SERVER / POINTS_PER_HASH; if (numhashes > max_hashcount) { numhashes = max_hashcount; } #ifdef DEBUG int percent = floorf( ratio * 100.0 ); // percent of total hashes linked to this sever syslog( LOG_INFO, "Ketama: Server no. %d: %s (mem: %lu = %u%% or %d of %d)\n", i, addr, newmemory, percent, numhashes * POINTS_PER_HASH, HASH_COUNT * numservers * POINTS_PER_HASH ); #endif #if defined(ENABLE_FNV_HASH) Fnv32_t hval = FNV1_32_INIT; #endif // create the points on the ring for the new server for( k = 0; k < numhashes; k++ ) { #if defined(ENABLE_FNV_HASH) hval = fnv_32a_str(addr, hval); ring[numpoints].point = hval; snprintf( ring[numpoints].ip, sizeof(ring[numpoints].ip), "%s", addr); numpoints++; if (numpoints > maxpoints) { snprintf( k_error, sizeof(k_error), "Ketama: add_server_to_continuum tried to exceed mcs array bounds.\n" ); syslog1( LOG_INFO, k_error ); free(ring); free(slist); return 0; } #else char ss[30]; unsigned char digest[16]; snprintf( ss, sizeof(ss), "%s-%d", addr, k ); ketama_md5_digest( ss, digest ); // Use successive 4-bytes from hash as numbers for the points on the circle: int h; for( h = 0; h < POINTS_PER_HASH; h++ ) { ring[numpoints].point = ( digest[3+h*4] << 24 ) | ( digest[2+h*4] << 16 ) | ( digest[1+h*4] << 8 ) | digest[h*4]; snprintf( ring[numpoints].ip, sizeof(ring[numpoints].ip), "%s", addr); numpoints++; if (numpoints > maxpoints) { snprintf( k_error, sizeof(k_error), "Ketama: add_server_to_continuum tried to exceed mcs array bounds.\n" ); syslog1( LOG_INFO, k_error ); free(ring); free(slist); return 0; } } #endif } // append the points on the current ring (will be sorted in the reconstruction phase) for( i = 0; i < numoldpoints; i++ ) { memcpy( &ring[numpoints], &cont->array[i], sizeof(mcs) ); numpoints++; if (numpoints > maxpoints) { snprintf( k_error, sizeof(k_error), "Ketama: add_server_to_continuum tried to exceed mcs array bounds.\n" ); syslog1( LOG_INFO, k_error ); free(ring); free(slist); return 0; } } // reconstruct the continuum return reconstruct_continuum(key, slist, numservers, ring, numpoints, memory, fmodtime); }
/** \brief Loads the continuum of servers (each server as many points on a circle). * \param key Shared memory key for storing the newly created continuum. * \param slist The address of the list of servers that your building the continuum from. * \param numservers Number of servers available * \param memory Amount of memory available accross all servers * \param fmodtime File modtime * \return 0 on failure, 1 on success. */ int load_continuum(key_t key, serverinfo* slist, int numservers, unsigned long memory, time_t fmodtime) { int maxpoints = POINTS_PER_SERVER * numservers; // maximum number of ring points (HASH_COUNT * POINTS_PER_HASH * numservers) // Continuum will hold one mcs for each point on the circle: mcs *ring = (mcs*) malloc( maxpoints * sizeof(mcs) ); int i, k, numpoints = 0; // buildup the continuum ring for( i = 0; i < numservers; i++ ) { float ratio = (float) slist[i].memory / (float)memory; // ratio of hashes for this server to total hashes int numhashes = floorf( ratio * HASH_COUNT * (float)numservers ); // number of hashes for this server #ifdef DEBUG int percent = floorf( ratio * 100.0 ); // percent of total hashes linked to this sever syslog( LOG_INFO, "Ketama: Server no. %d: %s (mem: %lu = %u%% or %d of %d)\n", i, slist[i].addr, slist[i].memory, percent, numhashes * POINTS_PER_HASH, HASH_COUNT * numservers * POINTS_PER_HASH ); #endif #if defined(ENABLE_FNV_HASH) Fnv32_t hval = FNV1_32_INIT; #endif // create the points on the ring for this server for( k = 0; k < numhashes; k++ ) { #if defined(ENABLE_FNV_HASH) hval = fnv_32a_str(slist[i].addr, hval); ring[numpoints].point = hval; snprintf( ring[numpoints].ip, sizeof(ring[numpoints].ip), "%s", slist[i].addr); numpoints++; #else char ss[30]; unsigned char digest[16]; snprintf( ss, sizeof(ss), "%s-%d", slist[i].addr, k ); ketama_md5_digest( ss, digest ); // Use successive 4-bytes from hash as numbers for the points on the circle: int h; for( h = 0; h < POINTS_PER_HASH; h++ ) { ring[numpoints].point = ( digest[3+h*4] << 24 ) | ( digest[2+h*4] << 16 ) | ( digest[1+h*4] << 8 ) | digest[h*4]; snprintf( ring[numpoints].ip, sizeof(ring[numpoints].ip), "%s", slist[i].addr); numpoints++; if (numpoints > maxpoints) { snprintf( k_error, sizeof(k_error), "Ketama: load_continuum tried to exceed mcs array bounds.\n" ); syslog1( LOG_INFO, k_error ); free(ring); free(slist); return 0; } } #endif } } return reconstruct_continuum(key, slist, numservers, ring, numpoints, memory, fmodtime); }