Permalink
Browse files

Failure policy implementation.

Users can now customize how and when a node should fail after an error occures.

The default behavior is the FailImmediatelyPolicy whihc behaves exactly as the previous version of the cliend did: fail a node when an error occures.

Also included the ThrottlingFailurePolicy which only fails a node if N failures happen in T time. If there is at least T time between two failures, the node's state will not change. This is useful where the nodes are healthy but occasionally receive timeouts occur. (E.g. sometimes receiving large data but the client is configured to use an extremely small receive timeout value.)
  • Loading branch information...
1 parent 52d1562 commit 5e0dacc9f51f8d495c19eec3194d773d06714556 @enyim committed Aug 3, 2011
@@ -7,6 +7,40 @@ namespace Enyim.Caching.Configuration
{
public static class ConfigurationHelper
{
+ internal static bool TryGetAndRemove(Dictionary<string, string> dict, string name, out int value, bool required)
+ {
+ string tmp;
+ if (TryGetAndRemove(dict, name, out tmp, required)
+ && Int32.TryParse(tmp, out value))
+ {
+ return true;
+ }
+
+ if (required)
+ throw new System.Configuration.ConfigurationErrorsException("Missing or invalid parameter: " + (String.IsNullOrEmpty(name) ? "element content" : name));
+
+ value = 0;
+
+ return false;
+ }
+
+ internal static bool TryGetAndRemove(Dictionary<string, string> dict, string name, out TimeSpan value, bool required)
+ {
+ string tmp;
+ if (TryGetAndRemove(dict, name, out tmp, required)
+ && TimeSpan.TryParse(tmp, out value))
+ {
+ return true;
+ }
+
+ if (required)
+ throw new System.Configuration.ConfigurationErrorsException("Missing or invalid parameter: " + (String.IsNullOrEmpty(name) ? "element content" : name));
+
+ value = TimeSpan.Zero;
+
+ return false;
+ }
+
internal static bool TryGetAndRemove(Dictionary<string, string> dict, string name, out string value, bool required)
{
if (dict.TryGetValue(name, out value))
@@ -1,4 +1,5 @@
using System;
+using Enyim.Caching.Memcached;
namespace Enyim.Caching.Configuration
{
@@ -66,6 +67,8 @@ TimeSpan DeadTimeout
get;
set;
}
+
+ INodeFailurePolicyFactory FailurePolicyFactory { get; set; }
}
}
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
+using Enyim.Caching.Memcached;
namespace Enyim.Caching.Configuration
{
@@ -13,6 +14,7 @@ public class SocketPoolConfiguration : ISocketPoolConfiguration
private TimeSpan receiveTimeout = new TimeSpan(0, 0, 10);
private TimeSpan deadTimeout = new TimeSpan(0, 0, 10);
private TimeSpan queueTimeout = new TimeSpan(0, 0, 0, 0, 100);
+ private INodeFailurePolicyFactory policyFactory = new FailImmediatelyPolicyFactory();
int ISocketPoolConfiguration.MinPoolSize
{
@@ -93,6 +95,18 @@ TimeSpan ISocketPoolConfiguration.DeadTimeout
this.deadTimeout = value;
}
}
+
+ INodeFailurePolicyFactory ISocketPoolConfiguration.FailurePolicyFactory
+ {
+ get { return this.policyFactory; }
+ set
+ {
+ if (value == null)
+ throw new ArgumentNullException("value");
+
+ this.policyFactory = value;
+ }
+ }
}
}
@@ -1,6 +1,7 @@
using System;
using System.ComponentModel;
using System.Configuration;
+using Enyim.Caching.Memcached;
namespace Enyim.Caching.Configuration
{
@@ -87,6 +88,13 @@ protected override void PostDeserialize()
throw new ConfigurationErrorsException("maxPoolSize must be larger than minPoolSize.");
}
+ [ConfigurationProperty("failurePolicyFactory", IsRequired = false)]
+ public ProviderElement<INodeFailurePolicyFactory> FailurePolicyFactory
+ {
+ get { return (ProviderElement<INodeFailurePolicyFactory>)base["failurePolicyFactory"]; }
+ set { base["failurePolicyFactory"] = value; }
+ }
+
#region [ ISocketPoolConfiguration ]
int ISocketPoolConfiguration.MinPoolSize
@@ -113,6 +121,24 @@ TimeSpan ISocketPoolConfiguration.DeadTimeout
set { this.DeadTimeout = value; }
}
+ TimeSpan ISocketPoolConfiguration.QueueTimeout
+ {
+ get { return this.QueueTimeout; }
+ set { this.QueueTimeout = value; }
+ }
+
+ TimeSpan ISocketPoolConfiguration.ReceiveTimeout
+ {
+ get { return this.ReceiveTimeout; }
+ set { this.ReceiveTimeout = value; }
+ }
+
+ INodeFailurePolicyFactory ISocketPoolConfiguration.FailurePolicyFactory
+ {
+ get { return this.FailurePolicyFactory.CreateInstance() ?? new FailImmediatelyPolicyFactory(); }
+ set { }
+ }
+
#endregion
}
}
@@ -43,6 +43,9 @@
</ItemGroup>
<ItemGroup>
<Compile Include="Configuration\FactoryElement.cs" />
+ <Compile Include="Memcached\FailurePolicy\FailImmediatelyPolicy.cs" />
+ <Compile Include="Memcached\FailurePolicy\ThrottlingFailurePolicy.cs" />
+ <Compile Include="Memcached\FailurePolicy\INodeFailurePolicy.cs" />
<Compile Include="CountdownEvent.cs" />
<Compile Include="InterlockedStack.cs" />
<Compile Include="FastActivator.cs">
@@ -54,5 +54,7 @@ public interface IMemcachedClient : IDisposable
ServerStats Stats();
ServerStats Stats(string type);
+
+ event Action<IMemcachedNode> NodeFailed;
}
}
@@ -23,6 +23,7 @@ public class DefaultServerPool : IServerPool, IDisposable
private bool isTimerActive;
private long deadTimeoutMsec;
private bool isDisposed;
+ private event Action<IMemcachedNode> nodeFailed;
public DefaultServerPool(IMemcachedClientConfiguration configuration, IOperationFactory opFactory)
{
@@ -149,12 +150,15 @@ private void NodeFail(IMemcachedNode node)
return;
}
+ // bubble up the fail event to the client
+ var fail = this.nodeFailed;
+ if (fail != null)
+ fail(node);
+
// re-initialize the locator
- // TEST
var newLocator = this.configuration.CreateNodeLocator();
newLocator.Initialize(allNodes.Where(n => n.IsAlive).ToArray());
Interlocked.Exchange(ref this.nodeLocator, newLocator);
- // TEST
// the timer is stopped until we encounter the first dead server
// when we have one, we trigger it and it will run after DeadTimeout has elapsed
@@ -212,6 +216,12 @@ void IServerPool.Start()
this.nodeLocator = locator;
}
+ event Action<IMemcachedNode> IServerPool.NodeFailed
+ {
+ add { this.nodeFailed += value; }
+ remove { this.nodeFailed -= value; }
+ }
+
#endregion
#region [ IDisposable ]
@@ -0,0 +1,51 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Enyim.Caching.Memcached
+{
+ /// <summary>
+ /// Fails a node immediately when an error occures. This is the default policy.
+ /// </summary>
+ public sealed class FailImmediatelyPolicy : INodeFailurePolicy
+ {
+ bool INodeFailurePolicy.ShouldFail()
+ {
+ return true;
+ }
+ }
+
+ /// <summary>
+ /// Creates instances of <see cref="T:FailImmediatelyPolicy"/>.
+ /// </summary>
+ public class FailImmediatelyPolicyFactory : INodeFailurePolicyFactory
+ {
+ private static readonly INodeFailurePolicy PolicyInstance = new FailImmediatelyPolicy();
+
+ INodeFailurePolicy INodeFailurePolicyFactory.Create(IMemcachedNode node)
+ {
+ return PolicyInstance;
+ }
+ }
+}
+
+#region [ License information ]
+/* ************************************************************
+ *
+ * Copyright (c) 2011 Attila Kiskó, enyim.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * ************************************************************/
+#endregion
@@ -0,0 +1,37 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Enyim.Caching.Memcached
+{
+ public interface INodeFailurePolicy
+ {
+ bool ShouldFail();
+ }
+
+ public interface INodeFailurePolicyFactory
+ {
+ INodeFailurePolicy Create(IMemcachedNode node);
+ }
+}
+
+#region [ License information ]
+/* ************************************************************
+ *
+ * Copyright (c) 2011 Attila Kiskó, enyim.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * ************************************************************/
+#endregion
Oops, something went wrong.

0 comments on commit 5e0dacc

Please sign in to comment.