FetchEntity and threads

Posts   
 
    
cylakes
User
Posts: 6
Joined: 07-Jan-2007
# Posted on: 07-Jan-2007 15:56:20   

Hello,

I have problems when calling FetchEntity (of the same entity) from several threads. Each thread uses his own DataAccessAdapter. The threads call FetchEntity of the same entity. The code created by llblgen or internal llblgen classes throw exceptions. Should I do special operation when working with threads?

Here come several stack traces (within llblgen code) of th exceptions I get:

case 1:

Stack Trace:
System.ArgumentException: Item has already been added.  Key in dictionary: "8549a191-91b4-49a0-bb2e-462510df12c0"  Key being added: "8549a191-91b4-49a0-bb2e-462510df12c0"
   at System.Collections.Hashtable.Insert(Object key, Object nvalue, Boolean add)
   at System.Collections.Hashtable.Add(Object key, Object value)
   at SD.LLBLGen.Pro.ORMSupportClasses.EntityBase2.SetEntitySyncInformation(String fieldName, IEntity2 relatedEntity, IEntityRelation relation)
   at MB.DB.LLBLGen.Auto.EntityClasses.EnlbsScannedRawEntity.SetupSyncEnlCommon(IEntity2 relatedEntity) in q:\Src\Lib\CLR\IDFMU\MB.DB.LLBLGen.Auto\DataStructures\EntityClasses\EnlbsScannedRawEntity.cs:line 1539
   at MB.DB.LLBLGen.Auto.EntityClasses.EnlbsScannedRawEntity.SetRelatedEntity(IEntity2 relatedEntity, String fieldName) in q:\Src\Lib\CLR\IDFMU\MB.DB.LLBLGen.Auto\DataStructures\EntityClasses\EnlbsScannedRawEntity.cs:line 408
   at MB.DB.LLBLGen.Auto.EntityClasses.EnlCommonEntity.set_EnlbsScannedRaw(EnlbsScannedRawEntity value) in q:\Src\Lib\CLR\IDFMU\MB.DB.LLBLGen.Auto\DataStructures\EntityClasses\EnlCommonEntity.cs:line 17562
   at MB.DB.LLBLGen.Auto.EntityClasses.EnlCommonEntity.SetRelatedEntityProperty(String propertyName, IEntityCore entity) in q:\Src\Lib\CLR\IDFMU\MB.DB.LLBLGen.Auto\DataStructures\EntityClasses\EnlCommonEntity.cs:line 1601
   at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.MergeNormal(IEntityCollection2 rootEntities, IPrefetchPathElement2 currentElement, Boolean rootEntitiesArePkSide)
   at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchPrefetchPath(IEntityCollection2 rootEntities, IRelationPredicateBucket filterBucket, Int64 maxNumberOfItemsToReturn, ISortExpression sortClauses, IPrefetchPath2 prefetchPath)
   at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchEntity(IEntity2 entityToFetch, IPrefetchPath2 prefetchPath, Context contextToUse)
   at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchEntity(IEntity2 entityToFetch, IPrefetchPath2 prefetchPath)

case 2:

