Skip to content
Browse files

MemcachedClient performance monitoring support.

  • Loading branch information...
1 parent b67091a commit a0c5dd99654876c723c34d7c82f3636332f26de8 @enyim committed Dec 13, 2010
View
6 Enyim.Caching/Changes.mdown
@@ -1,5 +1,11 @@
# Enyim Memcached Version History
+## Version 2.8-indev
+
+ * Performance monitoring support. The included DefaultPerformanceMonitor will use Windows performance counters. See [the wiki](http://github.com/enyim/EnyimMemcached/wiki/Configure-the-Performance-Monitor) about configuring this feature.
+ * Fixed a race condition in the InterlockedQueue. See [Bug #36](https://github.com/enyim/EnyimMemcached/issues#issue/36). Thanks to [danp60](https://github.com/danp60).
+ * Now it's possible to change the timeout for acquiring a socket from a full pool. This can help reduce the impact of a dead node on yor application. See the [memached/socketPool](https://github.com/enyim/EnyimMemcached/wiki/MemcachedClient-Configuration) element on the wiki.
+
## Version 2.7
* Removed log4net from the project and made the log provider configurable. See http://github.com/enyim/EnyimMemcached/wiki/Configure-Logging
View
98 Enyim.Caching/Configuration/FactoryElement.cs
@@ -0,0 +1,98 @@
+using System;
+using System.ComponentModel;
+using System.Configuration;
+using System.Collections.Generic;
+using Enyim.Caching.Memcached;
+using Enyim.Reflection;
+using System.Xml.Linq;
+
+namespace Enyim.Caching.Configuration
+{
+ /// <summary>
+ /// This element is used to define locator/transcoder/keyTransformer instances. It also provides custom initializations for them using a factory.
+ /// </summary>
+ /// <typeparam name="TFactory"></typeparam>
+ public class FactoryElement<TFactory> : ConfigurationElement
+ where TFactory : class, IProvider
+ {
+ protected readonly Dictionary<string, string> Parameters = new Dictionary<string, string>();
+ private TFactory instance;
+
+ protected virtual bool IsOptional { get { return false; } }
+
+ /// <summary>
+ /// Gets or sets the type of the factory.
+ /// </summary>
+ [ConfigurationProperty("factory"), TypeConverter(typeof(TypeNameConverter))]
+ public Type Factory
+ {
+ get { return (Type)base["factory"]; }
+ set { base["factory"] = value; }
+ }
+
+ protected override bool OnDeserializeUnrecognizedAttribute(string name, string value)
+ {
+ ConfigurationProperty property = new ConfigurationProperty(name, typeof(string), value);
+ base[property] = value;
+
+ this.Parameters[name] = value;
+
+ return true;
+ }
+
+ /// <summary>
+ /// Creates the provider by using the factory (if present) or directly instantiating by type name
+ /// </summary>
+ /// <returns></returns>
+ public TFactory CreateInstance()
+ {
+ //check if we have a factory
+ if (this.instance == null)
+ {
+ var type = this.Factory;
+ if (type == null)
+ {
+ if (this.IsOptional)
+ return null;
+
+ throw new ConfigurationErrorsException("factory must be defined");
+ }
+
+ this.instance = (TFactory)FastActivator.Create(type);
+ this.instance.Initialize(this.Parameters);
+ }
+
+ return this.instance;
+ }
+ }
+
+ public class OptionalFactoryElement<TResult> : FactoryElement<TResult>
+ where TResult : class, IProvider
+ {
+ protected override bool IsOptional
+ {
+ get { return true; }
+ }
+ }
+
+}
+
+#region [ License information ]
+/* ************************************************************
+ *
+ * Copyright (c) 2010 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
View
2 Enyim.Caching/Configuration/IMemcachedClientConfiguration.cs
@@ -41,6 +41,8 @@ public interface IMemcachedClientConfiguration
ITranscoder CreateTranscoder();
IServerPool CreatePool();
+
+ IPerformanceMonitor CreatePerformanceMonitor();
}
}
View
10 Enyim.Caching/Configuration/MemcachedClientConfiguration.cs
@@ -102,6 +102,11 @@ public ITranscoder Transcoder
}
/// <summary>
+ /// Gets or sets the <see cref="T:Enyim.Caching.Memcached.IPerformanceMonitor"/> instance which will be used monitor the performance of the client.
+ /// </summary>
+ public IPerformanceMonitor PerformanceMonitor { get; set; }
+
+ /// <summary>
/// Gets or sets the type of the communication between client and server.
/// </summary>
public MemcachedProtocol Protocol { get; set; }
@@ -154,6 +159,11 @@ IServerPool IMemcachedClientConfiguration.CreatePool()
throw new ArgumentOutOfRangeException("Unknown protocol: " + (int)this.Protocol);
}
+ IPerformanceMonitor IMemcachedClientConfiguration.CreatePerformanceMonitor()
+ {
+ return this.PerformanceMonitor;
+ }
+
#endregion
}
}
View
16 Enyim.Caching/Configuration/MemcachedClientSection.cs
@@ -74,6 +74,16 @@ public ProviderElement<ITranscoder> Transcoder
}
/// <summary>
+ /// Gets or sets the <see cref="T:Enyim.Caching.Memcached.IPerformanceMonitor"/> which will be used monitor the performance of the client.
+ /// </summary>
+ [ConfigurationProperty("performanceMonitor", IsRequired = false)]
+ public ProviderElement<IPerformanceMonitor> PerformanceMonitor
+ {
+ get { return (ProviderElement<IPerformanceMonitor>)base["performanceMonitor"]; }
+ set { base["performanceMonitor"] = value; }
+ }
+
+ /// <summary>
/// Called after deserialization.
/// </summary>
protected override void PostDeserialize()
@@ -97,6 +107,7 @@ public MemcachedProtocol Protocol
}
#region [ IMemcachedClientConfiguration]
+
IList<IPEndPoint> IMemcachedClientConfiguration.Servers
{
get { return this.Servers.ToIPEndPointCollection(); }
@@ -138,6 +149,11 @@ IServerPool IMemcachedClientConfiguration.CreatePool()
throw new ArgumentOutOfRangeException("Unknown protocol: " + (int)this.Protocol);
}
+ IPerformanceMonitor IMemcachedClientConfiguration.CreatePerformanceMonitor()
+ {
+ return this.PerformanceMonitor.CreateInstance();
+ }
+
#endregion
}
}
View
2 Enyim.Caching/Configuration/ProviderElement.cs
@@ -70,7 +70,7 @@ public T CreateInstance()
var type = this.Factory;
if (type != null)
{
- this.factoryInstance = (IProviderFactory<T>)Activator.CreateInstance(type);
+ this.factoryInstance = (IProviderFactory<T>)FastActivator.Create(type);
this.factoryInstance.Initialize(this.parameters);
}
}
View
100 Enyim.Caching/Enyim.Caching.csproj
@@ -40,62 +40,45 @@
</Reference>
</ItemGroup>
<ItemGroup>
+ <Compile Include="Configuration\FactoryElement.cs" />
<Compile Include="FastActivator.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Logging\LogManager.cs" />
<Compile Include="Configuration\AuthenticationConfiguration.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Configuration\AuthenticationElement.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Configuration\LoggerSection.cs" />
<Compile Include="Configuration\ConfigurationElementException.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Configuration\ConfigurationHelper.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Configuration\EndPointElement.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Configuration\EndPointElementCollection.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Configuration\IAuthenticationConfiguration.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Configuration\IMemcachedClientConfiguration.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Configuration\InterfaceValidator.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Configuration\ISocketPoolConfiguration.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Configuration\IVBucketConfiguration.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Configuration\JsonVBucketConfig.cs" />
<Compile Include="Configuration\MemcachedClientConfiguration.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Configuration\MemcachedClientSection.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Configuration\ProviderElement.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Configuration\SocketPoolConfiguration.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Configuration\SocketPoolElement.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Configuration\TextElement.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="HashkitCrc32.cs" />
<Compile Include="HashkitMurmur.cs" />
@@ -111,226 +94,163 @@
<Compile Include="Logging\ILog.cs" />
<Compile Include="MemcachedClient.cs" />
<Compile Include="Memcached\Authentication\PlainTextAuthenticator.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\CommandNotSupportedException.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\DefaultServerPool.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\Enums.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\IAuthenticator.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\IMemcachedKeyTransformer.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\IMemcachedNode.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\IMemcachedNodeLocator.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\IOperationFactory.cs">
- <SubType>Code</SubType>
+ </Compile>
+ <Compile Include="Memcached\PerformanceMonitor\DefaultPerformanceMonitorFactory.cs" />
+ <Compile Include="Memcached\PerformanceMonitor\IPerformanceMonitor.cs" />
+ <Compile Include="Memcached\PerformanceMonitor\PerformanceCounterInstaller.cs">
+ <SubType>Component</SubType>
+ </Compile>
+ <Compile Include="Memcached\PerformanceMonitor\DefaultPerformanceMonitor.cs">
</Compile>
<Compile Include="Memcached\IProviderFactory.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\ISaslAuthenticationProvider.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\IServerPool.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\KeyTransformers\Base64KeyTransformer.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\KeyTransformers\DefaultKeyTransformer.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\KeyTransformers\KeyTransformerBase.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\KeyTransformers\SHA1KeyTransformer.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\KeyTransformers\TigerHashKeyTransformer.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\Locators\KetamaNodeLocatorFactory.cs" />
<Compile Include="Memcached\Locators\DefaultNodeLocator.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\Locators\KetamaNodeLocator.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\Locators\SingleNodeLocator.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\Locators\VBucketNodeLocator.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\Locators\VBucketNodeLocatorFactory.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\MemcachedClientException.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\MemcachedException.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\MemcachedNode.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\OperationInterfaces.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\PooledSocket.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\Protocol\Binary\BinaryConverter.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\Protocol\Binary\BinaryMultiItemOperation.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\Protocol\Binary\BinaryNode.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\Protocol\Binary\BinaryOperation.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\Protocol\Binary\BinaryOperationFactory.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\Protocol\Binary\BinaryPool.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\Protocol\Binary\BinaryRequest.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\Protocol\Binary\BinaryResponse.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\Protocol\Binary\BinarySingleItemOperation.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\Protocol\Binary\ConcatOperation.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\Protocol\Binary\DeleteOperation.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\Protocol\Binary\FlushOperation.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\Protocol\Binary\GetOperation.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\Protocol\Binary\MultiGetOperation.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\Protocol\Binary\MutatorOperation.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\Protocol\Binary\OpCode.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\Protocol\Binary\SaslContinue.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\Protocol\Binary\SaslStart.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\Protocol\Binary\SaslStep.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\Protocol\Binary\StatsOperation.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\Protocol\Binary\StoreOperation.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\Protocol\ItemOperation.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\Protocol\MultiItemOperation.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\Protocol\Operation.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\Protocol\Text\ConcateOperation.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\Protocol\Text\CasOperation.cs" />
<Compile Include="Memcached\Protocol\Text\DeleteOperation.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\Protocol\Text\FlushOperation.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\Protocol\Text\GetHelper.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\Protocol\Text\GetOperation.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\Protocol\Text\MultiGetOperation.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\Protocol\Text\MutatorOperation.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\Protocol\Text\StatsOperation.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\Protocol\Text\StoreOperation.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\Protocol\Text\StoreOperationBase.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\Protocol\Text\TextOperationFactory.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\Protocol\Text\TextSocketHelper.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\ServerStats.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\StatItem.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\StoreMode.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\ThrowHelper.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\Transcoders\CacheItem.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\Transcoders\DefaultTranscoder.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Memcached\Transcoders\ITranscoder.cs">
- <SubType>Code</SubType>
</Compile>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="TigerHash.cs" />
<Compile Include="UIntEqualityComparer.cs" />
</ItemGroup>
<ItemGroup>
- <None Include="Demo.config">
+ <None Include="Changes.mdown">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
- <None Include="Changes.mdown">
+ <None Include="Demo.config">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
View
8 Enyim.Caching/Memcached/IProviderFactory.cs
@@ -6,11 +6,15 @@ namespace Enyim.Caching.Memcached
/// Provides a way for custom initalization of the providers (locators, transcoders, key transformers)
/// </summary>
/// <typeparam name="T"></typeparam>
- public interface IProviderFactory<T>
+ public interface IProviderFactory<T> : IProvider
{
- void Initialize(Dictionary<string, string> parameters);
T Create();
}
+
+ public interface IProvider
+ {
+ void Initialize(Dictionary<string, string> parameters);
+ }
}
#region [ License information ]
View
2 Enyim.Caching/Memcached/Locators/KetamaNodeLocatorFactory.cs
@@ -15,7 +15,7 @@ public class KetamaNodeLocatorFactory : IProviderFactory<IMemcachedNodeLocator>
{
private string hashName;
- void IProviderFactory<IMemcachedNodeLocator>.Initialize(Dictionary<string, string> parameters)
+ void IProvider.Initialize(Dictionary<string, string> parameters)
{
ConfigurationHelper.TryGetAndRemove(parameters, "hashName", out this.hashName, false);
}
View
2 Enyim.Caching/Memcached/Locators/VBucketNodeLocatorFactory.cs
@@ -26,7 +26,7 @@ public class VBucketNodeLocatorFactory : IProviderFactory<IMemcachedNodeLocator>
private string hashAlgo;
private VBucket[] buckets;
- void IProviderFactory<IMemcachedNodeLocator>.Initialize(Dictionary<string, string> parameters)
+ void IProvider.Initialize(Dictionary<string, string> parameters)
{
ConfigurationHelper.TryGetAndRemove(parameters, "hashAlgorithm", out this.hashAlgo, true);
View
255 Enyim.Caching/Memcached/PerformanceMonitor/DefaultPerformanceMonitor.cs
@@ -0,0 +1,255 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Diagnostics;
+
+namespace Enyim.Caching.Memcached
+{
+ public class DefaultPerformanceMonitor : IPerformanceMonitor
+ {
+ private OpMonitor pcGet;
+ private OpMonitor pcSet;
+ private OpMonitor pcAdd;
+ private OpMonitor pcReplace;
+ private OpMonitor pcDelete;
+ private OpMonitor pcIncrement;
+ private OpMonitor pcDecrement;
+ private OpMonitor pcAppend;
+ private OpMonitor pcPrepend;
+
+ public DefaultPerformanceMonitor(string instance)
+ {
+ this.pcGet = new OpMonitor(instance, DefaultPerformanceMonitor.CategoryName, DefaultPerformanceMonitor.Names.Get);
+ this.pcSet = new OpMonitor(instance, DefaultPerformanceMonitor.CategoryName, DefaultPerformanceMonitor.Names.Set);
+ this.pcAdd = new OpMonitor(instance, DefaultPerformanceMonitor.CategoryName, DefaultPerformanceMonitor.Names.Add);
+ this.pcReplace = new OpMonitor(instance, DefaultPerformanceMonitor.CategoryName, DefaultPerformanceMonitor.Names.Replace);
+ this.pcDelete = new OpMonitor(instance, DefaultPerformanceMonitor.CategoryName, DefaultPerformanceMonitor.Names.Delete);
+ this.pcIncrement = new OpMonitor(instance, DefaultPerformanceMonitor.CategoryName, DefaultPerformanceMonitor.Names.Increment);
+ this.pcDecrement = new OpMonitor(instance, DefaultPerformanceMonitor.CategoryName, DefaultPerformanceMonitor.Names.Decrement);
+ this.pcAppend = new OpMonitor(instance, DefaultPerformanceMonitor.CategoryName, DefaultPerformanceMonitor.Names.Append);
+ this.pcPrepend = new OpMonitor(instance, DefaultPerformanceMonitor.CategoryName, DefaultPerformanceMonitor.Names.Prepend);
+ }
+
+ #region [ IDisposable ]
+
+ ~DefaultPerformanceMonitor()
+ {
+ this.Dispose();
+ }
+
+ public void Dispose()
+ {
+ ((IDisposable)this).Dispose();
+ }
+
+ void IDisposable.Dispose()
+ {
+ GC.SuppressFinalize(this);
+
+ if (this.pcGet != null)
+ {
+ this.pcGet.Dispose();
+ this.pcSet.Dispose();
+ this.pcAdd.Dispose();
+ this.pcReplace.Dispose();
+ this.pcDelete.Dispose();
+ this.pcIncrement.Dispose();
+ this.pcDecrement.Dispose();
+ this.pcAppend.Dispose();
+ this.pcPrepend.Dispose();
+
+ this.pcGet = null;
+ this.pcSet = null;
+ this.pcAdd = null;
+ this.pcReplace = null;
+ this.pcDelete = null;
+ this.pcIncrement = null;
+ this.pcDecrement = null;
+ this.pcAppend = null;
+ this.pcPrepend = null;
+ }
+ }
+
+ #endregion
+ #region [ consts ]
+
+ public const string CategoryName = "Enyim.Caching.Memcached";
+
+ internal static class Names
+ {
+ public const string Get = "Get";
+ public const string Set = "Set";
+ public const string Add = "Add";
+ public const string Replace = "Replace";
+ public const string Delete = "Delete";
+ public const string Increment = "Increment";
+ public const string Decrement = "Decrement";
+ public const string Append = "Append";
+ public const string Prepend = "Prepend";
+ }
+
+ #endregion
+ #region [ IPerformanceMonitor ]
+
+ void IPerformanceMonitor.Get(int amount, bool success)
+ {
+ this.pcGet.Increment(amount, success);
+ }
+
+ void IPerformanceMonitor.Store(StoreMode mode, int amount, bool success)
+ {
+ switch (mode)
+ {
+ case StoreMode.Add:
+ this.pcAdd.Increment(amount, success);
+ break;
+
+ case StoreMode.Replace:
+ this.pcReplace.Increment(amount, success);
+ break;
+
+ case StoreMode.Set:
+ this.pcSet.Increment(amount, success);
+ break;
+ }
+ }
+
+ void IPerformanceMonitor.Delete(int amount, bool success)
+ {
+ this.pcDelete.Increment(amount, success);
+ }
+
+ void IPerformanceMonitor.Mutate(MutationMode mode, int amount, bool success)
+ {
+ switch (mode)
+ {
+ case MutationMode.Increment:
+ this.pcIncrement.Increment(amount, success);
+ break;
+
+ case MutationMode.Decrement:
+ this.pcDecrement.Increment(amount, success);
+ break;
+ }
+ }
+
+ void IPerformanceMonitor.Concatenate(ConcatenationMode mode, int amount, bool success)
+ {
+ switch (mode)
+ {
+ case ConcatenationMode.Append:
+ this.pcAppend.Increment(amount, success);
+ break;
+
+ case ConcatenationMode.Prepend:
+ this.pcPrepend.Increment(amount, success);
+ break;
+ }
+ }
+
+ #endregion
+ #region [ OpMonitor ]
+
+ internal class OpMonitor : IDisposable
+ {
+ private PerformanceCounter pcTotal;
+ private PerformanceCounter pcHits;
+ private PerformanceCounter pcMisses;
+ private PerformanceCounter pcTotalPerSec;
+ private PerformanceCounter pcHitsPerSec;
+ private PerformanceCounter pcMissesPerSec;
+
+ const string Total = " Total";
+ const string Hits = " Hits";
+ const string Misses = " Misses";
+ const string TotalPerSec = " Total/sec";
+ const string HitsPerSec = " Hits/sec";
+ const string MissesPerSec = " Misses/sec";
+
+ public OpMonitor(string instance, string category, string name)
+ {
+ this.pcTotal = new PerformanceCounter(category, name + Total, instance, false);
+ this.pcHits = new PerformanceCounter(category, name + Hits, instance, false);
+ this.pcMisses = new PerformanceCounter(category, name + Misses, instance, false);
+ this.pcTotalPerSec = new PerformanceCounter(category, name + TotalPerSec, instance, false);
+ this.pcHitsPerSec = new PerformanceCounter(category, name + HitsPerSec, instance, false);
+ this.pcMissesPerSec = new PerformanceCounter(category, name + MissesPerSec, instance, false);
+
+ // reste the counters to 0
+ this.pcHits.RawValue = 0;
+ this.pcHitsPerSec.RawValue = 0;
+ this.pcMisses.RawValue = 0;
+ this.pcMissesPerSec.RawValue = 0;
+ this.pcTotal.RawValue = 0;
+ this.pcTotalPerSec.RawValue = 0;
+ }
+
+ ~OpMonitor()
+ {
+ this.Dispose();
+ }
+
+ public void Increment(int amount, bool success)
+ {
+ this.pcTotal.IncrementBy(amount);
+ this.pcTotalPerSec.IncrementBy(amount);
+
+ if (success)
+ {
+ this.pcHits.IncrementBy(amount);
+ this.pcHitsPerSec.IncrementBy(amount);
+ }
+ else
+ {
+ this.pcMisses.IncrementBy(amount);
+ this.pcMissesPerSec.IncrementBy(amount);
+ }
+ }
+
+ internal static CounterCreationData[] CreateCounters(string op)
+ {
+ var retval = new CounterCreationData[6];
+
+ retval[0] = new CounterCreationData(op + Total, "Total number of " + op + " operations during the client's lifetime", PerformanceCounterType.NumberOfItems64);
+ retval[1] = new CounterCreationData(op + Hits, "Total number of successful " + op + " operations during the client's lifetime", PerformanceCounterType.NumberOfItems64);
+ retval[2] = new CounterCreationData(op + Misses, "Total number of failed " + op + " operations during the client's lifetime", PerformanceCounterType.NumberOfItems64);
+
+ retval[3] = new CounterCreationData(op + TotalPerSec, "Number of " + op + " operations handled by the client per second.", PerformanceCounterType.RateOfCountsPerSecond64);
+ retval[4] = new CounterCreationData(op + HitsPerSec, "Number of successful " + op + " operations handled by the client per second.", PerformanceCounterType.RateOfCountsPerSecond64);
+ retval[5] = new CounterCreationData(op + MissesPerSec, "Number of failed " + op + " operations handled by the client per second.", PerformanceCounterType.RateOfCountsPerSecond64);
+
+ return retval;
+ }
+
+ public void Dispose()
+ {
+ ((IDisposable)this.pcTotalPerSec).Dispose();
+ }
+
+ void IDisposable.Dispose()
+ {
+ GC.SuppressFinalize(this);
+
+ if (this.pcHits != null)
+ {
+ this.pcHits.Dispose();
+ this.pcHitsPerSec.Dispose();
+ this.pcMisses.Dispose();
+ this.pcMissesPerSec.Dispose();
+ this.pcTotal.Dispose();
+ this.pcTotalPerSec.Dispose();
+
+ this.pcHits = null;
+ this.pcHitsPerSec = null;
+ this.pcMisses = null;
+ this.pcMissesPerSec = null;
+ this.pcTotal = null;
+ this.pcTotalPerSec = null;
+ }
+ }
+ }
+
+ #endregion
+ }
+}
View
37 Enyim.Caching/Memcached/PerformanceMonitor/DefaultPerformanceMonitorFactory.cs
@@ -0,0 +1,37 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Diagnostics;
+
+namespace Enyim.Caching.Memcached
+{
+ public class DefaultPerformanceMonitorFactory : IProviderFactory<IPerformanceMonitor>
+ {
+ private string name;
+
+ internal DefaultPerformanceMonitorFactory() { }
+
+ public DefaultPerformanceMonitorFactory(string name)
+ {
+ if (String.IsNullOrEmpty(name))
+ throw new ArgumentException("Name must be specified.", "name");
+
+ this.name = name;
+ }
+
+ void IProvider.Initialize(Dictionary<string, string> parameters)
+ {
+ if ((parameters != null
+ && (!parameters.TryGetValue("name", out this.name)
+ || String.IsNullOrEmpty(this.name)))
+ || (parameters == null && String.IsNullOrEmpty(this.name)))
+ throw new ArgumentException("The DefaultPerformanceMonitor must have a name assigned. Use the name attribute in the configuration file.");
+ }
+
+ IPerformanceMonitor IProviderFactory<IPerformanceMonitor>.Create()
+ {
+ return new DefaultPerformanceMonitor(this.name);
+ }
+ }
+}
View
17 Enyim.Caching/Memcached/PerformanceMonitor/IPerformanceMonitor.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Diagnostics;
+
+namespace Enyim.Caching.Memcached
+{
+ public interface IPerformanceMonitor : IDisposable
+ {
+ void Get(int amount, bool success);
+ void Store(StoreMode mode, int amount, bool success);
+ void Delete(int amount, bool success);
+ void Mutate(MutationMode mode, int amount, bool success);
+ void Concatenate(ConcatenationMode mode, int amount, bool success);
+ }
+}
View
30 Enyim.Caching/Memcached/PerformanceMonitor/PerformanceCounterInstaller.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Diagnostics;
+
+namespace Enyim.Caching.Memcached
+{
+ [System.ComponentModel.RunInstaller(true)]
+ public class PerformanceCounterInstaller : System.Diagnostics.PerformanceCounterInstaller
+ {
+ public PerformanceCounterInstaller()
+ {
+ this.CategoryName = DefaultPerformanceMonitor.CategoryName;
+ this.CategoryType = PerformanceCounterCategoryType.MultiInstance;
+ this.UninstallAction = System.Configuration.Install.UninstallAction.Remove;
+
+ // Get
+ this.Counters.AddRange(DefaultPerformanceMonitor.OpMonitor.CreateCounters(DefaultPerformanceMonitor.Names.Get));
+ this.Counters.AddRange(DefaultPerformanceMonitor.OpMonitor.CreateCounters(DefaultPerformanceMonitor.Names.Set));
+ this.Counters.AddRange(DefaultPerformanceMonitor.OpMonitor.CreateCounters(DefaultPerformanceMonitor.Names.Add));
+ this.Counters.AddRange(DefaultPerformanceMonitor.OpMonitor.CreateCounters(DefaultPerformanceMonitor.Names.Replace));
+ this.Counters.AddRange(DefaultPerformanceMonitor.OpMonitor.CreateCounters(DefaultPerformanceMonitor.Names.Delete));
+ this.Counters.AddRange(DefaultPerformanceMonitor.OpMonitor.CreateCounters(DefaultPerformanceMonitor.Names.Increment));
+ this.Counters.AddRange(DefaultPerformanceMonitor.OpMonitor.CreateCounters(DefaultPerformanceMonitor.Names.Decrement));
+ this.Counters.AddRange(DefaultPerformanceMonitor.OpMonitor.CreateCounters(DefaultPerformanceMonitor.Names.Append));
+ this.Counters.AddRange(DefaultPerformanceMonitor.OpMonitor.CreateCounters(DefaultPerformanceMonitor.Names.Prepend));
+ }
+ }
+}
View
94 Enyim.Caching/MemcachedClient.cs
@@ -25,31 +25,26 @@ public class MemcachedClient : IMemcachedClient
private IServerPool pool;
private IMemcachedKeyTransformer keyTransformer;
private ITranscoder transcoder;
+ private IPerformanceMonitor performanceMonitor;
/// <summary>
/// Initializes a new MemcachedClient instance using the default configuration section (enyim/memcached).
/// </summary>
- public MemcachedClient() : this(DefaultSettings) { }
+ public MemcachedClient()
+ : this(DefaultSettings) { }
protected IServerPool Pool { get { return this.pool; } }
protected IMemcachedKeyTransformer KeyTransformer { get { return this.keyTransformer; } }
protected ITranscoder Transcoder { get { return this.transcoder; } }
+ protected IPerformanceMonitor PerformanceMonitor { get { return this.performanceMonitor; } }
/// <summary>
/// Initializes a new MemcachedClient instance using the specified configuration section.
/// This overload allows to create multiple MemcachedClients with different pool configurations.
/// </summary>
/// <param name="sectionName">The name of the configuration section to be used for configuring the behavior of the client.</param>
- public MemcachedClient(string sectionName) : this(GetSection(sectionName)) { }
-
- private static IMemcachedClientConfiguration GetSection(string sectionName)
- {
- MemcachedClientSection section = (MemcachedClientSection)ConfigurationManager.GetSection(sectionName);
- if (section == null)
- throw new ConfigurationErrorsException("Section " + sectionName + " is not found.");
-
- return section;
- }
+ public MemcachedClient(string sectionName)
+ : this(GetSection(sectionName)) { }
/// <summary>
/// Initializes a new instance of the <see cref="T:MemcachedClient"/> using the specified configuration instance.
@@ -62,17 +57,22 @@ public MemcachedClient(IMemcachedClientConfiguration configuration)
this.keyTransformer = configuration.CreateKeyTransformer() ?? new DefaultKeyTransformer();
this.transcoder = configuration.CreateTranscoder() ?? new DefaultTranscoder();
+ this.performanceMonitor = configuration.CreatePerformanceMonitor();
this.pool = configuration.CreatePool();
this.pool.Start();
}
public MemcachedClient(IServerPool pool, IMemcachedKeyTransformer keyTransformer, ITranscoder transcoder)
+ : this(pool, keyTransformer, transcoder, null) { }
+
+ public MemcachedClient(IServerPool pool, IMemcachedKeyTransformer keyTransformer, ITranscoder transcoder, IPerformanceMonitor performanceMonitor)
{
if (pool == null) throw new ArgumentNullException("pool");
if (keyTransformer == null) throw new ArgumentNullException("keyTransformer");
if (transcoder == null) throw new ArgumentNullException("transcoder");
+ this.performanceMonitor = performanceMonitor;
this.keyTransformer = keyTransformer;
this.transcoder = transcoder;
@@ -86,6 +86,15 @@ public MemcachedClient(IServerPool pool, IMemcachedKeyTransformer keyTransformer
catch { }
}
+ private static IMemcachedClientConfiguration GetSection(string sectionName)
+ {
+ MemcachedClientSection section = (MemcachedClientSection)ConfigurationManager.GetSection(sectionName);
+ if (section == null)
+ throw new ConfigurationErrorsException("Section " + sectionName + " is not found.");
+
+ return section;
+ }
+
/// <summary>
/// Retrieves the specified item from the cache.
/// </summary>
@@ -163,13 +172,17 @@ protected virtual bool PerformTryGet(string key, out ulong cas, out object value
value = this.transcoder.Deserialize(command.Result);
cas = command.CasValue;
+ if (this.performanceMonitor != null) this.performanceMonitor.Get(1, true);
+
return true;
}
}
value = null;
cas = 0;
+ if (this.performanceMonitor != null) this.performanceMonitor.Get(1, false);
+
return false;
}
@@ -296,17 +309,22 @@ protected virtual bool PerformStore(StoreMode mode, string key, object value, ui
{
log.Error(e);
+ if (this.performanceMonitor != null) this.performanceMonitor.Store(mode, 1, false);
+
return false;
}
var command = this.pool.OperationFactory.Store(mode, hashedKey, item, expires, cas);
var retval = node.Execute(command);
cas = command.CasValue;
+ if (this.performanceMonitor != null) this.performanceMonitor.Store(mode, 1, true);
return retval;
}
+ if (this.performanceMonitor != null) this.performanceMonitor.Store(mode, 1, false);
+
return false;
}
@@ -516,10 +534,14 @@ protected virtual ulong PerformMutate(MutationMode mode, string key, ulong defau
cas = command.CasValue;
+ if (this.performanceMonitor != null) this.performanceMonitor.Mutate(mode, 1, success);
+
if (success)
return command.Result;
}
+ if (this.performanceMonitor != null) this.performanceMonitor.Mutate(mode, 1, false);
+
// TODO not sure about the return value when the command fails
return defaultValue;
}
@@ -593,10 +615,13 @@ protected virtual bool PerformConcatenate(ConcatenationMode mode, string key, re
var retval = node.Execute(command);
cas = command.CasValue;
+ if (this.performanceMonitor != null) this.performanceMonitor.Concatenate(mode, 1, true);
return retval;
}
+ if (this.performanceMonitor != null) this.performanceMonitor.Concatenate(mode, 1, false);
+
return false;
}
@@ -707,7 +732,6 @@ public bool Remove(string key)
var retval = new Dictionary<string, T>(hashed.Count);
var handles = new List<WaitHandle>();
- var i = 0;
//execute each list of keys on their respective node
foreach (var slice in byServer)
@@ -725,8 +749,6 @@ public bool Remove(string key)
var mre = new ManualResetEvent(false);
handles.Add(mre);
- if (log.IsDebugEnabled) log.Debug(i + " Expecting: " + nodeKeys.Length);
-
//execute the mgets in parallel
action.BeginInvoke(mget, iar =>
{
@@ -735,7 +757,22 @@ public bool Remove(string key)
using (iar.AsyncWaitHandle)
if (action.EndInvoke(iar))
{
- if (log.IsDebugEnabled) log.Debug(iar.AsyncState + " Success: " + mget.Result.Count);
+ if (this.performanceMonitor != null)
+ {
+ // full list of keys sent to the server
+ var expectedKeys = (string[])iar.AsyncState;
+ var expectedCount = expectedKeys.Length;
+
+ // number of items returned
+ var resultCount = mget.Result.Count;
+
+ // log the results
+ this.performanceMonitor.Get(resultCount, true);
+
+ // log the missing keys
+ if (resultCount != expectedCount)
+ this.performanceMonitor.Get(expectedCount - resultCount, true);
+ }
// deserialize the items in the dictionary
foreach (var kvp in mget.Result)
@@ -761,11 +798,10 @@ public bool Remove(string key)
// indicate that we finished processing
mre.Set();
}
- }, i);
-
- i++;
+ }, nodeKeys);
}
+ // wait for all nodes to finish
if (handles.Count > 0)
{
SafeWaitAllAndDispose(handles.ToArray());
@@ -774,6 +810,10 @@ public bool Remove(string key)
return retval;
}
+ /// <summary>
+ /// Waits for all WaitHandles and works in both STA and MTA mode.
+ /// </summary>
+ /// <param name="waitHandles"></param>
private static void SafeWaitAllAndDispose(WaitHandle[] waitHandles)
{
try
@@ -840,18 +880,18 @@ void IDisposable.Dispose()
/// <remarks>You should only call this when you are not using static instances of the client, so it can close all conections and release the sockets.</remarks>
public void Dispose()
{
+ GC.SuppressFinalize(this);
+
if (this.pool != null)
{
- GC.SuppressFinalize(this);
+ try { this.pool.Dispose(); }
+ finally { this.pool = null; }
+ }
- try
- {
- this.pool.Dispose();
- }
- finally
- {
- this.pool = null;
- }
+ if (this.performanceMonitor != null)
+ {
+ try { this.performanceMonitor.Dispose(); }
+ finally { this.performanceMonitor = null; }
}
}

0 comments on commit a0c5dd9

Please sign in to comment.
Something went wrong with that request. Please try again.