DbConnectionPoolCounters.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. //------------------------------------------------------------------------------
  2. // <copyright file="DbConnectionPoolCounters.cs" company="Microsoft">
  3. // Copyright (c) Microsoft Corporation. All rights reserved.
  4. // </copyright>
  5. // <owner current="true" primary="true">[....]</owner>
  6. //------------------------------------------------------------------------------
  7. namespace System.Data.ProviderBase {
  8. using System;
  9. using System.Collections;
  10. using System.Data.Common;
  11. using System.Diagnostics;
  12. using System.Globalization;
  13. using System.Reflection;
  14. using System.Runtime.ConstrainedExecution;
  15. using System.Security;
  16. using System.Security.Permissions;
  17. using System.Security.Principal;
  18. using System.Runtime.Versioning;
  19. internal abstract class DbConnectionPoolCounters {
  20. private static class CreationData {
  21. static internal readonly CounterCreationData HardConnectsPerSecond = new CounterCreationData(
  22. "HardConnectsPerSecond",
  23. "The number of actual connections per second that are being made to servers",
  24. PerformanceCounterType.RateOfCountsPerSecond32);
  25. static internal readonly CounterCreationData HardDisconnectsPerSecond = new CounterCreationData(
  26. "HardDisconnectsPerSecond",
  27. "The number of actual disconnects per second that are being made to servers",
  28. PerformanceCounterType.RateOfCountsPerSecond32);
  29. static internal readonly CounterCreationData SoftConnectsPerSecond = new CounterCreationData(
  30. "SoftConnectsPerSecond",
  31. "The number of connections we get from the pool per second",
  32. PerformanceCounterType.RateOfCountsPerSecond32);
  33. static internal readonly CounterCreationData SoftDisconnectsPerSecond = new CounterCreationData(
  34. "SoftDisconnectsPerSecond",
  35. "The number of connections we return to the pool per second",
  36. PerformanceCounterType.RateOfCountsPerSecond32);
  37. static internal readonly CounterCreationData NumberOfNonPooledConnections = new CounterCreationData(
  38. "NumberOfNonPooledConnections",
  39. "The number of connections that are not using connection pooling",
  40. PerformanceCounterType.NumberOfItems32);
  41. static internal readonly CounterCreationData NumberOfPooledConnections = new CounterCreationData(
  42. "NumberOfPooledConnections",
  43. "The number of connections that are managed by the connection pooler",
  44. PerformanceCounterType.NumberOfItems32);
  45. static internal readonly CounterCreationData NumberOfActiveConnectionPoolGroups = new CounterCreationData(
  46. "NumberOfActiveConnectionPoolGroups",
  47. "The number of unique connection strings",
  48. PerformanceCounterType.NumberOfItems32);
  49. static internal readonly CounterCreationData NumberOfInactiveConnectionPoolGroups = new CounterCreationData(
  50. "NumberOfInactiveConnectionPoolGroups",
  51. "The number of unique connection strings waiting for pruning",
  52. PerformanceCounterType.NumberOfItems32);
  53. static internal readonly CounterCreationData NumberOfActiveConnectionPools = new CounterCreationData(
  54. "NumberOfActiveConnectionPools",
  55. "The number of connection pools",
  56. PerformanceCounterType.NumberOfItems32);
  57. static internal readonly CounterCreationData NumberOfInactiveConnectionPools = new CounterCreationData(
  58. "NumberOfInactiveConnectionPools",
  59. "The number of connection pools",
  60. PerformanceCounterType.NumberOfItems32);
  61. static internal readonly CounterCreationData NumberOfActiveConnections = new CounterCreationData(
  62. "NumberOfActiveConnections",
  63. "The number of connections currently in-use",
  64. PerformanceCounterType.NumberOfItems32);
  65. static internal readonly CounterCreationData NumberOfFreeConnections = new CounterCreationData(
  66. "NumberOfFreeConnections",
  67. "The number of connections currently available for use",
  68. PerformanceCounterType.NumberOfItems32);
  69. static internal readonly CounterCreationData NumberOfStasisConnections = new CounterCreationData(
  70. "NumberOfStasisConnections",
  71. "The number of connections currently waiting to be made ready for use",
  72. PerformanceCounterType.NumberOfItems32);
  73. static internal readonly CounterCreationData NumberOfReclaimedConnections = new CounterCreationData(
  74. "NumberOfReclaimedConnections",
  75. "The number of connections we reclaim from GC'd external connections",
  76. PerformanceCounterType.NumberOfItems32);
  77. };
  78. sealed internal class Counter {
  79. private PerformanceCounter _instance;
  80. internal Counter (string categoryName, string instanceName, string counterName, PerformanceCounterType counterType) {
  81. if (ADP.IsPlatformNT5) {
  82. try {
  83. if (!ADP.IsEmpty(categoryName) && !ADP.IsEmpty(instanceName)) {
  84. PerformanceCounter instance = new PerformanceCounter();
  85. instance.CategoryName = categoryName;
  86. instance.CounterName = counterName;
  87. instance.InstanceName = instanceName;
  88. instance.InstanceLifetime = PerformanceCounterInstanceLifetime.Process;
  89. instance.ReadOnly = false;
  90. instance.RawValue = 0; // make sure we start out at zero
  91. _instance = instance;
  92. }
  93. }
  94. catch (InvalidOperationException e) {
  95. ADP.TraceExceptionWithoutRethrow(e);
  96. //
  97. }
  98. }
  99. }
  100. internal void Decrement() {
  101. PerformanceCounter instance = _instance;
  102. if (null != instance) {
  103. instance.Decrement();
  104. }
  105. }
  106. [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
  107. internal void Dispose () { //
  108. PerformanceCounter instance = _instance;
  109. _instance = null;
  110. if (null != instance) {
  111. instance.RemoveInstance();
  112. // should we be calling instance.Close?
  113. // if we do will it exacerbate the Dispose vs. Decrement race condition
  114. //instance.Close();
  115. }
  116. }
  117. internal void Increment() {
  118. PerformanceCounter instance = _instance;
  119. if (null != instance) {
  120. instance.Increment();
  121. }
  122. }
  123. };
  124. const int CounterInstanceNameMaxLength = 127;
  125. internal readonly Counter HardConnectsPerSecond;
  126. internal readonly Counter HardDisconnectsPerSecond;
  127. internal readonly Counter SoftConnectsPerSecond;
  128. internal readonly Counter SoftDisconnectsPerSecond;
  129. internal readonly Counter NumberOfNonPooledConnections;
  130. internal readonly Counter NumberOfPooledConnections;
  131. internal readonly Counter NumberOfActiveConnectionPoolGroups;
  132. internal readonly Counter NumberOfInactiveConnectionPoolGroups;
  133. internal readonly Counter NumberOfActiveConnectionPools;
  134. internal readonly Counter NumberOfInactiveConnectionPools;
  135. internal readonly Counter NumberOfActiveConnections;
  136. internal readonly Counter NumberOfFreeConnections;
  137. internal readonly Counter NumberOfStasisConnections;
  138. internal readonly Counter NumberOfReclaimedConnections;
  139. protected DbConnectionPoolCounters() : this(null, null) {
  140. }
  141. protected DbConnectionPoolCounters(string categoryName, string categoryHelp) {
  142. AppDomain.CurrentDomain.DomainUnload += new EventHandler(this.UnloadEventHandler);
  143. AppDomain.CurrentDomain.ProcessExit += new EventHandler(this.ExitEventHandler);
  144. AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(this.ExceptionEventHandler);
  145. string instanceName = null;
  146. if (!ADP.IsEmpty(categoryName)) {
  147. if (ADP.IsPlatformNT5) {
  148. instanceName = GetInstanceName();
  149. }
  150. }
  151. // level 0-3: hard connects/disconnects, plus basic pool/pool entry statistics
  152. string basicCategoryName = categoryName;
  153. HardConnectsPerSecond = new Counter(basicCategoryName, instanceName, CreationData.HardConnectsPerSecond.CounterName, CreationData.HardConnectsPerSecond.CounterType);
  154. HardDisconnectsPerSecond = new Counter(basicCategoryName, instanceName, CreationData.HardDisconnectsPerSecond.CounterName, CreationData.HardDisconnectsPerSecond.CounterType);
  155. NumberOfNonPooledConnections = new Counter(basicCategoryName, instanceName, CreationData.NumberOfNonPooledConnections.CounterName, CreationData.NumberOfNonPooledConnections.CounterType);
  156. NumberOfPooledConnections = new Counter(basicCategoryName, instanceName, CreationData.NumberOfPooledConnections.CounterName, CreationData.NumberOfPooledConnections.CounterType);
  157. NumberOfActiveConnectionPoolGroups = new Counter(basicCategoryName, instanceName, CreationData.NumberOfActiveConnectionPoolGroups.CounterName, CreationData.NumberOfActiveConnectionPoolGroups.CounterType);
  158. NumberOfInactiveConnectionPoolGroups = new Counter(basicCategoryName, instanceName, CreationData.NumberOfInactiveConnectionPoolGroups.CounterName, CreationData.NumberOfInactiveConnectionPoolGroups.CounterType);
  159. NumberOfActiveConnectionPools = new Counter(basicCategoryName, instanceName, CreationData.NumberOfActiveConnectionPools.CounterName, CreationData.NumberOfActiveConnectionPools.CounterType);
  160. NumberOfInactiveConnectionPools = new Counter(basicCategoryName, instanceName, CreationData.NumberOfInactiveConnectionPools.CounterName, CreationData.NumberOfInactiveConnectionPools.CounterType);
  161. NumberOfStasisConnections = new Counter(basicCategoryName, instanceName, CreationData.NumberOfStasisConnections.CounterName, CreationData.NumberOfStasisConnections.CounterType);
  162. NumberOfReclaimedConnections = new Counter(basicCategoryName, instanceName, CreationData.NumberOfReclaimedConnections.CounterName, CreationData.NumberOfReclaimedConnections.CounterType);
  163. // level 4: expensive stuff
  164. string verboseCategoryName = null;
  165. if (!ADP.IsEmpty(categoryName)) {
  166. // don't load TraceSwitch if no categoryName so that Odbc/OleDb have a chance of not loading TraceSwitch
  167. // which are also used by System.Diagnostics.PerformanceCounter.ctor & System.Transactions.get_Current
  168. TraceSwitch perfCtrSwitch = new TraceSwitch("ConnectionPoolPerformanceCounterDetail", "level of detail to track with connection pool performance counters");
  169. if (TraceLevel.Verbose == perfCtrSwitch.Level) {
  170. verboseCategoryName = categoryName;
  171. }
  172. }
  173. SoftConnectsPerSecond = new Counter(verboseCategoryName, instanceName, CreationData.SoftConnectsPerSecond.CounterName, CreationData.SoftConnectsPerSecond.CounterType);
  174. SoftDisconnectsPerSecond = new Counter(verboseCategoryName, instanceName, CreationData.SoftDisconnectsPerSecond.CounterName, CreationData.SoftDisconnectsPerSecond.CounterType);
  175. NumberOfActiveConnections = new Counter(verboseCategoryName, instanceName, CreationData.NumberOfActiveConnections.CounterName, CreationData.NumberOfActiveConnections.CounterType);
  176. NumberOfFreeConnections = new Counter(verboseCategoryName, instanceName, CreationData.NumberOfFreeConnections.CounterName, CreationData.NumberOfFreeConnections.CounterType);
  177. }
  178. [FileIOPermission(SecurityAction.Assert, Unrestricted=true)]
  179. private string GetAssemblyName() {
  180. string result = null;
  181. // First try GetEntryAssembly name, then AppDomain.FriendlyName.
  182. Assembly assembly = Assembly.GetEntryAssembly();
  183. if (null != assembly) {
  184. AssemblyName name = assembly.GetName();
  185. if (name != null) {
  186. result = name.Name; // MDAC 73469
  187. }
  188. }
  189. return result;
  190. }
  191. // SxS: this method uses GetCurrentProcessId to construct the instance name.
  192. //
  193. [ResourceExposure(ResourceScope.None)]
  194. [ResourceConsumption(ResourceScope.Process, ResourceScope.Process)]
  195. private string GetInstanceName() {
  196. string result = null;
  197. string instanceName = GetAssemblyName(); // instance perfcounter name
  198. if (ADP.IsEmpty(instanceName)) {
  199. AppDomain appDomain = AppDomain.CurrentDomain;
  200. if (null != appDomain) {
  201. instanceName = appDomain.FriendlyName;
  202. }
  203. }
  204. //
  205. int pid = SafeNativeMethods.GetCurrentProcessId();
  206. // SQLBUDT #366157 -there are several characters which have special meaning
  207. // to PERFMON. They recommend that we translate them as shown below, to
  208. // prevent problems.
  209. result = String.Format((IFormatProvider)null, "{0}[{1}]", instanceName, pid);
  210. result = result.Replace('(','[').Replace(')',']').Replace('#','_').Replace('/','_').Replace('\\','_');
  211. // SQLBUVSTS #94625 - counter instance name cannot be greater than 127
  212. if (result.Length > CounterInstanceNameMaxLength) {
  213. // Replacing the middle part with "[...]"
  214. // For example: if path is c:\long_path\very_(Ax200)_long__path\perftest.exe and process ID is 1234 than the resulted instance name will be:
  215. // c:\long_path\very_(AxM)[...](AxN)_long__path\perftest.exe[1234]
  216. // while M and N are adjusted to make each part before and after the [...] = 61 (making the total = 61 + 5 + 61 = 127)
  217. const string insertString = "[...]";
  218. int firstPartLength = (CounterInstanceNameMaxLength - insertString.Length) / 2;
  219. int lastPartLength = CounterInstanceNameMaxLength - firstPartLength - insertString.Length;
  220. result = string.Format((IFormatProvider)null, "{0}{1}{2}",
  221. result.Substring(0, firstPartLength),
  222. insertString,
  223. result.Substring(result.Length - lastPartLength, lastPartLength));
  224. Debug.Assert(result.Length == CounterInstanceNameMaxLength,
  225. string.Format((IFormatProvider)null, "wrong calculation of the instance name: expected {0}, actual: {1}", CounterInstanceNameMaxLength, result.Length));
  226. }
  227. return result;
  228. }
  229. [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
  230. public void Dispose() {
  231. // ExceptionEventHandler with IsTerminiating may be called before
  232. // the Connection Close is called or the variables are initialized
  233. SafeDispose(HardConnectsPerSecond);
  234. SafeDispose(HardDisconnectsPerSecond);
  235. SafeDispose(SoftConnectsPerSecond);
  236. SafeDispose(SoftDisconnectsPerSecond);
  237. SafeDispose(NumberOfNonPooledConnections);
  238. SafeDispose(NumberOfPooledConnections);
  239. SafeDispose(NumberOfActiveConnectionPoolGroups);
  240. SafeDispose(NumberOfInactiveConnectionPoolGroups);
  241. SafeDispose(NumberOfActiveConnectionPools);
  242. SafeDispose(NumberOfActiveConnections);
  243. SafeDispose(NumberOfFreeConnections);
  244. SafeDispose(NumberOfStasisConnections);
  245. SafeDispose(NumberOfReclaimedConnections);
  246. }
  247. [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
  248. private void SafeDispose(Counter counter) { // WebData 103603
  249. if (null != counter) {
  250. counter.Dispose();
  251. }
  252. }
  253. [PrePrepareMethod]
  254. void ExceptionEventHandler (object sender, UnhandledExceptionEventArgs e) {
  255. if ((null != e) && e.IsTerminating) {
  256. Dispose();
  257. }
  258. }
  259. [PrePrepareMethod]
  260. void ExitEventHandler (object sender, EventArgs e) {
  261. Dispose();
  262. }
  263. [PrePrepareMethod]
  264. void UnloadEventHandler (object sender, EventArgs e) {
  265. Dispose();
  266. }
  267. }
  268. sealed internal class DbConnectionPoolCountersNoCounters : DbConnectionPoolCounters {
  269. public static readonly DbConnectionPoolCountersNoCounters SingletonInstance = new DbConnectionPoolCountersNoCounters();
  270. private DbConnectionPoolCountersNoCounters() : base () {
  271. }
  272. }
  273. }