Stack Trace:
System.NullReferenceException: Object reference not set to an instance of an object.
   at MB.DB.LLBLGen.Auto.EntityClasses.EnlbsScannedRawEntity.DesetupSyncEnlCommon(Boolean signalRelatedEntity) in q:\Src\Lib\CLR\IDFMU\MB.DB.LLBLGen.Auto\DataStructures\EntityClasses\EnlbsScannedRawEntity.cs:line 1500
   at MB.DB.LLBLGen.Auto.EntityClasses.EnlbsScannedRawEntity.SetupSyncEnlCommon(IEntity2 relatedEntity) in q:\Src\Lib\CLR\IDFMU\MB.DB.LLBLGen.Auto\DataStructures\EntityClasses\EnlbsScannedRawEntity.cs:line 1531
   at MB.DB.LLBLGen.Auto.EntityClasses.EnlbsScannedRawEntity.SetRelatedEntity(IEntity2 relatedEntity, String fieldName) in q:\Src\Lib\CLR\IDFMU\MB.DB.LLBLGen.Auto\DataStructures\EntityClasses\EnlbsScannedRawEntity.cs:line 408
   at MB.DB.LLBLGen.Auto.EntityClasses.EnlCommonEntity.set_EnlbsScannedRaw(EnlbsScannedRawEntity value) in q:\Src\Lib\CLR\IDFMU\MB.DB.LLBLGen.Auto\DataStructures\EntityClasses\EnlCommonEntity.cs:line 17562
   at MB.DB.LLBLGen.Auto.EntityClasses.EnlCommonEntity.SetRelatedEntityProperty(String propertyName, IEntityCore entity) in q:\Src\Lib\CLR\IDFMU\MB.DB.LLBLGen.Auto\DataStructures\EntityClasses\EnlCommonEntity.cs:line 1601
   at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.MergeNormal(IEntityCollection2 rootEntities, IPrefetchPathElement2 currentElement, Boolean rootEntitiesArePkSide)
   at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchPrefetchPath(IEntityCollection2 rootEntities, IRelationPredicateBucket filterBucket, Int64 maxNumberOfItemsToReturn, ISortExpression sortClauses, IPrefetchPath2 prefetchPath)
   at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchEntity(IEntity2 entityToFetch, IPrefetchPath2 prefetchPath, Context contextToUse)
   at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchEntity(IEntity2 entityToFetch, IPrefetchPath2 prefetchPath)

case 3:

Stack Trace:
System.NullReferenceException: Object reference not set to an instance of an object.
   at MB.DB.LLBLGen.Auto.EntityClasses.EnlbsScannedRawEntity.SetupSyncEnlCommon(IEntity2 relatedEntity) in q:\Src\Lib\CLR\IDFMU\MB.DB.LLBLGen.Auto\DataStructures\EntityClasses\EnlbsScannedRawEntity.cs:line 1540
   at MB.DB.LLBLGen.Auto.EntityClasses.EnlbsScannedRawEntity.SetRelatedEntity(IEntity2 relatedEntity, String fieldName) in q:\Src\Lib\CLR\IDFMU\MB.DB.LLBLGen.Auto\DataStructures\EntityClasses\EnlbsScannedRawEntity.cs:line 408
   at MB.DB.LLBLGen.Auto.EntityClasses.EnlCommonEntity.set_EnlbsScannedRaw(EnlbsScannedRawEntity value) in q:\Src\Lib\CLR\IDFMU\MB.DB.LLBLGen.Auto\DataStructures\EntityClasses\EnlCommonEntity.cs:line 17562
   at MB.DB.LLBLGen.Auto.EntityClasses.EnlCommonEntity.SetRelatedEntityProperty(String propertyName, IEntityCore entity) in q:\Src\Lib\CLR\IDFMU\MB.DB.LLBLGen.Auto\DataStructures\EntityClasses\EnlCommonEntity.cs:line 1601
   at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.MergeNormal(IEntityCollection2 rootEntities, IPrefetchPathElement2 currentElement, Boolean rootEntitiesArePkSide)
   at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchPrefetchPath(IEntityCollection2 rootEntities, IRelationPredicateBucket filterBucket, Int64 maxNumberOfItemsToReturn, ISortExpression sortClauses, IPrefetchPath2 prefetchPath)
   at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchEntity(IEntity2 entityToFetch, IPrefetchPath2 prefetchPath, Context contextToUse)
   at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchEntity(IEntity2 entityToFetch, IPrefetchPath2 prefetchPath)

case 4:

