0

When we released last friday, I received an error which I do not get on acceptance. The error message is:

could not execute update query[SQL: delete from dbo.MyTable where col1=? and col2=? and col3=? and col4=? and col5=?]

My C# code is as follows:

var hqlDelete = "DELETE MyTable m WHERE m.Col1 = :var_1 AND m.Col2 = :var_2 AND m.Col3= :var_3 AND m.Col4 = :var_4 AND m.Col5= :var_5";
var deletedEntities = session.CreateQuery(hqlDelete)
                             .SetString("var_1", variable1)
                             .SetString("var_2", variable2)
                             .SetString("var_3", variable3)
                             .SetString("var_4", variable4)
                             .SetString("var_5", variable5)
                             .ExecuteUpdate();
transaction.Commit();
session.Close();

Now, as I said, the error did not trigger when testing on acceptance. Also, when I test with the production database (code from my developer seat), it works without problems too.

The code is triggered when I call a web service and POST a "measurement" to it. The only difference is that I call the service when testing, and on production an other company sends measurements to the web service.

I think it might have something to do with the amount of sessions/transactions, but that would not really explain why the variables show up as ? in the error message.

Any ideas? Is there more information I could supply so you can help me with this one?

Edit: InnerExeption is

{"Transaction (Process ID 68) was deadlocked on lock | communication buffer resources with another process and has been chosen as the deadlock victim. Rerun the transaction."}

Tjab
  • 368
  • 1
  • 4
  • 18
  • 2
    Firstly, show full exception stack.. that part above would be just a beginning... the real and interesting info will follow... – Radim Köhler Mar 23 '16 at 07:35
  • 1
    As pointed by Radim, your error message is insufficient. It just tells what it can not do. Usually it gives some reasons. Log details of the exception, including its `InnerException`. If you do not have more, add log4net, configure it for logging at least warnings and above, and NHibernate should then issue detailed logs about the failure. – Frédéric Mar 23 '16 at 08:25
  • Ok thanks. I was hoping this was some typical mistake or something. It's kinda hard to debug, since I have to follow a release plan (I can release once a month) and therefore only test on acceptance. So even if I add log4net, I'll have to wait a month before I can see what the errors are. – Tjab Mar 23 '16 at 12:06
  • @Frédéric, I have updated the question. I was able to reproduce the error by testing excessively (spam the web service). I'll start googling on how to solve this now ;) – Tjab Mar 23 '16 at 13:55

1 Answers1

1

Solving deadlocks can be a hard matter, especially when using an ORM. Deadlocks usually occurs because locks on database objects are not acquired in the same order by different processes (or threads), causing them to wait for each other.

An ORM does not give you much control on lock acquiring order. You may rework your queries ordering, but this could be tedious. Especially when caching causes some of them to not hit DB. Moreover, it should be done with the same ordering on any other application using the same database.

You may detect deadlock errors and do what the message say: retry the whole process. With NHibernate, this means discarding the current session and retry your whole unit of work.

If your database is SQL Server, there is a default setting which greatly increase deadlocks risk: the disabling of read committed snapshot mode. If it is disabled on your database, you may greatly reduce deadlock risks by enabling it. This mode allows reads under read committed isolation level to cease issuing read locks.

You may check this setting with

select snapshot_isolation_state_desc, is_read_committed_snapshot_on
    from sys.databases 
    where name = 'YourDbName'

You may enable this setting with

alter database YourDbName
    set allow_snapshot_isolation on

alter database YourDbName
    set read_committed_snapshot on

This requires having none running transaction on the target db. And of course, this requires admin rights on DB.

On an application for which I was not having the option to change this setting, I had to go a more quirky way: setting NHibernate default isolation mode (connection.isolation configuration parameter) to ReadUncommitted. My application was mostly read-only, and I was elevating the isolation mode explicitly on the few transactions having to read then write data (using session.BeginTransaction(System.Data.IsolationLevel.ReadCommitted) by example).

You should also check the isolation modes which are currently used by all applications using the database: are some of them using higher isolation level than actually required? (RepeatableRead and Serializable should be avoided if possible.) This is a time consuming process since it requires a good understanding of isolation levels, while studying each use case for determining what is the appropriate minimal isolation level.

Frédéric
  • 9,364
  • 3
  • 62
  • 112
  • Thank you for the very elaborate answer! The snapshot isolation/read committed is indeed set to false/0, and I did not know of this setting. – Tjab Apr 04 '16 at 08:25
  • 1
    I have forgotten a point about read committed snapshot mode: it causes more use of tempdb space. Your DBA should know about it. – Frédéric Apr 04 '16 at 08:53
  • Hmm I need to look in to that too then. The database itself is quite large, talking about almost 400 million records in 1 table at the moment... don't know if it's 'the larger the table/db, the more the tempdb space is used', but if so. :) – Tjab Apr 04 '16 at 09:00
  • 1
    Tempdb use is more related to updated rows count than to rows global count, as far as I know. For more reliable answer on that subject, you should probably search for references elsewhere on the web, eventually asking on http://dba.stackexchange.com/ if not finding information relevant for your case. – Frédéric Apr 04 '16 at 09:18