Skip to content
This repository

Make TryGet reliable #73

Closed
wants to merge 3 commits into from

3 participants

Vadim Chekan Attila Kiskó shamilio
Vadim Chekan

I would expect TryGet to never fail and return "false" either when cache miss or exception happen.
But it is not so. No exception is handled by TryGet.

This leads to "poisoned cache" situation. If I save in cache some value which throws an exception when deserializing, it will never be replaced by a good value in this client code (pseudocode):
if(!_cache.TryGet())
_cache.Set(GetValueExpensive())

Instead much more heavier approach needs to be taken with wrapping TryGet into exception and handling it.
Unfortunately "poisoned cache" situation happens not very often and very likely won't be detected in QA cycle. As result it encourage writing a code which is not reliable.

Proposed patch mitigates the issue by wrapping Try* methods implementation into try-catch clause.

Attila Kiskó
Owner

What kind of exception did you get? Some exceptions MUST be thrown, but it's possible that the exception you encountered was a bug coming from other parts of the code, this doing a generic catch is not a good solution.

Also, could you please reformat your code to match the rest of the files, so i do not have to commit a "cosmetic changes" patch?

(tabs, instead of spaces; follow indentation, etc.)

Attila Kiskó
Owner

I think it would make more sense that detect the private.key first and use it only if no other options are provided. (Move it to line 33)

Also, can you please follow the style of the rest of the code? " Exists('..\private.snk') "

shamilio

Hello, everybody.

How to distinguish whether the TryGet returns false because of absense of key in the cache or because of the communication failure?

TryGet should throws MemcachedClientException in case of communication failures.

Vadim Chekan

My particular problem was with a byte-based enum. Such a value successfully serializes but upon deserialization throws an error. As I think about it more, I start realizing that TryGet which never throws may be not the best idea because it would mask situation like mine and but would remain unnoticed for some time.
May be problem lays in associations: I expected it to behave like *.TryParse which would swallow exceptions otherwise thrown by Parse.

Attila Kiskó
Owner

Probably the serialization exceptions should escape from the transcoder which would handle your particular case as well.

I'll give it a try to see how it can be solved because it must be configurable to be backwards compatible with previous versions.

Vadim Chekan Enyim recommendations:
Private signature is used only if no other options are provided.
Style fix.
50f4bb8
Vadim Chekan

Fixed signature key according to your recommendations. Thanks for review!

Vadim Chekan

Will rework those series of patches into smaller ones.

Vadim Chekan vchekan closed this April 29, 2013
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Showing 3 unique commits by 1 author.

Jul 22, 2011
Vadim Chekan If a file "private.snk" is supplied, use it. 8109062
Vadim Chekan Made TryGet and TryGetWithCas never failing e1b4dbf
Aug 24, 2011
Vadim Chekan Enyim recommendations:
Private signature is used only if no other options are provided.
Style fix.
50f4bb8
This page is out of date. Refresh to see the latest.
49  Enyim.Caching/MemcachedClient.cs
@@ -102,9 +102,9 @@ private static IMemcachedClientConfiguration GetSection(string sectionName)
102 102
 		/// <returns>The retrieved item, or <value>null</value> if the key was not found.</returns>
103 103
 		public object Get(string key)
104 104
 		{
105  
-			object tmp;
106  
-
107  
-			return this.TryGet(key, out tmp) ? tmp : null;
  105
+			object tmp;
  106
+		    ulong cas;
  107
+			return this.PerformTryGet(key, out cas, out tmp) ? tmp : null;
108 108
 		}
109 109
 
110 110
 		/// <summary>
@@ -114,9 +114,9 @@ public object Get(string key)
114 114
 		/// <returns>The retrieved item, or <value>default(T)</value> if the key was not found.</returns>
115 115
 		public T Get<T>(string key)
116 116
 		{
117  
-			object tmp;
118  
-
119  
-			return TryGet(key, out tmp) ? (T)tmp : default(T);
  117
+			object tmp;
  118
+            ulong cas;
  119
+			return PerformTryGet(key, out cas, out tmp) ? (T)tmp : default(T);
120 120
 		}
121 121
 
122 122
 		/// <summary>
@@ -126,10 +126,18 @@ public T Get<T>(string key)
126 126
 		/// <param name="value">The retrieved item or null if not found.</param>
127 127
 		/// <returns>The <value>true</value> if the item was successfully retrieved.</returns>
128 128
 		public bool TryGet(string key, out object value)
129  
-		{
130  
-			ulong cas = 0;
131  
-
132  
-			return this.PerformTryGet(key, out cas, out value);
  129
+		{
  130
+		    try
  131
+            {
  132
+                ulong cas;
  133
+                return this.PerformTryGet(key, out cas, out value);
  134
+            }
  135
+            catch(Exception e)
  136
+            {
  137
+                log.Error("TryGet", e);
  138
+                value = null;
  139
+                return false;
  140
+            }
133 141
 		}
134 142
 
135 143
 		public CasResult<object> GetWithCas(string key)
@@ -149,13 +157,20 @@ public CasResult<T> GetWithCas<T>(string key)
149 157
 		public bool TryGetWithCas(string key, out CasResult<object> value)
150 158
 		{
151 159
 			object tmp;
152  
-			ulong cas;
153  
-
154  
-			var retval = this.PerformTryGet(key, out cas, out tmp);
155  
-
156  
-			value = new CasResult<object> { Cas = cas, Result = tmp };
157  
-
158  
-			return retval;
  160
+			ulong cas;
  161
+
  162
+            try
  163
+            {
  164
+                var retval = this.PerformTryGet(key, out cas, out tmp);
  165
+                value = new CasResult<object> {Cas = cas, Result = tmp};
  166
+                return retval;
  167
+            }
  168
+            catch (Exception e)
  169
+            {
  170
+                log.Error("TryGet", e);
  171
+                value = default(CasResult<object>);
  172
+                return false;
  173
+            }
159 174
 		}
160 175
 
161 176
 		protected virtual bool PerformTryGet(string key, out ulong cas, out object value)
6  build/CommonProperties.targets
@@ -30,6 +30,12 @@
30 30
 		<DelaySign>true</DelaySign>
31 31
 	</PropertyGroup>
32 32
 
  33
+	<!-- if private signature is supplied -->
  34
+	<PropertyGroup Condition=" Exists('..\private.snk') ">
  35
+		<AssemblyOriginatorKeyFile>..\private.snk</AssemblyOriginatorKeyFile>
  36
+		<DelaySign>false</DelaySign>
  37
+	</PropertyGroup>
  38
+
33 39
 	<!-- sign the assembly using the specified key file containing both the private and public keys -->
34 40
 	<PropertyGroup Condition=" '$(PrivateKeyPath)' != '' ">
35 41
 		<AssemblyOriginatorKeyFile>$(PrivateKeyPath)</AssemblyOriginatorKeyFile>
Commit_comment_tip

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.