views:

43

answers:

5

Using NHibernate, I have just attempted to save a graph of objects using NHibernate. The save failed due to a not-null constraint violation.

I am now finding that a table in the database corresponding to one of the objects in the graph now appears to be locked. I cannot query it. Whenever I try, it just sits there doing nothing until I cancel the query. This has been the case for about half an hour.

In the save code, the transaction is first committed and then disposed.

SELECT @@TRANCOUNT against the database gives 0.

Any ideas a) What's going on and b) How can I get the table back?

Thanks

David

A: 

If you have access to the database, try running sp_lock to see who has the table locked.

Edit: That will return records with several columns of information described here. One of those columns is spid. You can use sp_who (or sp_who2) to see who is logged in, and look for the spid holding the lock.

If you have "view server state" permission, you can also use sys.dm_tran_locks.

DOK
sp_lock tells me I have two KEY locks, a PAG lock and a TAB lock. How do I find out who the user is? And how do I remove the locks?
David
A: 

@@TRANCOUNT is scoped to the current connection.

Assuming you have sufficient permissions on the server.

select hostname,program_name, spid,text  from master.sys.sysprocesses 
outer apply sys.dm_exec_sql_text(sql_handle) s
WHERE open_tran>0

gives any connections with currently open transactions along with the last text executed by that connection. I'm using sysprocesses for the reasons here.

If the above query shows your culprit then you can get the spid and use kill <spidnumber> to unceremoniously roll it back.You might want to consider setting XACT_ABORT on for future queries so errors won't leave transactions open.

Martin Smith
Invalid object name 'sysprocesses'. What schema is it in?
David
For SQL2005+ it is in the `sys` schema but the `sys` is optional for backwards compatibility so sounds like you haven't got permissions to view it. Can you do something on your application side to close the connection? Also do you have a similar problem with @DOK's suggestions?
Martin Smith
If I can't view it, noone can! It's my database... DOK's suggestions ran okay.
David
@Martin: XACT_ABORT > 'Specifies whether SQL Server automatically rolls back the current transaction when a Transact-SQL statement raises a run-time error.' Is this not the default behaviour???
David
@David - No that's not the default behaviour. Sorry try `select * from master..sysprocesses WHERE open_tran>0`. That will give you the spid of the session with the open transaction (say 53). If you are sure that is the correct session you can use `kill 53` to unceremoniously kill it and roll it back.
Martin Smith
And to check that it is the right spid you can use `declare @handle varbinary(100) select @handle = most_recent_sql_handle from sys.dm_exec_connections where session_id=53 select text from sys.dm_exec_sql_text(@handle)` (Replace 53 as appropriate)
Martin Smith
What does the last one do? The result just seems to be a load of code as text...
David
@Martin - your comment about using select * from master..sysprocesses and kill 53 is the one that's working for me. Would you mind editing answer to reflect this?
David
@David - Will do. The last one gets you the text last executed by connection so you could verify you were killing the right one.
Martin Smith
@Martin - thank you for the tip about XACT_ABORT. I feel like I need to handle the problem in a higher app level, so I'm catching exceptions in the repository code and rolling back the transaction instead.
David
A: 

Are you sure you properly disposed of your session? That's usually the cause of such problems...

DanP
A: 

try running this:

SELECT
    r.session_id AS spid
        ,r.cpu_time,r.reads,r.writes,r.logical_reads 
        ,r.blocking_session_id AS BlockingSPID
        ,LEFT(OBJECT_NAME(st.objectid, st.dbid),50) AS ShortObjectName
        ,LEFT(DB_NAME(r.database_id),50) AS DatabaseName
        ,s.program_name
        ,s.login_name
        ,OBJECT_NAME(st.objectid, st.dbid) AS ObjectName
        ,SUBSTRING(st.text, (r.statement_start_offset/2)+1,( (CASE r.statement_end_offset
                                                                  WHEN -1 THEN DATALENGTH(st.text)
                                                                  ELSE r.statement_end_offset
                                                              END - r.statement_start_offset
                                                             )/2
                                                           ) + 1
                  ) AS SQLText
    FROM sys.dm_exec_requests                          r
        JOIN sys.dm_exec_sessions                      s ON r.session_id = s.session_id
        CROSS APPLY sys.dm_exec_sql_text (sql_handle) st
    WHERE r.session_id!=@@SPID

it will tell you who is blocking your query

KM
I'm sorry, this doesn't return any rows!
David
then there isn't any activity on that database, remove the `WHERE` and you will see the row for this actual query. Open multiple SSMS windows, run a slow query in one window and then quicky change to this window and run this query, you can see rows then
KM
A: 

Regardless of any other DB-specific behaviors, you should rollback the transaction immediately after an error. That shouldn't leave anything locked.

Diego Mijelshon
I've worked (as a developer) with databases for years and I always thought transactions were rolled back automatically if the database errors. Funny time to find out how wrong I was!
David
Thank you Diego. I have updated my repository code accordingly and now the table isn't locked after database errors.
David