System.NullReferenceException: Object reference not set to an instance of an object.
   at SD.LLBLGen.Pro.ORMSupportClasses.EntityBase2.SetEntitySyncInformation(String fieldName, IEntity2 relatedEntity, IEntityRelation relation)
   at MB.DB.LLBLGen.Auto.EntityClasses.EnlbsScannedRawEntity.SetupSyncEnlCommon(IEntity2 relatedEntity) in q:\Src\Lib\CLR\IDFMU\MB.DB.LLBLGen.Auto\DataStructures\EntityClasses\EnlbsScannedRawEntity.cs:line 1539
   at MB.DB.LLBLGen.Auto.EntityClasses.EnlbsScannedRawEntity.SetRelatedEntity(IEntity2 relatedEntity, String fieldName) in q:\Src\Lib\CLR\IDFMU\MB.DB.LLBLGen.Auto\DataStructures\EntityClasses\EnlbsScannedRawEntity.cs:line 408
   at MB.DB.LLBLGen.Auto.EntityClasses.EnlCommonEntity.set_EnlbsScannedRaw(EnlbsScannedRawEntity value) in q:\Src\Lib\CLR\IDFMU\MB.DB.LLBLGen.Auto\DataStructures\EntityClasses\EnlCommonEntity.cs:line 17562
   at MB.DB.LLBLGen.Auto.EntityClasses.EnlCommonEntity.SetRelatedEntityProperty(String propertyName, IEntityCore entity) in q:\Src\Lib\CLR\IDFMU\MB.DB.LLBLGen.Auto\DataStructures\EntityClasses\EnlCommonEntity.cs:line 1601
   at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.MergeNormal(IEntityCollection2 rootEntities, IPrefetchPathElement2 currentElement, Boolean rootEntitiesArePkSide)
   at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchPrefetchPath(IEntityCollection2 rootEntities, IRelationPredicateBucket filterBucket, Int64 maxNumberOfItemsToReturn, ISortExpression sortClauses, IPrefetchPath2 prefetchPath)
   at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchEntity(IEntity2 entityToFetch, IPrefetchPath2 prefetchPath, Context contextToUse)
   at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchEntity(IEntity2 entityToFetch, IPrefetchPath2 prefetchPath)

Where MB.DB.LLBLGen.Auto\DataStructures is code generated by llblgen for our database.

case 5:

Stack Trace:
System.ArgumentOutOfRangeException: Specified argument was out of the range of valid values.
Parameter name: Index was out of range.  Must be non-negative and less than the size of the collection.
   at System.Collections.CollectionBase.System.Collections.IList.get_Item(Int32 index)
   at SD.LLBLGen.Pro.ORMSupportClasses.EntityCollectionBase2.get_Item(Int32 index)
   at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.MergeNormal(IEntityCollection2 rootEntities, IPrefetchPathElement2 currentElement, Boolean rootEntitiesArePkSide)
   at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchPrefetchPath(IEntityCollection2 rootEntities, IRelationPredicateBucket filterBucket, Int64 maxNumberOfItemsToReturn, ISortExpression sortClauses, IPrefetchPath2 prefetchPath)
   at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchPrefetchPath(IEntityCollection2 rootEntities, IRelationPredicateBucket filterBucket, Int64 maxNumberOfItemsToReturn, ISortExpression sortClauses, IPrefetchPath2 prefetchPath)
   at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchPrefetchPath(IEntityCollection2 rootEntities, IRelationPredicateBucket filterBucket, Int64 maxNumberOfItemsToReturn, ISortExpression sortClauses, IPrefetchPath2 prefetchPath)
   at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchEntity(IEntity2 entityToFetch, IPrefetchPath2 prefetchPath, Context contextToUse)
   at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchEntity(IEntity2 entityToFetch, IPrefetchPath2 prefetchPath)

                          Thanks ,
                                 Cylakes
Chester
Support Team
Posts: 223
Joined: 15-Jul-2005
# Posted on: 07-Jan-2007 19:04:18   

Are you trying to fetch the same entity from multiple threads? If so, why?

(edit) Also, what version and release date of LLBLGen are you using?

cylakes
User
Posts: 6
Joined: 07-Jan-2007
# Posted on: 08-Jan-2007 08:22:52   

Hello,

First, answers to the questions.

1) The version info llblgen version: version: 1.0.2004.2 final Released on June 13th,2005

The dlls: dll: SD.LLBLGen.Pro.ORMSupportClasses.NET11 Version: 1.0.2004.2

