A bug in MemCache distributed cache

Source: Internet
Author: User
Tags net hash

The Memcached distributed cache policy is not supported by the server. multiple servers do not know each other's existence. Distributed implementation is implemented by the client code (Memcached. clientLibrary) is implemented by caching key-server ing. The basic principle is to evaluate the hash value of the cache key and perform a modulo operation on the number of servers using the hash value, the key value is assigned to the server on which the modulo operation result is an index.

The core algorithm of Memcached. ClientLibrary for calculating the hashcode of the cache key is as follows:

1 /// <summary> 2 /// Returns appropriate SockIO object given 3 // string cache key and optional hashcode. 4 /// 5 /// Trys to get SockIO from pool. fails over 6 // to additional pools in event of server failure. 7 /// </summary> 8 /// <param name = "key"> hashcode for cache key </param> 9 /// <param name = "hashCode"> if not null, then the int hashcode to use </param> 10 // <returns> SockIO obj connecte D to server </returns> 11 public SockIO GetSock (string key, object hashCode) 12 {13 string hashCodeString = "<null>"; 14 if (hashCode! = Null) 15 hashCodeString = hashCode. toString (); 16 17 if (Log. isDebugEnabled) 18 {19 Log. debug (GetLocalizedString ("cache socket pick "). replace ("$ Key $", key ). replace ("$ HashCode $", hashCodeString); 20} 21 22 if (key = null | key. length = 0) 23 {24 if (Log. isDebugEnabled) 25 {26 Log. debug (GetLocalizedString ("null key"); 27} 28 return null; 29} 30 31 if (! _ Initialized) 32 {33 if (Log. isErrorEnabled) 34 {35 Log. error (GetLocalizedString ("get socket from uninitialized pool"); 36} 37 return null; 38} 39 40 // if no servers return null 41 if (_ buckets. count = 0) 42 return null; 43 44 // if only one server, return it 45 if (_ buckets. count = 1) 46 return GetConnection (string) _ buckets [0]); 47 48 int tries = 0; 49 50 // generate hashcode 51 int hv; 52 I F (hashCode! = Null) 53 {54 hv = (int) hashCode; 55} 56 else 57 {58 59 // NATIVE_HASH = 0 60 // OLD_COMPAT_HASH = 1 61 // NEW_COMPAT_HASH = 2 62 switch (_ hashingAlgorithm) 63 {64 case HashingAlgorithm. native: 65 hv = key. getHashCode (); 66 break; 67 68 case HashingAlgorithm. oldCompatibleHash: 69 hv = OriginalHashingAlgorithm (key); 70 break; 71 72 case HashingAlgorithm. newCompatibleHash: 73 hv = NewHashingAlgor Ithm (key); 74 break; 75 76 default: 77 // use the native hash as a default 78 hv = key. getHashCode (); 79 _ hashingAlgorithm = HashingAlgorithm. native; 80 break; 81} 82} 83 84 // keep trying different servers until we find one 85 while (tries ++ <= _ buckets. count) 86 {87 // get bucket using hashcode 88 // get one from factory 89 int bucket = hv % _ buckets. count; 90 if (bucket <0) 91 bucket + = _ buck Ets. count; 92 93 SockIO sock = GetConnection (string) _ buckets [bucket]); 94 95 if (Log. isDebugEnabled) 96 {97 Log. debug (GetLocalizedString ("cache choose "). replace ("$ Bucket $", _ buckets [bucket]. toString ()). replace ("$ Key $", key); 98} 9999 100 if (sock! = Null) 101 return sock; 102 103 // if we do not want to failover, then bail here104 if (! _ Failover) 105 return null; 106 107 // if we failed to get a socket from this server108 // then we try again by adding an incrementer to the109 // current key and then rehashing 110 switch (_ hashingAlgorithm) 111 {112 case HashingAlgorithm. native: 113 hv + = (string) ("" + tries + key )). getHashCode (); 114 break; 115 116 case HashingAlgorithm. oldCompatibleHash: 117 hv + = OriginalHashingAlgorithm ("" + tries + key); 118 break; 119 120 case HashingAlgorithm. newCompatibleHash: 121 hv + = NewHashingAlgorithm ("" + tries + key); 122 break; 123 124 default: 125 // use the native hash as a default126 hv + = (string) ("" + tries + key )). getHashCode (); 127 _ hashingAlgorithm = HashingAlgorithm. native; 128 break; 129} 130} 131 132 return null; 133}Obtain the core code of the server based on the cache key.

From the source code (-82 lines of code), we can find that there are three hashcode calculation algorithms:

(1) HashingAlgorithm. Native: uses the. NET hash algorithm, which is fast, but may not be compatible with other clients. For example, the cache needs to be shared with java and ruby clients;

(2) HashingAlgorithm. OldCompatibleHash: compatible with other clients, but slow;

