Problem of refresh with PerformanceCounter and failover.

L

Lionel Schiepers

We have a multithreaded service written in .NET 2.0 that is running 24h/24.
This service checks nearly 250 another servers to log statisticals data
acquired via WMI, SNMP, PerformanceCounter...

We use XOsoft for the fail over of our SQL Server databases (active, passive
but the clients are always using the same name to connect to the elected
active server) but we always have problems when the active server change
because the PerformanceCounter objects use a cache located to the class
PerformanceCounterLib internal to the .NET framework. This cache contains
the categories available for each server name. Because the servers are not
identical, the categories of a server cannot be reused for the other one and
we catch these exceptions after a fail over.

[2006/06/21 13:05:16.1935] Pdh Failed for xxx..bvdep.net
System.InvalidOperationException: Could not Read Category Index: 3616.
at System.Diagnostics.CategorySample..ctor(Byte[] data, CategoryEntry
entry, PerformanceCounterLib library)
at System.Diagnostics.PerformanceCounterLib.GetCategorySample(String
category)
at System.Diagnostics.PerformanceCounterLib.GetCategorySample(String
machine, String category)
at System.Diagnostics.PerformanceCounter.NextSample()
at ServerPatrol.Agent.Components.Specialized.Pdh.Invoke(ResultPipeline
results, TargetDTO target, String& resultDescription)


It exists the method PerformanceCounter.CloseSharedResources() that clears
the cache but for all servers which is not interesting for us because we
have all the time between 1.000 and 3.000 open performance counters
dispatched to ~250 servers. I'm not even sure it's possible because the
service is always running between 10 and 60 threads at the same time and the
implementation of CloseSharedResources doesn't seem to be thread safe.

Is there any tricks to correct this kind of problem?

I have two solutions but I prefer not to use them until a better one.


1. Put the PerformanceCounters associated to a server in a dedicated
application domain. That way, I can call CloseSharedResources without
loosing the data of the other servers. I don't like this solution because:
- I don't want to manage the lifetime of my objects between the
application domain
- I don't want an application with ~250 application domains.
- Debugging is harder.

2. Directly removing the concerned cached entries via reflection (because
the class is internal). I used Reflector to write the following code but I
don't like this solution because the internal implementation could change
between different version of the .NET Framework.

System.Reflection.Assembly assembly =
System.Reflection.Assembly.GetAssembly(typeof(PerformanceCounter));
Type type =
assembly.GetType("System.Diagnostics.PerformanceCounterLib");
System.Reflection.FieldInfo fi = type.GetField(
"libraryTable",
System.Reflection.BindingFlags.Static |
System.Reflection.BindingFlags.NonPublic);

Hashtable dictionary = fi.GetValue(null) as Hashtable;
if (null == dictionary)
return;
System.Globalization.CultureInfo culture = new
System.Globalization.CultureInfo(9);
string key =
server.ToLower(System.Globalization.CultureInfo.InvariantCulture) + ":" +
culture.LCID.ToString("X3",
System.Globalization.CultureInfo.InvariantCulture);
object o = dictionary[key];
if (null != o)
{
System.Reflection.MethodInfo mi = o.GetType().GetMethod("Close",
System.Reflection.BindingFlags.Instance |
System.Reflection.BindingFlags.NonPublic);
mi.Invoke(o, new object[0]);
dictionary.Remove(key);
}

Thanks in advance.

Lionel Schiepers (ls at bvdep.com),
Bureau van Dijk Electronic Publishing
Belgium.
 
L

Lionel Schiepers

I forgot to mention that I recreate the performance counter when I catch an
exception.

Lionel Schiepers


Lionel Schiepers said:
We have a multithreaded service written in .NET 2.0 that is running
24h/24. This service checks nearly 250 another servers to log statisticals
data acquired via WMI, SNMP, PerformanceCounter...

We use XOsoft for the fail over of our SQL Server databases (active,
passive but the clients are always using the same name to connect to the
elected active server) but we always have problems when the active server
change because the PerformanceCounter objects use a cache located to the
class PerformanceCounterLib internal to the .NET framework. This cache
contains the categories available for each server name. Because the
servers are not identical, the categories of a server cannot be reused for
the other one and we catch these exceptions after a fail over.

[2006/06/21 13:05:16.1935] Pdh Failed for xxx..bvdep.net
System.InvalidOperationException: Could not Read Category Index: 3616.
at System.Diagnostics.CategorySample..ctor(Byte[] data, CategoryEntry
entry, PerformanceCounterLib library)
at System.Diagnostics.PerformanceCounterLib.GetCategorySample(String
category)
at System.Diagnostics.PerformanceCounterLib.GetCategorySample(String
machine, String category)
at System.Diagnostics.PerformanceCounter.NextSample()
at ServerPatrol.Agent.Components.Specialized.Pdh.Invoke(ResultPipeline
results, TargetDTO target, String& resultDescription)


It exists the method PerformanceCounter.CloseSharedResources() that clears
the cache but for all servers which is not interesting for us because we
have all the time between 1.000 and 3.000 open performance counters
dispatched to ~250 servers. I'm not even sure it's possible because the
service is always running between 10 and 60 threads at the same time and
the implementation of CloseSharedResources doesn't seem to be thread safe.

Is there any tricks to correct this kind of problem?

I have two solutions but I prefer not to use them until a better one.


1. Put the PerformanceCounters associated to a server in a dedicated
application domain. That way, I can call CloseSharedResources without
loosing the data of the other servers. I don't like this solution because:
- I don't want to manage the lifetime of my objects between the
application domain
- I don't want an application with ~250 application domains.
- Debugging is harder.

2. Directly removing the concerned cached entries via reflection (because
the class is internal). I used Reflector to write the following code but I
don't like this solution because the internal implementation could change
between different version of the .NET Framework.

System.Reflection.Assembly assembly =
System.Reflection.Assembly.GetAssembly(typeof(PerformanceCounter));
Type type =
assembly.GetType("System.Diagnostics.PerformanceCounterLib");
System.Reflection.FieldInfo fi = type.GetField(
"libraryTable",
System.Reflection.BindingFlags.Static |
System.Reflection.BindingFlags.NonPublic);

Hashtable dictionary = fi.GetValue(null) as Hashtable;
if (null == dictionary)
return;
System.Globalization.CultureInfo culture = new
System.Globalization.CultureInfo(9);
string key =
server.ToLower(System.Globalization.CultureInfo.InvariantCulture) + ":" +
culture.LCID.ToString("X3",
System.Globalization.CultureInfo.InvariantCulture);
object o = dictionary[key];
if (null != o)
{
System.Reflection.MethodInfo mi = o.GetType().GetMethod("Close",
System.Reflection.BindingFlags.Instance |
System.Reflection.BindingFlags.NonPublic);
mi.Invoke(o, new object[0]);
dictionary.Remove(key);
}

Thanks in advance.

Lionel Schiepers (ls at bvdep.com),
Bureau van Dijk Electronic Publishing
Belgium.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top