RunTime Version: v1.1.4322

dll: SD.LLBLGen.Pro.DQE.Oracle10g.NET11 Version : 1.0.2004.2 RunTime Version: v1.1.4322

2) The reason for trying to fetch the same entity from multiple threads: We implement web services . Each client of the web service may fetch the same entity. It may occur simultaniously. The client may fetch the same records in the tables or different records in the same tables. For example , we have EMPLOYEE table. Different web services (that run simultaniously) may fetch the same record in EMPLOYEE table or different records in EMPLOYEE table. Each web service is a different thread.


And now some more information : 1) We have quite a "healthy" prefetch path when calling FetchEntity. 2) When we enclose the FetchEntity in critical section (lock()) , no problems occur.

                        Thanks,
                        Cylakes
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39927
Joined: 17-Aug-2003
# Posted on: 08-Jan-2007 10:02:44   

Could you please download the latest build of the 1.0.2004.2 runtime libs (and templates)? They're available in the customer area. If possible, please migrate to 1.0.2005.1 (which is a free upgrade for you).

Frans Bouma | Lead developer LLBLGen Pro
cylakes
User
Posts: 6
Joined: 07-Jan-2007
# Posted on: 08-Jan-2007 15:09:55   

Hello, We are at a final stage of system tests. It may be problematic for us to upgrade to new run time libraries or to new llblgen version (since we have to repeat all tests...) Will the upgrade of the run time libs fix the problem? Is it a known problem? Is the code generated by llblgen thread safe? Thanks, Cylakes

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39927
Joined: 17-Aug-2003
# Posted on: 08-Jan-2007 19:07:11   

cylakes wrote:

Hello, We are at a final stage of system tests. It may be problematic for us to upgrade to new run time libraries or to new llblgen version (since we have to repeat all tests...) Will the upgrade of the run time libs fix the problem?

that's hard to tell, as there were a series of bugfixes to the 1.0.2004.2 runtimes after the date of your designer build. The 1.0.2004.2 build available on our website is usable in your current code. I'd also update the templates to the latest build.

Is it a known problem?

A null-ref exception can be caused by many things.

Is the code generated by llblgen thread safe? Thanks, Cylakes

No, only the singleton code is threadsafe (internally in the runtime lib) as there aren't any locks etc. used. So if you are setting and getting data from a single entity by multiple threads, you will run into problems unless you use a lock yourself to mark a critical section.

Frans Bouma | Lead developer LLBLGen Pro
cylakes
User
Posts: 6
Joined: 07-Jan-2007
# Posted on: 09-Jan-2007 10:08:27   

Hello, Maybe i did not explain myself well about the way we work with threads. Each thread has its own objects. The threads do not share the same entity objects. Here is a demo of our work for each thread:

using System;
using System.Data;
using System.Data.Common;
using System.ComponentModel;
using Oracle.DataAccess.Client;
using Oracle.DataAccess.Types;
using SD.LLBLGen.Pro.ORMSupportClasses;
using SD.LLBLGen.Pro.DQE.Oracle;
using MB.DB.LLBLGen.Auto;                   // classes generated by LLBLGEN
using MB.DB.LLBLGen.Auto.EntityClasses;     // classes generated by LLBLGEN
using MB.DB.LLBLGen.Auto.FactoryClasses;    // classes generated by LLBLGEN
using MB.DB.LLBLGen.Auto.HelperClasses;     // classes generated by LLBLGEN



