Diagnosing NHibernate Second‑Level Cache Issues: A Practical Guide
When NHibernate’s second‑level cache isn’t working, performance suffers. This guide walks you through the most common symptoms, root causes, ordered checks, fixes, and when to seek help.
03 Aug 2025, 10:34 UTC

Problem Statement
When your application’s queries keep hitting the database instead of the cache, performance drops and you lose the benefits of NHibernate’s second‑level cache. The symptoms can be subtle – missing cache logs, stale data, or silent cache misses – and the root cause is often a mis‑configured or disabled cache component.
Typical Symptoms
| Symptom | Description |
|---|---|
No Cache Hit entries in logs | Cache not initialized or disabled. |
| Queries hit the DB on every run | Cache provider failed to store results. |
| Stale data after updates | Improper locking strategy or missing invalidation. |
| Runtime exceptions mentioning region names | Region mis‑configuration or missing definition. |
| Memory spikes after enabling cache | Eviction policy not set or region size too large. |
Root Causes & Diagnostic Table
| Cause | Diagnostic Check | Fix |
|---|---|---|
| Cache not enabled in NHibernate configuration | Search cache.use_second_level_cache in hibernate.cfg.xml or fluent config. | Set cache.use_second_level_cache="true" and rebuild SessionFactory. |
| Wrong provider assembly or missing connection string | Review cache.provider_class and provider‑specific settings. | Install correct provider NuGet package, update assembly reference, and provide valid connection string. |
| Entity not marked cacheable | Check mapping files or Fluent API for Cacheable(). | Add Cacheable() to the mapping or Cacheable() in Fluent. |
| Region name mismatch or missing region definition | Verify region names in mappings match those in provider config. | Align region names or add missing region definitions. |
| Incorrect locking strategy | Look for cache.use_minimal_puts or locking strategy in logs. | Choose appropriate strategy: read-only for immutable data, read-write for mutable. |
| SessionFactory not rebuilt after changes | Confirm that SessionFactory is recreated after config changes. | Rebuild SessionFactory programmatically or restart the application. |
Ordered Diagnostic Checklist
- Verify Cache Flag
var cfg = new Configuration(); cfg.Configure(); Console.WriteLine(cfg.GetProperty("cache.use_second_level_cache"));Expected output:
true. Iffalse, enable it. - Confirm Provider Initialization
Enable NHibernate logging at
DEBUGlevel. Search forCache provider initializedmessages. If missing, checkcache.provider_classand assembly load errors. - Check Entity Cacheability
// Mapping XML example <class name="Order" table="Orders" cache="read-write" />or Fluent API:
public class OrderMap : ClassMap<Order> { public OrderMap() { Cache.ReadWrite(); } } - Run a Test Query and Inspect Logs
using (var session = sessionFactory.OpenSession()) { var order = session.Get<Order>(1); }Check logs for
Cache Missfollowed byCache Hiton subsequent runs. If only misses appear, the cache region isn’t storing results. - Inspect Provider’s Cache Region
For Infinispan, use
http://localhost:9990/rest/cache/Orders. For MemoryCache, useMemoryCache.Default.Get("Orders:1")in a debug session. - Monitor Memory Usage
Use
Process.GetCurrentProcess().PrivateMemorySize64or a profiler to detect rapid growth after enabling cache. - Validate Eviction Policy
Check provider config for
max-entriesoreviction-policy. Adjust to avoid memory overflow. - Confirm Locking Strategy
If using
read-write, ensure updates trigger cache invalidation. Test by updating an entity and re-querying.
Fixes Tied to Findings
- Enable Cache Flag – Add
cache.use_second_level_cache="true"tohibernate.cfg.xmlor fluent config. - Correct Provider – Add NuGet package
NHibernate.SQLCacheor provider‑specific package, updatecache.provider_class. - Mark Entities – Add
Cacheable()in mappings. - Align Region Names – Ensure mapping
cache="read-write"matches provider region. - Set Locking Strategy – Use
cache.use_minimal_puts="true"for read‑only data orread-writefor mutable. - Rebuild SessionFactory – Call
sessionFactory.Close()and recreate after config changes. - Configure Eviction – Set
max-entriesoreviction-policy="LRU"in provider config.
Escalation Criteria
- If after all checks the cache still behaves incorrectly, verify that the provider version supports the features you need (e.g., Infinispan 12.x requires specific JARs).
- Consult the provider’s documentation for any known bugs or required JVM options.
- Engage the application’s DevOps or infrastructure team if memory usage remains high despite eviction settings.
- Open a support ticket with the provider vendor if you encounter provider‑specific exceptions or performance regressions.
Practical Verification Checklist
- Logs show
Cache Hitafter first query. - Provider’s cache region contains the entity key.
- Memory usage stabilizes after cache population.
- Updates to entities correctly invalidate cache entries.
Conclusion
By following the ordered diagnostic steps and matching findings to targeted fixes, you can quickly pinpoint why NHibernate’s second‑level cache isn’t delivering. Keep an eye on logs, provider metrics, and memory usage to confirm that the cache is both functional and efficient.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.