3 using System.Collections.Generic;
5 using System.Runtime.Caching;
15 public static class QueryCache
22 private static readonly
int MAX_LEGHT_TRACE = 200;
27 private static List<string> _cUpdateTableWords =
new List<string>() {
" UPDATE ",
"INSERT",
"DELETE",
"DROP",
"TRUNCATE" };
32 private static List<string> lisTablasSistema =
new List<string>() {
"[LOG_ANALISIS]",
".LOG_ANALISIS",
"[CONNECT], [LOG_ERROR]" };
37 private static List<string> lisIdentificadoresTablas =
new List<string>() {
".DBO.",
".INFORMATION_SCHEMA.",
".SYS." };
39 private static ulong TotalPeticiones = 0;
40 private static ulong RespuestasDeCache = 0;
41 private static ulong InsercionesEnCache = 0;
43 private static int MaximoDeTablas = 0;
44 private static ulong EliminacionesItemsDeCache = 0;
45 private static ulong EliminacionesTablasDeCache = 0;
46 private static List<string> ListaClavesFallidas =
new List<string>();
48 static Dictionary<string, List<string>> _IndiceTablas =
new Dictionary<string, List<string>>();
49 static Dictionary<string, long> _CosteConsulta =
new Dictionary<string, long>();
50 private static long AhorroCoste = 0;
63 public static T GetObjectFromCache<T>(
string tcNomItem,
int tnTiempoEnCache, Func<T> toFuncionObtencionDatos)
67 ObjectCache loCache = MemoryCache.Default;
68 var objetoDeCache = (T)loCache[tcNomItem];
69 if (objetoDeCache == null)
73 CacheItemPolicy policy =
new CacheItemPolicy
75 AbsoluteExpiration = DateTimeOffset.Now.AddMinutes(tnTiempoEnCache)
78 Stopwatch timer =
new Stopwatch();
81 objetoDeCache = toFuncionObtencionDatos();
84 var coste = timer.ElapsedMilliseconds;
86 loCache.Set(tcNomItem, objetoDeCache, policy);
90 var tablasAfectadas = ObtenerTablasDelQuery(tcNomItem);
91 tablasAfectadas.ForEach(t => RegistrarQuerys(t, tcNomItem));
92 RegistrarCoste(tcNomItem, coste);
99 AhorroCoste += ObtenerCoste(tcNomItem);
105 ComprobarSiHayModificacionDeDatos(tcNomItem);
107 return objetoDeCache;
110 private static bool elQueryModificaDatos(
string tcQuery)
112 if (!
string.IsNullOrWhiteSpace(tcQuery) && _cUpdateTableWords.Any(lcWord => tcQuery.ToUpper().Contains(lcWord.ToUpper())))
119 private static bool elQueryModificaDatos(
string tcQuery, out List<string> tablasAfectadas)
121 tablasAfectadas =
new List<string>();
122 if (elQueryModificaDatos(tcQuery))
124 tablasAfectadas = ObtenerTablasDelQuery(tcQuery);
133 private static List<string> ObtenerTablasDelQuery(
string tcQuery)
136 var partesDelQuery = tcQuery.ToUpper().Split(
' ');
138 var listaTablas = partesDelQuery.Where(p => lisIdentificadoresTablas.Any(ident => p.ToUpper().Contains(ident))).ToList();
139 if (listaTablas.Count > MaximoDeTablas)
140 MaximoDeTablas = listaTablas.Count;
142 if (!listaTablas.Any())
143 ListaClavesFallidas.Add(tcQuery);
153 public static void ComprobarSiHayModificacionDeDatos(
string tcQuery)
155 #if PoCv2 //Si se modifican datos hago saltar la cache 156 if (elQueryModificaDatos(tcQuery, out List<string> tablasAfectadas))
158 if (lisTablasSistema.Any(lcWord => tcQuery.ToUpper().Contains(lcWord.ToUpper())))
160 Trace.WriteLineIf(Debugger.IsAttached,
" => Evitar que el query haga saltar la cache => " + queryToTrace(tcQuery),
"Cache");
164 tablasAfectadas.ForEach(t => BorrarQuerysCacheTabla(t));
165 Trace.WriteLineIf(Debugger.IsAttached,
"Query que modifica datos => " + queryToTrace(tcQuery),
"Cache");
170 if (elQueryModificaDatos(tcQuery))
172 if (lisTablasSistema.Any(lcWord => tcQuery.ToUpper().Contains(lcWord.ToUpper())))
173 Trace.WriteLineIf(Debugger.IsAttached,
" => Evitar que el query haga saltar la cache => " + queryToTrace(tcQuery),
"Cache");
177 Trace.WriteLineIf(Debugger.IsAttached,
"Query que hace saltar la cache => " + queryToTrace(tcQuery),
"Cache");
184 private static void BorrarQuerysCacheTabla(
string tabla)
186 if (_IndiceTablas.ContainsKey(tabla))
188 EliminacionesTablasDeCache++;
190 ObjectCache loCache = MemoryCache.Default;
192 foreach (
string key
in _IndiceTablas[tabla])
194 EliminacionesItemsDeCache++;
198 Trace.WriteLineIf(Debugger.IsAttached, $
"Eliminar tabla de la cache {tabla} con {_IndiceTablas[tabla].Count} items registrados",
"Cache");
200 _IndiceTablas[tabla].Clear();
201 _IndiceTablas.Remove(tabla);
205 private static void RegistrarQuerys(
string tabla,
string key)
207 if (_IndiceTablas.ContainsKey(tabla))
209 if (!_IndiceTablas[tabla].Contains(key))
211 _IndiceTablas[tabla].Add(key);
216 _IndiceTablas.Add(tabla,
new List<string>() { key });
220 private static void RegistrarCoste(
string key,
long coste)
222 if (_CosteConsulta.ContainsKey(key))
223 _CosteConsulta[key] = coste;
225 _CosteConsulta.Add(key, coste);
228 private static long ObtenerCoste(
string key)
230 if (_CosteConsulta.ContainsKey(key))
232 return _CosteConsulta[key];
239 private static string queryToTrace(
string tcQuery)
241 if (tcQuery.Length > MAX_LEGHT_TRACE)
242 return tcQuery.Substring(0, MAX_LEGHT_TRACE) +
"*** TRUNCADO ***";
252 public static bool ContaninsKey(
string tcKey)
254 ObjectCache cache = MemoryCache.Default;
255 return cache.Contains(tcKey);
258 internal static void VaciarCache()
261 EstadoDeLaCache(
"Vaciado final");
262 _IndiceTablas.Clear();
263 ReiniciarEstadisticas();
265 Trace.WriteLineIf(Debugger.IsAttached,
"*********** Vaciado final de cache *************",
"Cache");
268 ObjectCache loCache = MemoryCache.Default;
273 internal static void ResetCache()
276 EstadoDeLaCache(
"Reset de cache");
277 _IndiceTablas.Clear();
278 ReiniciarEstadisticas();
280 Trace.WriteLineIf(Debugger.IsAttached,
"*********** Reset de cache *************",
"Cache");
284 ObjectCache loCache = MemoryCache.Default;
289 private static void ReiniciarEstadisticas()
292 RespuestasDeCache = 0;
294 EliminacionesTablasDeCache = 0;
295 EliminacionesItemsDeCache = 0;
296 InsercionesEnCache = 0;
298 ListaClavesFallidas.Clear();
301 public static void EstadoDeLaCache(
string momento =
"sin especificar")
303 ObjectCache loCache = MemoryCache.Default;
304 var items = loCache.Count();
306 var tablas = _IndiceTablas.Count;
307 var listaTablas =
new List<string>();
309 foreach (var tabla
in _IndiceTablas.Keys)
311 claves += _IndiceTablas[tabla].Count;
312 listaTablas.Add(tabla);
317 if (Debugger.IsAttached)
319 Trace.WriteLineIf(Debugger.IsAttached, $
"*********** ESTADO DE LA CACHE ({momento}) *************",
"Cache");
320 Trace.WriteLineIf(Debugger.IsAttached, $
"Items {items}",
"Cache");
321 Trace.WriteLineIf(Debugger.IsAttached, $
"Total peticiones al motor SQL {TotalPeticiones}",
"Cache");
322 Trace.WriteLineIf(Debugger.IsAttached, $
"Peticiones atendidas con elementos de cache {RespuestasDeCache}",
"Cache");
323 Trace.WriteLineIf(Debugger.IsAttached, $
" * Estimación de coste ahorrado acumulado con cache {AhorroCoste} ms.",
"Cache");
324 Trace.WriteLineIf(Debugger.IsAttached, $
"Máximo de tablas de un elemento de cache {MaximoDeTablas}",
"Cache");
325 Trace.WriteLineIf(Debugger.IsAttached, $
"Diccionario con {tablas} tablas y {claves} claves",
"Cache");
326 Trace.WriteLineIf(Debugger.IsAttached, $
"Inserciones en cache {InsercionesEnCache}",
"Cache");
327 Trace.WriteLineIf(Debugger.IsAttached, $
"Eliminaciones de indices {EliminacionesTablasDeCache}",
"Cache");
328 Trace.WriteLineIf(Debugger.IsAttached, $
"Eliminaciones de elementos {EliminacionesItemsDeCache}",
"Cache");
329 Trace.WriteLineIf(Debugger.IsAttached, $
"Memoria (del proceso, no de la cache): ",
"Cache");
361 private static void traceUsoDeMemoria()
363 Process currentProcess =
System.Diagnostics.Process.GetCurrentProcess();
364 long totalBytesOfMemoryUsed = currentProcess.WorkingSet64;
365 Trace.WriteLineIf(Debugger.IsAttached,
"Total: " + totalBytesOfMemoryUsed +
" bytes - " + (totalBytesOfMemoryUsed * 0.000001) +
" Mb." ,
"Cache");
372 public static class QueryCacheExtensions
378 public static void __Clear(
this ObjectCache cache)
380 List<string> cacheKeys = cache.Select(kvp => kvp.Key).ToList();
381 foreach (
string cacheKey
in cacheKeys)
383 cache.Remove(cacheKey);