-
Notifications
You must be signed in to change notification settings - Fork 934
NH-4077 - do not auto-flush while already flushing #682
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
NH-4077 - do not auto-flush while already flushing #682
Conversation
/// being called multiple times. | ||
/// </summary> | ||
/// <returns>A disposable to dispose when auto flush should be restored.</returns> | ||
IDisposable SuspendAutoFlush(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Needing it from an other internal class than the session, and factoring it by the way.
finally | ||
{ | ||
AfterOperation(success); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as previous AfterOperation
case.
finally | ||
{ | ||
AfterOperation(success); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Discrepancy: the AfterOperation(success);
was previously put after decrementing back dontFlushFromFind
, meaning after operations could trigger auto-flush. Now it is done before leaving the new using, so before re-enabling auto-flushes.
But AfterOperation
does in fact nothing if we are inside a transaction. And when we are not inside a transaction, auto-flush is anyway already disabled. So this change has no consequences with current code.
finally | ||
{ | ||
dontFlushFromFind--; | ||
AfterOperation(success); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as previous AfterOperation
case.
finally | ||
{ | ||
AfterOperation(success); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as previous AfterOperation
case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I backported the changes to 4.1.1.GA and it worked just fine with our old bugged code; so I believe the changes are good...at least for what we use it for.
/// <inheritdoc/> | ||
public IDisposable SuspendAutoFlush() | ||
{ | ||
_suspendAutoFlushCount++; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any thoughts on putting this in the SuspendAutoFlushHelper
ctor? IMO it makes it more obvious that the operation is (supposed to be) symmetric, plus you cannot forget one half if you need to reuse that class elsewhere (although unlikely, given it's private
)
Also, thoughts on using Interlocked.Increment
instead for atomicity?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make sens moving to constructor. But as sessions are inherently non-thread safe and so are supposed to be used only by one thread at a time, I will not introduce a thread safety guard here.
820b080
to
b4eaab9
Compare
I think it would make sense to move |
So, moving by the way the |
Yes, I meant to write it, but forgot |
#region IDisposable Support | ||
private bool _disposedValue; // To detect redundant calls | ||
|
||
protected virtual void Dispose(bool disposing) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This class is private, so I dont see the reasons to have virtual members and/or implement Disposable "correctly"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Default standard snippet from VS, including the anti-redundant call (which is a must for this case).
And well, we could consider than when disposing
is false (meaning being called by GC, explicit dispose lacking) we should left the counter incremented for avoiding "fixing" the lack of dispose call through GC and hiding somewhat it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now moved to SessionImpl
, pointless virtual
removed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But, then we miss finalizer, which should call Dispose(false)
then
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No more needed since Fx 2 I believe, handled by GC without any finalizer. The fact that SessionImpl
still does that is an old remnant of Fx 1.
Current recommendations are explicit about not needing a finalizer in almost all cases nowaday.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But anyway no one is going to call Dispose(false)
, so I don't think we need to follow this obsolete pattern. Also, I don't think that framework will call Dispose(false)
or Dispose()
on the finalization stage. We need to prevent calling Dispose()
twice, but anyway, without this check we'll get NullReferenceException
as session would be unset by the time of second time Dispose()
is called. Which, I believe, is clearer than allowing Dispose()
to be called twice with no op on the second call.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From where is the protected virtual Dispose(boolean disposing)
pattern said to be obsolete? Only implementing explicitly a finalizer is obsolete for most cases. This doc seems quite current.
Now for such a little class it should not ever have consequences, but I just rather stick to recommended patterns when they are strongly recommended as in previously linked documentation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see where it says that finalize is optional:
What I see is:
You must override Finalize to ensure that unmanaged resources are disposed of if your System.IDisposable.Dispose implementation is not called by a consumer of your type.
But again, we implement the IDisposable
interface not for it's purpose, but because of convenience of using
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The fact is my implementation is invalid for the disposing pattern because I should not touch the managed session when disposing is false: if both were dereferenced, the session could have been GC already and should no more be used in any way.
I am probably wrong by telling the GC call disposing(false) itself, it is likely only useful if some later inheritor decide to implement its own finalizer though this should no more be the way to go (use SafeHandle instead for not having to handle directly the releasing of unmanaged resources).
But well, following the recommended practice just avoids having to think about such details while not being a significant overhead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the recommended variation and doesn't require overriding the System.Object.Finalize method.
This is in the first point above. The "must" is in the second way of implementing dispose, while the recommended way is the first.
b4eaab9
to
f13afb1
Compare
I would implement the helper as following: class SuspendAutoFlushHelper : IDisposable
{
SessionImpl _session;
public SuspendAutoFlushHelper(SessionImpl session)
{
_session = session;
_session._suspendAutoFlushCount++;
}
public void Dispose()
{
if (_session == null)
throw new ObjectDisposedException("The SuspendAutoFlushHelper has been disposed already");
_session._suspendAutoFlushCount--;
_session = null;
}
} This just fit for purpose. The The reason for implementing it like this, because a) we just abusing the |
Also, there is no point of |
I not really agree with the Now for this hijacked dispose, ok, I will code it your way. (Note: private is not enough, only sealed is. We can derived from a private nested class within the same containing class.) |
1ec68ca
to
aaee9d9
Compare
Thanks for reviewing, I have seen in some previous merge to master than updating the branch (merging from master) then merging back to master was causing some noise like here for the test warnings. (Which actual commits are to be found below on 9 august.) So I am going to rebase instead, discarding the review by the way. |
np
…On Wed, 6 Sep 2017 at 10:02 PM, Frédéric Delaporte ***@***.***> wrote:
Thanks for the reviewing,
I have seen in some previous merge to master than updating the branch
(merging from master) then merging back to master was causing some noise
like here
<https://github.com/nhibernate/nhibernate-core/commits/master?after=5d8219b39c3f904ee0b3a04e6b692c1bbd478270+30>
for the tests warning.
So I am going to rebase instead, discarding the review by the way.
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#682 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAI1l6xJ7vaIFVMxWZPQ_wgiFiz11978ks5sfm24gaJpZM4PL_0g>
.
|
both fixtures use an evil PostXxxListener to cause/force an auto-flush of the session that is currently being committed inside a transaction. for PostInsertFixture it leads to to duplicate inserts. for PostUpdateFixture it causes an ArgumentOutOfRange exception by clearing a list that is currently being iterated.
aaee9d9
to
0cba4e0
Compare
NH-4077.
The trouble was due to auto-flushing getting triggered from a flush event, altering the flush action list while it was iterating over it.