question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Potential threading issues with .Query<T>

See original GitHub issue

I’m running into a scenario where I’ve got calls into a class that handles some data, and noticed that occasionally I get Object reference not set to an instance of an object, or that the command has been disposed.

Upon googling, I found an old issue here (that I can’t seem to find now) that states that DataConnection is not thread safe, that a new DataConnection should be created from each thread.

My concern is I’m not always sure the origin in the calls to that data class. An example could be someone spawning a new task, then calling that pre-existing instance, or trying to query data from a timer callback.

I’m just trying to find any decent way of guard other users against running into this sort of thing.

Being that I don’t know enough about linq2db’s internals to know why it’s not thread safe, is it something that could feasibly be added?

I feel like at this point my only option to handle this (to ensure dataconnection per thread) would be to blindly create new dataconnections each and every call/attempt to do anything in my data class.

Exception message:
Stack trace:

System.ObjectDisposedException
Cannot access a disposed object.
Object name: 'OracleCommand'.
   at Oracle.ManagedDataAccess.Client.OracleCommand.ValidateStatePriorToExecution()
   at Oracle.ManagedDataAccess.Client.OracleCommand.ExecuteReader(Boolean requery, Boolean fillRequest, CommandBehavior behavior)
   at Oracle.ManagedDataAccess.Client.OracleCommand.ExecuteDbDataReader(CommandBehavior behavior)
   at LinqToDB.Data.DataConnection.ExecuteReader(CommandBehavior commandBehavior)
   at LinqToDB.Data.CommandInfo.Query[T]()

and

System.NullReferenceException
Object reference not set to an instance of an object.
   at Oracle.ManagedDataAccess.Client.OracleCommand.ExecuteReader(Boolean requery, Boolean fillRequest, CommandBehavior behavior)
   at Oracle.ManagedDataAccess.Client.OracleCommand.ExecuteDbDataReader(CommandBehavior behavior)
   at LinqToDB.Data.DataConnection.ExecuteReader(CommandBehavior commandBehavior)
   at LinqToDB.Data.CommandInfo.Query[T]()

Steps to reproduce

public void Main(){
   var db = new Db();
   var timer = new System.Timers.Timer();
   timer.Interval = 1000;
   timer.Elapsed += () => {
      db.Get<T>(...);
   };
   timer.Start();

   for(var i = 0; i< 100;i++){
      db.Get<T>(...);
   }

   
}

create a DataConnection, and timer, and loop querying from both. Eventually you’ll run into one of these exceptions.

Environment details

linq2db version: 2.7.4 (not sure this matters) Database Server: ? Database Provider: ? Operating system: ? .NET Framework: ?

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Comments:6 (5 by maintainers)

github_iconTop GitHub Comments

1reaction
sdanylivcommented, Jun 15, 2020

To solve the issue I have created a new DataConnection inside the ForEach code block. Thank you to help me to find the right direction.

You SHOULD create data connection for each thread. DataConnection is not thread safe.

@MaceWindu, i think guard for such improper usage is needed. Maybe in DataProvider.ExecuteScope().

1reaction
to11mtmcommented, Aug 29, 2019

I would posit that it’s not thread safe because that is a fairly common paradigm in many data access providers.

As a consideration; let’s say DataConnection was threadsafe. What would that mean when it comes to things like Transactions? How would that API work? (It’s certainly DOABLE, but it would look very different and perhaps not clean as one would think at first glance.)

I feel like at this point my only option to handle this (to ensure dataconnection per thread) would be to blindly create new dataconnections each and every call/attempt to do anything in my data class.

That’s a pretty standard pattern in ADO.NET.

/*
Helper methods like this are awesome if you are writing library code. 
Or even app code where the same query bits are being reused.
The _jobQueueTableConfiguration here is a thread safe container of strings and other thread safe primitives.
*/
        protected ITable<SqlCommonDbOddJobMetaData> QueueTable(DataConnection conn)
        {
            return conn.GetTable<SqlCommonDbOddJobMetaData>().TableName(_jobQueueTableConfiguration.QueueTableName); 
        }
        public virtual void MarkJobSuccess(Guid jobGuid)
        {
            //The ConnectionFactory here is thread safe; 
            //it just returns new DataConnection objects 
            //based on the configuration bits it is provided
            //(This example is from a library that supports multiple DBs)
            using (var conn = _jobQueueConnectionFactory.CreateDataConnection())
            {
                
                QueueTable(conn)
                    .Where(q => q.JobGuid == jobGuid)
                    .Set(q => q.Status, JobStates.Processed)
                    .Set(q => q.LockClaimTime, (DateTime?) null)
                    .Update();
            }
        }

Edit; IIRC NHibernate SessionFactory is one of the exceptions to this statement, but that is again an exception and not the rule; Session itself is not thread safe, and you mainly want SessionFactory as as singleton because it is relatively expensive.

In Linq2Db DataConnection is (in my experience) fairly inexpensive, and there is caching in much of the query pipeline where appropriate (which is usually the more expensive part).

Edit 2: Added more explanation to code above.

Read more comments on GitHub >

github_iconTop Results From Across the Web

c# - Query on Queues and Thread Safety
If I am understanding correctly, this method is not thread safe (assuming this specific instance of this object is known to multiple threads)....
Read more >
Query Suggest and Multi-Threading
In this blog post, I'll do a deep dive into how Query Suggest comes up with suggestions and describe our multi-threaded performance problem...
Read more >
Command-Query-Separation and multithreading safe ...
Now, with multi threading, I run into a major problem: the separation of the query and the command basically invalidates the result from...
Read more >
A Guide to Multithreading in SQL
In this article, we delved into the complexities of implementing multithreading in SQL, from basic concepts to advanced topics such as ...
Read more >
Potential pitfalls with PLINQ
Writing to non-thread-safe instance methods from a PLINQ query can lead to data corruption which may or may not go undetected in your...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found