class ReadDBLogicManager
{
    //-------------------------------------------------------------------
    // This is the code skeleton that is performed by each thread.
    // Each thread has its own DataAccessAdapter
    // Each thread has it own EnlCommonEntity
    // However, there may be situations that different threads access the same records in database.
    // When there is no lock statement we have the mentioned problems in 
    // FetchEntity. When we add the lock,there aer no problems.
    //-------------------------------------------------------------------
    public  void ThreadMain()
    {
        MB.DB.LLBLGen.Auto.DataAccessAdapter dataAcessAdapter;// inherits SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase
        dataAcessAdapter = new MBDataAccessAdapter(connectionString, keepConnectionOpen);
        MB.DB.LLBLGen.Auto.EntityClasses.EnlCommonEntity enlCommonEntity;// inherits  SD.LLBLGen.Pro.ORMSupportClasses.EntityBase2
        enlCommonEntity = new enlCommonEntity(...);
        
        IPrefetchPath2 prefetchPath = new PrefetchPath2((Int32)EntityType.EnlCommonEntity);
        prefetchPath.Add(....);
        prefetchPath.Add(....);
        prefetchPath.Add(....);

        Boolean doesExist;

//      lock(typeof(ReadDBLogicManager))
//      {
            // Executing query
            doesExist = dataAcessAdapter.FetchEntity(enlCommonEntity, prefetchPath);
//      }
        
        ....
        
        

    }
}

As I mentioned, in the code demo the lock solves the problems. Is working with several threads , where each thread has is own LLBLGEN generated objects, problematic? Should every call to llblgen objects be enclosed in lock statement?

                      Thanks, Cylakes
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39927
Joined: 17-Aug-2003
# Posted on: 10-Jan-2007 10:04:38   

The code looks OK. I have no idea why this can cause problems, as all your code is contained inside a method, and by that the objects created are local to the method, and thus to the thread running the method, i.o.w.: objects created by thread A running ThreadMain aren't visible by thread B and vice versa.

What's also odd is that a GUID is already present in a set according to the exceptions, which is odd as a guid is always unique.

Could you at least TRY the latest runtime lib builds and the latest templates for 1.0.2004.2 ? (thus latest templates -> regenerate code). So we can rule out bugs which are fixed in later builds of 1.0.2004.2 ?

Also, as you're using multi-threading, please add the compiler directive 'MULTICPU' to the dbspecific C# project.

Frans Bouma | Lead developer LLBLGen Pro
cylakes
User
Posts: 6
Joined: 07-Jan-2007
# Posted on: 14-Jan-2007 14:41:23   

Hello, 1)

Also, as you're using multi-threading, please add the compiler directive 'MULTICPU' to the dbspecific C# project.

How do I do it? 2)
Assuming that regenerating LLBLGEN does not help, is there any other alternatives? Thanks, Cylakes

RoyRichter
User
Posts: 1
Joined: 14-Jan-2007
# Posted on: 14-Jan-2007 14:52:24   

I would also like to get a list of common LLBL actions which are not threadsafe. Especially DataAccessAdapter, FetchEntity, FetchEntityCollection... Etc.

For example: "Using FetchEntity on a single cpu, single user, multi threaded application is not safe"

Thanks, Roy

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39927
Joined: 17-Aug-2003
# Posted on: 14-Jan-2007 15:30:01   

cylakes wrote:

Hello, 1)

Also, as you're using multi-threading, please add the compiler directive 'MULTICPU' to the dbspecific C# project.

How do I do it?

in the vs.net C# project properties.

2)
Assuming that regenerating LLBLGEN does not help, is there any other alternatives? Thanks, Cylakes

Well, I have no other idea why it fails. Our code is used in a lot of webapps for example, which are multi-threaded by default, so it's not something that is caused by a fundamental flaw.

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39927
Joined: 17-Aug-2003
# Posted on: 14-Jan-2007 15:31:03   

RoyRichter wrote:

I would also like to get a list of common LLBL actions which are not threadsafe. Especially DataAccessAdapter, FetchEntity, FetchEntityCollection... Etc.

For example: "Using FetchEntity on a single cpu, single user, multi threaded application is not safe" Thanks, Roy

DataAccessAdapter instances aren't thread safe. So per thread you need to create a NEW instance. Entity instances are as any other object: if you share them among threads, you have to do the locking yourself.

Frans Bouma | Lead developer LLBLGen Pro
cylakes
User
Posts: 6
Joined: 07-Jan-2007
# Posted on: 18-Jan-2007 14:54:33   

Hello, It was our problem . We used the same object of prefetch path in several threads. Thanks, Cylakes