In order to get early-warning of a slow or potentially slow areas, I'd like to have an Interceptor for NHibernate that can act as a performance monitor, so that any database operation that takes more than a given time raises an event and (importantly) a full stacktrace into the application's logs.
Interceptors seemed to be a good window into this. However, having experimented, there doesn't seem to be anyway to catch a "just-back-from-SQL" event:
OnPreFlush and OnPostFlush work on full batches where writes are involved, but aren't invoked on read events.
OnPrepareStatement() seems to be the best to put start measuring, but to stop?
For read events, OnLoad might be the place to stop the clock, but it's called once-per-entity returned - how do I know when I've got to the end of all entities?
For write events, I can't see any post-SQL event (other than those that work on the entire batch - OnPostFlush and OnAfterTransaction; I get the impression OnSave, OnFlushDirty, etc are called before the actual database call occurs - happy to be corrected though).
From what I can tell, documentation is heavily lacking on exactly what the pipeline order is with NHibernate's interaction with the database, and thus when in that pipeline different events and interceptor calls are called in relation to the actual SQL execution itself.
This is something that needs to be permanently available, sitting in the background, pretty much that requires no human intervention except when slow queries are detected. It also needs to run headless on a large farm of servers, so interactive tools such as NHibernate Profiler are out: it's literally something that we can enable and forget about, letting it log as and when appropriate.
Is there anything I have missed or misunderstood?
I had a similar problem. I wanted measure and log all queries that goes through NHibernate.
What I did is I wrote a custom batching factory (in this case I work with oracle) but you can apply the same technique to any db:
1-) Implement batcher factory, (in this case I am extending existing factory)
public class OracleLoggingBatchingBatcherFactory : OracleDataClientBatchingBatcherFactory
{
public override IBatcher CreateBatcher(ConnectionManager connectionManager, IInterceptor interceptor)
{
return new OracleLoggingBatchingBatcher(connectionManager, interceptor);
}
}
2-) Implement the Batcher itself (in this case I am extending existing batcher). Make sure you inherit IBatcher again since we want our new methods to be called
public class OracleLoggingBatchingBatcher : OracleDataClientBatchingBatcher, IBatcher
{
.... // here override ExecuteNonQuery, DoExecuteBatch and ExecuteReader.
//You can do all kind of intercepting, logging or measuring here
//If they are not overrideable just implement them and use "new" keyword if necessary
//since we inherit IBatcher explicitly it will work polymorphically.
//Make sure you call base implementation too or re-implement the method from scratch
}
3-) Register the factory via NHibernate config:
<property name="adonet.factory_class">OracleLoggingBatchingBatcherFactory, MyAssembly</property>
Related
I'd like to send some SQL to the database at the start of the (database) session.
For example SET TIME ZONE 'user-timezone', or set their client IP address to a custom variable (a "GUC" in postgres).
How can I automate this in Hibernate?
EclipseLink has a postAcquireClientSession event, but I don't see one with Hibernate.
The easier way to do this is to implement a custom org.hibernate.service.jdbc.connections.spi.ConnectionProvider an example of which is provided here and override getConnection, so that it runs your SQL.
Other methods that are messier and may or may not work include using Integrators to setup Event Listeners, which listen at the wrong level and are listed in the following Stack Overflow question. Information on integrators is available from here.
Interceptors like Event Listeners also listen at the wrong level. An example of them is here
The other more complicated ways are subclassing the SessonFactory implementation to allow you to see a custom statistics provider which will then get called as part of the connectionOpened callback in JDBCContext if statistics are enabled.
Or alternatively you could subclass either the SessonFactory Implementation and JDBC context implementation or Session Implementation in order to customise the same connectionOpened callback to run your SQL.
this can be done via the hibernate session:
session.doWork(new Work() {
#Override
public void execute(Connection connection) throws SQLException {
connection.prepareStatement("SET TIME ZONE 'user-timezone'").execute();
}
});
This can be either be done with a utility method or applied selectively via a #Before aspect.
I have been trying for three days to figure out this NHibernatefacility thing with Castle and wcf and it's really getting frustrating.
After solving a good dozen of errors, i have come to this one which seems pretty obvious but i can't solve.
This is my global.asax's Application_Start
container.AddFacility<AutoTxFacility>();
container.Register(Component.For<INHibernateInstaller>().ImplementedBy<NHibernateInstaller>());
container.AddFacility<NHibernateFacility>();
container.AddFacility<WcfFacility>();
container.Register(
Component.For<IAuthService>().ImplementedBy<AuthService>().LifestylePerWcfOperation(),
Component.For<IUserRepository>().ImplementedBy<UserRepository>().LifestylePerWcfOperation(),
Component.For<IActionWebService>().ImplementedBy<ActionWebService>().Named("ActionWebService").LifestylePerWcfOperation(),
Component.For<ISession>().LifeStyle.PerWcfOperation().UsingFactoryMethod(x => container.Resolve<ISessionManager>().OpenSession()));
This works for the first request. After that, it comes up with this error.
The factory was disposed and can no longer be used.
Object name: 'this'.
the error is happening in my userrepository in this line
[Transaction]
public virtual User GetUserByCredentials(string email, string password)
{
using (var tx = session())
{
return tx.QueryOver<User>().Where(x => x.Email == email && x.Password == password).SingleOrDefault();
}
}
I am having a feeling this has to do with the LIfestyle. I have tried multiple combinations but unsuccessful. I don't know what to do at this point. I got into this Castle thing with all the facilities (that are supposed to make life easier) and it's really complicated due to the lack of documentation. I haven't been able to find a descent guide to implement all of this together, not to mention something that is not 4 years old.
Help Please!
Sorry for not finding this question previously.
The most likely reason you're getting this error message is that you are re-registering the ISession. The point of the facility is to provide that support for you.
I also read in your comment that you have set the ISession to singleton. That should never ever be done, because any single fault on it an you will crash and burn and you'll have to throw away the full container (which most often is the composition root of the application, so you have to reboot the application).
The point of the facility is to give you AOP-based transactions, and then you need to have your transactions as close to the GUI or command layer as possible. Child operation, such as reading should not be wrapped in singular transactions with [Transaction] because they do not denote the transactional boundary for your operation.
Instead, look at your surface API and see where you have methods being called that are supposed to run with ACID; this is where you put the attributes.
In your case, it seems that your transactional boundaries are around the WCF calls. What you'd need to do is to replace the lifestyle that the ISession is registered with.
If you have a look at the c'tor for NHibernateFacility, you'll find an option to pass transient lifestyle; if all of your components depending on ISession are transient you'll be good to go with Transient lifestyle on ISession, because it is guaranteed to only live for as long as the object taken from the composition root/container lives.
The 'real' fix is to extend the facility, from my github, with an enum in the c'tor taking a PerWCFOperation value, and having the facility register ISessionManager and Func with those, similar to how it does with the three existing lifestyles.
I'm working on a system that performs bulk processing using NHibernate. I know that NHibernate was not designed for bulk processing, but nonetheless the system is working perfectly thanks to a number of optimizations.
The object at the lowest level of granularity (i.e. the root of my aggregates) has a number of string properties that cannot (or, it does not make sense to) be modeled as many-to-one's (e.g. "Comment"). In reality, the fields in the DB corresponding to these properties take only so many values (for example because most - but not all - comments are machine-generated), with the result that when hydrating tons of objects, lots of memory is wasted by having thousands and thousands of instances of strings with the same values.
I was thinking of optimizing this scenario transparently by creating my own NHibernate custom type that enhances NHibernate's StringType by overriding NullSafeGet() and doing a dictionary lookup to return the same instance of each string occurrence over and over. In other words, I would perform a kind of string interning myself. The use of a custom type allows me to select which properties of which objects should be "interned" by just specifying this type in the mapping files.
Ideally, I would like to "stick" this dictionary into the session, so that the lifetime of this string pool is tied with the lifetime of the first level cache. After all, from our system's point of view, it makes sense to intialize this string pool at the same time a session and its first-level cache are initialized, and to nuke the string pool at the same time a session is closed. It is also a desirable property that concurrent sessions are completely isolated from each other by having their own private dictionaries.
Problem is, I can't find a way to "inject" a custom implementation of NHibernate's session into NHibernate itself so that an IType can access it at NullSafeGet() time, short of creating my own personal NHibernate code branch.
Is there a way to provide NHibernate with a custom session implementation?
I see three different approaches to solve this:
1. Use a interceptor
In the IInterceptor, you get:
void AfterTransactionBegin(ITransaction tx);
void BeforeTransactionCompletion(ITransaction tx);
2. Wrap opening and closing the session:
Opening and closing the session is an explicit call. It should be easy to wrap this into a method.
public ISession OpenSession()
{
var session = sessionFactory.CreateSession();
StringType.Initialize();
}
You could make it much nicer. I wrote a transaction service, which has events. Then you could handle begin transaction and end transaction events.
3. Don't attach the string cache to the session
It doesn't need to be related to the session. The strings are immutable objects, it doesn't hurt when you mix them between sessions. To avoid that the cache grows unlimitedly, you could write your own or use an existing "most recently used"-cache. After growing to a certain size, it throws away the oldest items.
This would probably require some time to implement, but would be very nice and easy to use.
I'm working with an application right now that uses a third-party API for handling some batch email-related tasks, and in order for that to work, we need to store some information in this service. Unfortunately, this information (first/last name, email address) is also something we want to use from our application. My normal inclination is to pick one canonical data source and stick with it, but round-tripping to a web service every time I want to look up these fields isn't really a viable option (we use some of them quite a bit), and the service's API requires the records to be stored there, so the duplication is sadly necessary.
But I have no interest in peppering every method throughout our business classes with code to synchronize data to the web service any time they might be updated, and I also don't think my entity should be aware of the service to update itself in a property setter (or whatever else is updating the "truth").
We use NHibernate for all of our DAL needs, and to my mind, this data replication is really a persistence issue - so I've whipped up a PoC implementation using an EventListener (both PostInsert and PostUpdate) that checks, if the entity is of type X, and any of fields [Y..Z] have been changed, update the web service with the new state.
I feel like this is striking a good balance between ensuring that our data is the canonical source and making sure that it gets replicated transparently and minimizing the chances for changes to fall through the cracks and get us into a mismatch situation (not the end of the world if eg. the service is unreachable, we just do a manual batch update later, but for everybody's sanity in the general case, the goal is that we never have to think about it), but my colleagues and I still have a degree of uncomfortableness with this way forward.
Is this a horrid idea that will invite raptors into my database at inopportune times? Is it a totally reasonable thing to do with an EventListener? Is it a serviceable solution to a less-than-ideal situation that we can just make do with and move on forever tainted? If we soldier on down this road, are there any gotchas I should be wary of in the Events pipeline?
In case of unreliable data stores (web service in your case), I would introduce a concept of transactions (operations) and store them in local database, then periodically pull them from DB and execute against the Web Service (other data store).
Something like this:
public class OperationContainer
{
public Operation Operation; //what ever operations you need CRUD, or some specific
public object Data; //your entity, business object or whatever
}
public class MyMailService
{
public SendMail (MailBusinessObject data)
{
DataAcceessLair<MailBusinessObject>.Persist(data);
OperationContainer operation = new OperationContainer(){Operation=insert, Data=data};
DataAcceessLair<OperationContainer>.Persist(operation);
}
}
public class Updater
{
Timer EverySec;
public void OnEverySec()
{
var data = DataAcceessLair<OperationContainer>.GetFirstIn(); //FIFO
var webServiceData = WebServiceData.Converr(data); // do the logic to prepare data for WebService
try
{
new WebService().DoSomething(data);
DataAcceessLair<OperationContainer>.Remove(data);
}
}
}
This is actually pretty close to the concept of smart client - technically not logicaly. Take a look at book: .NET Domain-Driven Design with C#: Problem-Design-Solution, chapter 10. Or take a look at source code from the book, it's pretty close to your situation: http://dddpds.codeplex.com/
I have a need to inspect the set of attached entities that would be persisted if I called Flush() on a given session. (I'm writing code that accesses a Session as part of a generic pipeline before saving and it can be used in any number of contexts.)
I find myself wishing that there were a method like
mySession.GetPersistentEntities()
so I could inspect them and perform some preprocessing.
Anyone know of a way to do this?
Thanks,
Jeff
No, NHibernate's ISession does not expose anything like that. You can either:
Track these instances yourself (not recommended)
Use standard NHibernate mechanisms:
Event listeners (e.g. IFlushEventListener, ISaveOrUpdateEventListener)
Interceptors (IInterceptor.OnFlushDirty(), OnSave())
You could "hack" a little bit into the session context:
ISession session;
var sessionContext = session.GetSessionImplementation().PersistenceContext;
foreach(var entity in sessionContext.EntitiesByKey.Values)
{
// do anything with the entity
}
However, in your case I would use flush event listeners or an interceptor.