CreatePersistent dbConnection and multi-threaded test execution - Is Effort thread safe?

Apr 12, 2015 at 9:56 PM
I'm not sure if this is discussed elsewhere, but I'm trying to use the create persistent dbConnection method to save time in each of our unit tests. (We have about 1000 unit tests and the step to create the connection is substantial portion of the runtime).

Everything works fine when we run our tests using a single thread (VS default). However, when we turn on running tests in parallel, execution appears to hang when using a persistent connection. CreateTransient dbconnection with parallel tests works fine.

Anyways, I would love to use the persistent DB connection as it appears to save about 33% of total test execution time in single threaded execution.

Thanks for the excellent work on this tool!
Apr 13, 2015 at 12:17 PM
If you use persistent connection then all of your tests will run against the same instance of the database. In your case they will run even concurrently. Theoretically this should work as NMemory should handle concurrent transactions without any issue. However I never really addressed the initialisation part of the fake database, so it is possible that something is not completely thread safe during that phase. Does the test runner start to hang right at the beginning?

Btw I am not really a fan of using persistent connection for multiple tests, because it makes setting up the tests much harder and the tests may fail in ways that are really hard to reproduce. So I would rather start to think how to optimise the db setup phase.
Apr 13, 2015 at 4:57 PM

I think you are correct, it seems like there is something during the initialization phase. I tried debugging the unit tests in the configuration that hangs and eventually saw the following exception occur during a call to the CreatePersistent method.

From a writing of tests standpoint, using a persistent DB is not really a challenge for us. The way we set up our test data takes our multi-tenant design into account so everything worked perfectly in single threaded mode. Faster performance on db setup phase would be much appreciated in any case!

System.Reflection.TargetInvocationException was unhandled by user code
Message=Exception has been thrown by the target of an invocation.
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor)
   at System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters)
   at Effort.Internal.Common.DatabaseReflectionHelper.CreateTable(Database database, Type entityType, IKeyInfo primaryKeyInfo, MemberInfo identityField, Object[] constraintFactories)
   at Effort.Internal.DbManagement.DbContainer.Initialize(DbSchema schema)
   at Effort.Internal.DbManagement.DbContainer.Initialize(StoreItemCollection edmStoreSchema)
   at Effort.Provider.EffortProviderServices.<>c__DisplayClass4.<DbCreateDatabase>b__3(DbConnection x)
   at Effort.Provider.EffortProviderServices.Wrap[T](DbConnection connection, Func`2 action)
   at Effort.Provider.EffortProviderServices.DbCreateDatabase(DbConnection connection, Nullable`1 commandTimeout, StoreItemCollection storeItemCollection)
   at System.Data.Entity.Core.Common.DbProviderServices.CreateDatabase(DbConnection connection, Nullable`1 commandTimeout, StoreItemCollection storeItemCollection)
   at System.Data.Entity.Core.Objects.ObjectContext.CreateDatabase()
   at Effort.EntityConnectionFactory.CreateEntityConnection(MetadataWorkspace metadata, DbConnection connection)
   at Effort.EntityConnectionFactory.CreatePersistent(String entityConnectionString, IDataLoader dataLoader)
   at Effort.EntityConnectionFactory.CreatePersistent(String entityConnectionString)
   at SchedulePro.Tests.UnitTests.Fakes.FakeRepositoryModule.Load() in FakeRepositoryModule.cs:line 31
   at Ninject.Modules.NinjectModule.OnLoad(IKernel kernel)
   at Ninject.KernelBase.Load(IEnumerable`1 m)
   at Ninject.ModuleLoadExtensions.Load(IKernel kernel, INinjectModule[] modules)
   at Tests.UnitTests\BaseTest.cs:line 149
   at BuildGraphWithLimitsRunToCompletionTest() in 
RequirementGraphSearchServiceTests.cs:line 1413
InnerException: System.NullReferenceException
   Message=Object reference not set to an instance of an object.
        at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add)
        at NMemory.Concurrency.TableLockConcurrencyManager.RegisterTable(ITable table)
        at NMemory.Tables.TableCollection.RegisterTable(ITable table)
        at NMemory.Tables.TableCollection.Create[TEntity,TPrimaryKey](IKeyInfo`2 primaryKey, IdentitySpecification`1 identitySpecification)
        at Effort.Internal.Common.DatabaseReflectionHelper.WrapperMethods.CreateTable[TEntity,TPrimaryKey](Database database, IKeyInfo`2 primaryKeyInfo, Expression`1 identity, Object[] constraintFactories)