0

im using nhibernate as the orm for my webservices and i have implemented a CustomSqlDriver. The reason why i created CustomSqlDriver is because i need to get and save each query (with their parameters values) that gets executed in the app to the db. The AdjustCommand method gets called each time a query takes place (select,update,etc) and in the respective order.

public class CustomSqlDriver : SqlClientDriver {
    private static readonly log4net.ILog log = log4net.LogManager.GetLogger("CustomSqlClientDriverLogger");
    private static readonly Object _lock = new Object();

    public override void AdjustCommand(IDbCommand command)
    {
        base.AdjustCommand(command);
        var parameters = (SqlParameterCollection)command.Parameters;

        QueryType queryType = GetQueryType(command);
        string tableName = GetTableName(command.CommandText);
        string fullCommandText = GetFullQuery(command, parameters);

        Task.Run(() =>
        {
            lock (_lock)
            {
                log.Info($"Trying to save the query = {fullCommandText}");
                SaveToDb((int)queryType, fullCommandText, tableName);
            }
        });
    }
}

I need to save each query in order cause i will use them later in a different FIFO process. Cant do it in the same thread (cause it may be slow). As you can see, i tried using a lock on a static object within a Task.Run() and this works as long as the call to any webmethod is synchronous but if i call them in parallel, some rows lost order when they gets saved to the db.

Im not sure it this is the best approach D: Any help would be appreciated.

  • 1
    You cannot guarantee that the first query finished before a second query. That lock is arbitrary unless you are queuing up all queries server wide. You also are now forced to only ever run 1 web server if the FIFO is mandatory. – TyCobb Jul 03 '18 at 22:10
  • You misunderstood what the purpose of `lock` is. It only makes sure that no two or more threads are running the "locked" block at the same time. Whichever thread executes that block first, whichever executes second, etc. is of no concern for `lock` -- it just makes sure that one thread is executing that block, not which thread in particular. Also, Task.Run() itself does not guarantee execution of tasks in a specific order, either. If you need an ordered execution of your tasks, you need to queue the tasks in some way, for example with the help of `Task.ContinueWith`... –  Jul 03 '18 at 22:10
  • Can't you just store the timestamp inside the db? Why complicate things with locks? Then just order the results by that timestamp. – Federico Dipuma Jul 03 '18 at 22:12

1 Answers1

2

some rows lost order when they gets saved to the db.

Task.Run does not guarantee anything about order; items can be dequeued in just about any order, and even if they got dequeued in the right order and scheduled onto different threads, two threads can proceed differently and get to the lock in different orders. Even if they get to the lock in the right order, I don't think it is a strict guarantee that lock queue order will be preserved in all scenarios (more reading here) - just that only one will have the lock.

If you need FIFO: you need to define that FIFO to be: the order in which something gets into a reliable synchronous queue or list or similar. You can't add FIFO after the fact.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900