(3) HashingAlgorithm. NewCompatibleHash: It can be compatible with other clients and is said to be fast.

Further analysis found that Memcached. by default, ClientLibrary calculates the hashcode of the cache key by HashingAlgorithm. native, while HashingAlgorithm. the hashcode calculation algorithm of Native is "hv = key. getHashCode () "is used. the GetHashCode () method that comes with the string type of the net class library.

The Bug is about to emerge, according to Microsoft's (http://msdn.microsoft.com/zh-cn/library/system.object.gethashcode.aspx) Interpretation of GetHashCode:. NET Framework does not guarantee the default implementation of the GetHashCode method, and the value this method returns may differ. NET Framework versions and platforms, such as 32-bit and 64-bit platforms. The GetHashCode () function of the string type does not guarantee that the hash value returned by the same string on different platforms is the same, so the problem arises. For the same cache key of different servers, the generated hashcode may be different. The data corresponding to the same key may be cached on different MemCache servers. Data Consistency cannot be guaranteed, and the code to clear the cache may also be invalid.

// 64-bit 4.0 [_ DynamicallyInvokable, ReliabilityContract (Consistency. willNotCorruptState, Cer. mayFail), SecuritySafeCritical] public unsafe override int GetHashCode () {if (HashHelpers. s_UseRandomizedStringHashing) {return string. internalMarvin32HashString (this, this. length, 0L);} IntPtr arg_25_0; IntPtr expr_1C = arg_25_0 = this; if (expr_1C! = 0) {arg_25_0 = (IntPtr) (int) expr_1C + RuntimeHelpers. offsetToStringData);} char * ptr = arg_25_0; int num = 5381; int num2 = num; char * ptr2 = ptr; int num3; while (num3 = (int) (* (ushort *) ptr2 ))! = 0) {num = (num <5) + num ^ num3); num3 = (int) (* (ushort *) (ptr2 + (IntPtr) 2/2 )); if (num3 = 0) {break;} num2 = (num2 <5) + num2 ^ num3); ptr2 + = (IntPtr) 4/2 ;} return num + num2 * 1566083941;} // 64-bit 2.0 // string [ReliabilityContract (Consistency. willNotCorruptState, Cer. mayFail)] public unsafe override int GetHashCode () {IntPtr arg_0F_0; IntPtr expr_06 = arg_0F_0 = this; if (expr_06! = 0) {arg_0F_0 = (IntPtr) (int) expr_06 + RuntimeHelpers. offsetToStringData);} char * ptr = arg_0F_0; int num = 5381; int num2 = num; char * ptr2 = ptr; int num3; while (num3 = (int) (* (ushort *) ptr2 ))! = 0) {num = (num <5) + num ^ num3); num3 = (int) (* (ushort *) (ptr2 + (IntPtr) 2/2 )); if (num3 = 0) {break;} num2 = (num2 <5) + num2 ^ num3); ptr2 + = (IntPtr) 4/2 ;} return num + num2 * 1566083941;} // 32-bit 4.0 [_ DynamicallyInvokable, ReliabilityContract (Consistency. willNotCorruptState, Cer. mayFail), SecuritySafeCritical] public unsafe override int GetHashCode () {if (HashHelpers. s_UseRandomize DStringHashing) {return string. InternalMarvin32HashString (this, this. Length, 0L);} IntPtr arg_25_0; IntPtr expr_1C = arg_25_0 = this; if (expr_1C! = 0) {arg_25_0 = (IntPtr) (int) expr_1C + RuntimeHelpers. offsetToStringData);} char * ptr = arg_25_0; int num = 352654597; int num2 = num; int * ptr2 = (int *) ptr; int I; for (I = this. length; I> 2; I-= 4) {num = (num <5) + num + (num> 27) ^ * ptr2 ); num2 = (num2 <5) + num2 + (num2> 27) ^ ptr2 [(IntPtr) 4/4]); ptr2 + = (IntPtr) 8/4 ;} if (I> 0) {num = (num <5) + num + (num> 27) ^ * ptr2) ;}return num + num2 * 1566083941 ;}Implementation Code of several versions of GetHashCode

To solve the problem, do not use the default MemCache hash algorithm. There are two implementation methods:

(1) When initializing the MemCache server, specify it as MemCahce with other hash algorithms. The code is "this. pool. HashingAlgorithm = HashingAlgorithm. OldCompatibleHash ;".

(2) Use a custom hash algorithm to transmit the hash value when you call methods such as set (), get (), and delete (). These methods include parameters that pass the hashcode overload.

 

Reference: analyzes how the Memcached client distributes cached data to multiple servers and memcached client-memcacheddotnet (Memcached. clientLibrary) 1.1.5. distributed implementation of memcache and Object. the GetHashCode method and the possibility of key-based HashCode.

 

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.