Is there a leak with 2005?

Posts   
 
    
Ren
User
Posts: 42
Joined: 01-Jul-2005
# Posted on: 01-Nov-2005 01:00:56   

I upgraded my code from 1.0.2004.2 to 1.0.2005.1. Recompiled and everything looks okay.

I try my application which does some heavy processing and get the following error:

Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached. at System.Data.SqlClient.SqlConnectionPoolManager.GetPooledConnection(SqlConnectionString options, Boolean& isInTransaction) at System.Data.SqlClient.SqlConnection.Open() at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.OpenConnection() at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.ExecuteSingleRowRetrievalQuery(IRetrievalQuery queryToExecute, IEntityFields2 fieldsToFill, IFieldPersistenceInfo[] fieldsPersistenceInfo) at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchEntityUsingFilter(IEntityFields2 fieldsToFetch, IFieldPersistenceInfo[] persistenceInfos, IRelationPredicateBucket filter) at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchEntity(IEntity2 entityToFetch)

Now, this works using the older dlls. (I reverted my code back and used the old dlls)

I watched the app in debug mode and the memory keeps getting larger and larger. Let me know if you have any ideas. This is bothering me very very very much. My code that calls all has the DataAccessAdapter() (default) calls so somewhere it looks like its not closing.

Thanks, Ren

Paul.Lewis
User
Posts: 147
Joined: 22-Aug-2005
# Posted on: 01-Nov-2005 04:59:38   

Ren,

Can you pinpoint where in your code the leak is occuring? If in multiple code segments, are there any commonalities?

If you can provide a code example that results in memory leaks, we should be able to identify a cause.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39590
Joined: 17-Aug-2003
# Posted on: 01-Nov-2005 08:33:59   

With version below I mean: the real version, so not '2.0' or '2005' but 2.0.50727 or '2005 RTM'.

1) which .net version are you using 2) which sqlserver version are you using 3) is this a winforms app or a webapp? 4) could you paste some of YOUR code which is going wrong? Apparently the connection pool is full or something related.... This can be caused if you store dataaccessadapter objects somewhere (like in the session of asp.net)

Frans Bouma | Lead developer LLBLGen Pro
Ren
User
Posts: 42
Joined: 01-Jul-2005
# Posted on: 01-Nov-2005 19:34:10   

Okay here goes.

I took my .lgp file and opened it in the LLBLGen Pro Verision: 1.0.2005.1 Final (October 23rd, 2005) studio.

I rebuilt the project creating 2 projects (adapter). In visual studio .net 2003, I opend my solution and added references to my projects.

The DAL project I added the following 2 references: SD.LLBLGen.Pro.ORMSupportClasses.NET11.dll (10-24-2005 2:22:32 PM) SD.LLBLGen.Pro.DQE.SqlServer.NET11.dll (10-24-2005 2:22:34 PM)

My MiddleTier project I added: SD.LLBLGen.Pro.ORMSupportClasses.NET11.dll (10-24-2005 2:22:32 PM)

And my UI Tier I added: (Web Application) SD.LLBLGen.Pro.ORMSupportClasses.NET11.dll (10-24-2005 2:22:32 PM)

Built the solution and everything went fine.

On my development machine i get the following message in my event log: aspnet_wp.exe (PID: 268sunglasses was recycled because memory consumption exceeded the 305 MB (60 percent of available RAM). (The browser says: Server Application Unavailable)

On a test machine I got that message I sent earlier.

When I watch the task manager I see the memory start increasing almost at once when I click a button to start off a process.

The first method call looks like this:


        public static EntityCollection GetNewBillableItemEntitiesToCalculate()
        {
            IRelationPredicateBucket bucket = new RelationPredicateBucket();
            
            //batch id = null, invoice id = null, contract ver > 0, Hold = false, Suppress = false
            bucket.PredicateExpression.Add(PredicateFactory.CompareNull(BillableItemFieldIndex.BatchId));
            bucket.PredicateExpression.AddWithAnd(PredicateFactory.CompareNull(BillableItemFieldIndex.InvoiceId));
            bucket.PredicateExpression.AddWithAnd(PredicateFactory.CompareValue(BillableItemFieldIndex.ClientContractVersionId, ComparisonOperator.GreaterThan,0));
            bucket.PredicateExpression.AddWithAnd(PredicateFactory.CompareValue(BillableItemFieldIndex.HoldIndicator, ComparisonOperator.Equal, false));
            bucket.PredicateExpression.AddWithAnd(PredicateFactory.CompareValue(BillableItemFieldIndex.SuppressIndicator, ComparisonOperator.Equal, false));

            EntityCollection billableItems = new EntityCollection(new BillableItemEntityFactory());
            DataAccessAdapter adapter = new DataAccessAdapter();
            ISortExpression sorterBillableItems = new SortExpression(
                SortClauseFactory.Create(BillableItemFieldIndex.ClientContractVersionId, SortOperator.Ascending));

            IPrefetchPath2 prefetchPath = new PrefetchPath2((int)EntityType.BillableItemEntity);
            prefetchPath.Add(BillableItemEntity.PrefetchPathBillableItemAttributes);

            adapter.FetchEntityCollection(billableItems,bucket,0,sorterBillableItems,prefetchPath);
            return billableItems;
        }

Now this seemed to work before I rebuilt to 2005 and when I was referencing the dll's dated 4-25-2005. With the new code this is where the 'crap hits the fan' it seems. Upon the adapter.FetchEntityCollection call the code hangs and the memory starts creeping until it crashes. Could it be that I am writing the prefetch path part wrong? I profiled the query and it looks like this:


exec sp_executesql N'SELECT [Billing].[dbo].[Billable_Item].[Bill_Id] AS [Id], [Billing].[dbo].[Billable_Item].[Bill_Item_Event_Dt_Tm] AS [EventDateTime], [Billing].[dbo].[Billable_Item].[Bill_Item_Effective_Dt_Tm] AS [EffectiveDateTime], [Billing].[dbo].[Billable_Item].[Bill_Item_Desc] AS [Description], [Billing].[dbo].[Billable_Item].[Bill_Item_Qty] AS [Quantity], [Billing].[dbo].[Billable_Item].[Bill_Item_Hold_Ind] AS [HoldIndicator], [Billing].[dbo].[Billable_Item].[Bill_Item_Internal_Dollar_Amt] AS [InternalDollarAmt], [Billing].[dbo].[Billable_Item].[Bill_Item_External_Dollar_Amt] AS [ExternalDollarAmt], [Billing].[dbo].[Billable_Item].[Bill_Item_Rate_Tree_NodeId] AS [RateTreeNodeId], [Billing].[dbo].[Billable_Item].[Bill_Item_Type_Id] AS [TypeId], [Billing].[dbo].[Billable_Item].[Bill_Item_Batch_Id] AS [BatchId], [Billing].[dbo].[Billable_Item].[Clnt_Cntrct_Ver_Id] AS [ClientContractVersionId], [Billing].[dbo].[Billable_Item].[Invc_Id] AS [InvoiceId], [Billing].[dbo].[Billable_Item].[Invc_Grp_Id] AS [InvoiceGroupId], [Billing].[dbo].[Billable_Item].[Org_Id] AS [OrganizationId], [Billing].[dbo].[Billable_Item].[Prdct_Type_Id] AS [ProductTypeId], [Billing].[dbo].[Billable_Item].[Qty_Type_Id] AS [QuantityTypeId], [Billing].[dbo].[Billable_Item].[Rvw_Bill_Item_Id] AS [ReviewBillItemId], [Billing].[dbo].[Billable_Item].[Bill_Item_Suppress_Ind] AS [SuppressIndicator], [Billing].[dbo].[Billable_Item].[Data_Feed_Run_Id] AS [DataFeedRunId], [Billing].[dbo].[Billable_Item].[Insert_Prsn_Id] AS [InsertPersonId] FROM [Billing].[dbo].[Billable_Item]  WHERE ( [Billing].[dbo].[Billable_Item].[Bill_Item_Batch_Id] IS NULL AND [Billing].[dbo].[Billable_Item].[Invc_Id] IS NULL AND [Billing].[dbo].[Billable_Item].[Clnt_Cntrct_Ver_Id] > @ClientContractVersionId1 AND [Billing].[dbo].[Billable_Item].[Bill_Item_Hold_Ind] = @HoldIndicator2 AND [Billing].[dbo].[Billable_Item].[Bill_Item_Suppress_Ind] = @SuppressIndicator3) ORDER BY [Billing].[dbo].[Billable_Item].[Clnt_Cntrct_Ver_Id] ASC', N'@ClientContractVersionId1 int,@HoldIndicator2 bit,@SuppressIndicator3 bit', @ClientContractVersionId1 = 0, @HoldIndicator2 = 0, @SuppressIndicator3 = 0

I don't see any prefetch query stuff anywhere.

I re-wrote the method call and removed the prefetch path part so it looks like this:


        public static EntityCollection GetNewBillableItemEntitiesToCalculate(int recordsToPull)
        {

            IRelationPredicateBucket bucket = new RelationPredicateBucket();
            
            //batch id = null, invoice id = null, contract ver > 0, Hold = false, Suppress = false
            bucket.PredicateExpression.Add(PredicateFactory.CompareNull(BillableItemFieldIndex.BatchId));
            bucket.PredicateExpression.AddWithAnd(PredicateFactory.CompareNull(BillableItemFieldIndex.InvoiceId));
            bucket.PredicateExpression.AddWithAnd(PredicateFactory.CompareValue(BillableItemFieldIndex.ClientContractVersionId, ComparisonOperator.GreaterThan,0));
            bucket.PredicateExpression.AddWithAnd(PredicateFactory.CompareValue(BillableItemFieldIndex.HoldIndicator, ComparisonOperator.Equal, false));
            bucket.PredicateExpression.AddWithAnd(PredicateFactory.CompareValue(BillableItemFieldIndex.SuppressIndicator, ComparisonOperator.Equal, false));            
            EntityCollection billableItems = new EntityCollection(new BillableItemEntityFactory());
            DataAccessAdapter adapter = new DataAccessAdapter(true);
            ISortExpression sorterBillableItems = new SortExpression(
                SortClauseFactory.Create(BillableItemFieldIndex.ClientContractVersionId, SortOperator.Ascending));

            //IPrefetchPath2 prefetchPath = new PrefetchPath2((int)EntityType.BillableItemEntity);
            //  prefetchPath.Add(BillableItemEntity.PrefetchPathBillableItemAttributes);

            //  adapter.FetchEntityCollection(billableItems,bucket,0,sorterBillableItems,prefetchPath);
            try
            {
                adapter.FetchEntityCollection(billableItems,bucket,0,sorterBillableItems);

                foreach(BillableItemEntity b in billableItems)
                {
                    adapter.FetchEntityCollection(b.BillableItemAttributes,b.GetRelationInfoBillableItemAttributes());
                }

                return billableItems;
            }
            finally 
            {
                adapter.CloseConnection();
            }
        }

And this works. The memory didn't just keep climbing until it crapped out.

So it looks like the previous version of the .dll dated (4-25-2005) and the generated code from LLBLGen Pro v1.0.2004.2 worked with the prefetch call.

With the .dll dated (10-24-2005 ) and the generated code from LLBLGen Pro 1.0.2005.1 the method caused the error.

With the .dll dated (10-24-2005 ) and the generated code from LLBLGen Pro 1.0.2005.1 with the removal of the prefetch portion from the method call, the method worked and memory stayed level.

Unless I am doing the prefetch stuff wrong I am worried about this. Let me know what you think.

Thanks, Ren

I am using .net version: v1.1.4322 SP1 SQLServer version: 2000 This is a web app.

Ren
User
Posts: 42
Joined: 01-Jul-2005
# Posted on: 01-Nov-2005 20:34:12   

Here is the profiled query when I run the 'older' version of the application using (4-25-2005) dlls and 1.0.2004.2 generated code using the prefetch path against adapter.FetchEntityCollection


exec sp_executesql N'
SELECT [Billing].[dbo].[Billable_Item].[Bill_Id] AS [Id],
[Billing].[dbo].[Billable_Item].[Bill_Item_Event_Dt_Tm] AS [EventDateTime],
[Billing].[dbo].[Billable_Item].[Bill_Item_Effective_Dt_Tm] AS [EffectiveDateTime],
[Billing].[dbo].[Billable_Item].[Bill_Item_Desc] AS [Description],
[Billing].[dbo].[Billable_Item].[Bill_Item_Qty] AS [Quantity],
[Billing].[dbo].[Billable_Item].[Bill_Item_Hold_Ind] AS [HoldIndicator],
[Billing].[dbo].[Billable_Item].[Bill_Item_Internal_Dollar_Amt] AS [InternalDollarAmt],
[Billing].[dbo].[Billable_Item].[Bill_Item_External_Dollar_Amt] AS [ExternalDollarAmt],
[Billing].[dbo].[Billable_Item].[Bill_Item_Rate_Tree_NodeId] AS [RateTreeNodeId],
[Billing].[dbo].[Billable_Item].[Bill_Item_Type_Id] AS [TypeId],
[Billing].[dbo].[Billable_Item].[Bill_Item_Batch_Id] AS [BatchId],
[Billing].[dbo].[Billable_Item].[Clnt_Cntrct_Ver_Id] AS [ClientContractVersionId],
[Billing].[dbo].[Billable_Item].[Invc_Id] AS [InvoiceId],
[Billing].[dbo].[Billable_Item].[Invc_Grp_Id] AS [InvoiceGroupId],
[Billing].[dbo].[Billable_Item].[Org_Id] AS [OrganizationId],
[Billing].[dbo].[Billable_Item].[Prdct_Type_Id] AS [ProductTypeId],
[Billing].[dbo].[Billable_Item].[Qty_Type_Id] AS [QuantityTypeId],
[Billing].[dbo].[Billable_Item].[Rvw_Bill_Item_Id] AS [ReviewBillItemId],
[Billing].[dbo].[Billable_Item].[Bill_Item_Suppress_Ind] AS [SuppressIndicator],
[Billing].[dbo].[Billable_Item].[Data_Feed_Run_Id] AS [DataFeedRunId],
[Billing].[dbo].[Billable_Item].[Insert_Prsn_Id] AS [InsertPersonId] 
FROM [Billing].[dbo].[Billable_Item] 
WHERE ( [Billing].[dbo].[Billable_Item].[Bill_Item_Batch_Id] IS NULL 
And [Billing].[dbo].[Billable_Item].[Invc_Id] IS NULL 
And [Billing].[dbo].[Billable_Item].[Clnt_Cntrct_Ver_Id] > @ClientContractVersionId1 
And [Billing].[dbo].[Billable_Item].[Bill_Item_Hold_Ind] = @HoldIndicator2 
And [Billing].[dbo].[Billable_Item].[Bill_Item_Suppress_Ind] = @SuppressIndicator3) 
ORDER BY [Billing].[dbo].[Billable_Item].[Clnt_Cntrct_Ver_Id] ASC', 
N'@ClientContractVersionId1 int,@HoldIndicator2 bit,@SuppressIndicator3 bit', 
@ClientContractVersionId1 = 0, @HoldIndicator2 = 0, @SuppressIndicator3 = 0

No hanging and continuing memory growth.. (some memory growth, but it gets reclaimed - normal operation) the profiler shows the 2nd query.


exec sp_executesql N'
SELECT [Billing].[dbo].[Billable_Item_Attribute].[Bill_Item_Attr_Id] AS [Id],
[Billing].[dbo].[Billable_Item_Attribute].[Bill_Item_Attr_Val] AS [Value],
[Billing].[dbo].[Billable_Item_Attribute].[Bill_Id] AS [BillId],
[Billing].[dbo].[Billable_Item_Attribute].[Col_Id] AS [ColId],
[Billing].[dbo].[Billable_Item_Attribute].[Attr_Type_Id] AS [AttrTypeId] 
FROM [Billing].[dbo].[Billable_Item_Attribute] 
WHERE ( [Billing].[dbo].[Billable_Item_Attribute].[Bill_Id] IN (
SELECT [Billing].[dbo].[Billable_Item].[Bill_Id] AS [Id] 
FROM [Billing].[dbo].[Billable_Item] 
WHERE ( [Billing].[dbo].[Billable_Item].[Bill_Item_Batch_Id] IS NULL 
And [Billing].[dbo].[Billable_Item].[Invc_Id] IS NULL 
And [Billing].[dbo].[Billable_Item].[Clnt_Cntrct_Ver_Id] > @ClientContractVersionId1 
And [Billing].[dbo].[Billable_Item].[Bill_Item_Hold_Ind] = @HoldIndicator2 
And [Billing].[dbo].[Billable_Item].[Bill_Item_Suppress_Ind] = @SuppressIndicator3)))', 
N'@ClientContractVersionId1 int,@HoldIndicator2 bit,@SuppressIndicator3 bit', 
@ClientContractVersionId1 = 0, @HoldIndicator2 = 0, @SuppressIndicator3 = 0

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39590
Joined: 17-Aug-2003
# Posted on: 01-Nov-2005 21:22:50   

BillableItem - BillableItemAttribute is an 1:n relation?

If you do:


using(DataAccessAdapter adapter = new DataAccessAdapter())
{
            ISortExpression sorterBillableItems = new SortExpression(
                SortClauseFactory.Create(BillableItemFieldIndex.ClientContractVersionId, SortOperator.Ascending));

            IPrefetchPath2 prefetchPath = new PrefetchPath2((int)EntityType.BillableItemEntity);
            prefetchPath.Add(BillableItemEntity.PrefetchPathBillableItemAttributes);

            adapter.FetchEntityCollection(billableItems,bucket,0,sorterBillableItems,prefetchPath);
}

does it work then?

How much billableItem objects are read?

(Edit): So it apparently hangs after the first query? (the second query never arrives) ?

Your code looks ok. To setup a repro case here for testing, I need more information, like the # of billing_items and the # of billing_item attributes, their relation etc.

(edit2) One other thing to try: adapter.ParameterisedPrefetchPathThreshold = 1; before the FetchEntityCollection(), does that make it work? (as there are 2 prefetch path routines, one optimized for paths with items < ParameterisedPrefetchPathThreshold, and one using the good old code.

Frans Bouma | Lead developer LLBLGen Pro
Marcus avatar
Marcus
User
Posts: 747
Joined: 23-Apr-2004
# Posted on: 01-Nov-2005 22:59:14   

I've gone over that routine (FetchParameterisedPrefetchPath)... and can't see anything obvious that could be causing a memory leak...

I'll be interested in what this turns out to be!

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39590
Joined: 17-Aug-2003
# Posted on: 01-Nov-2005 23:07:26   

Marcus wrote:

I've gone over that routine (FetchParameterisedPrefetchPath)... and can't see anything obvious that could be causing a memory leak...

I'll be interested in what this turns out to be!

So am I simple_smile . NCover showed I covered every routine in the code with my unittests, so it has to be something weird. THough ... the code looks very ok...

Frans Bouma | Lead developer LLBLGen Pro
Ren
User
Posts: 42
Joined: 01-Jul-2005
# Posted on: 01-Nov-2005 23:50:21   

BillableItem is a 1:n relation with BillableItemAttribute

BillableItems count = 8605 (key: bill_id) BillableItemAttributes count = 119102 (foriegn key: bill_id)

Watching the task manager PF Usage starts at about 512MB and Gets to 831Mb and dies.

Using 'Using' didn't seem to work. Here is the code:

            IRelationPredicateBucket bucket = new RelationPredicateBucket();
            
            //batch id = null, invoice id = null, contract ver > 0, Hold = false, Suppress = false
            bucket.PredicateExpression.Add(PredicateFactory.CompareNull(BillableItemFieldIndex.BatchId));
            bucket.PredicateExpression.AddWithAnd(PredicateFactory.CompareNull(BillableItemFieldIndex.InvoiceId));
            bucket.PredicateExpression.AddWithAnd(PredicateFactory.CompareValue(BillableItemFieldIndex.ClientContractVersionId, ComparisonOperator.GreaterThan,0));
            bucket.PredicateExpression.AddWithAnd(PredicateFactory.CompareValue(BillableItemFieldIndex.HoldIndicator, ComparisonOperator.Equal, false));
            bucket.PredicateExpression.AddWithAnd(PredicateFactory.CompareValue(BillableItemFieldIndex.SuppressIndicator, ComparisonOperator.Equal, false));            
            EntityCollection billableItems = new EntityCollection(new BillableItemEntityFactory());
                        
            using(DataAccessAdapter adapter = new DataAccessAdapter())
            {
                ISortExpression sorterBillableItems = new SortExpression(
                    SortClauseFactory.Create(BillableItemFieldIndex.ClientContractVersionId, SortOperator.Ascending));

                IPrefetchPath2 prefetchPath = new PrefetchPath2((int)EntityType.BillableItemEntity);
                prefetchPath.Add(BillableItemEntity.PrefetchPathBillableItemAttributes);

                adapter.FetchEntityCollection(billableItems,bucket,0,sorterBillableItems,prefetchPath);
        

                    return billableItems;
                
            }

Next I tried adding the adapter.ParameterisedPrefetchPathThreshold = 1; before the adapter call like so..

            IRelationPredicateBucket bucket = new RelationPredicateBucket();
            
            //batch id = null, invoice id = null, contract ver > 0, Hold = false, Suppress = false
            bucket.PredicateExpression.Add(PredicateFactory.CompareNull(BillableItemFieldIndex.BatchId));
            bucket.PredicateExpression.AddWithAnd(PredicateFactory.CompareNull(BillableItemFieldIndex.InvoiceId));
            bucket.PredicateExpression.AddWithAnd(PredicateFactory.CompareValue(BillableItemFieldIndex.ClientContractVersionId, ComparisonOperator.GreaterThan,0));
            bucket.PredicateExpression.AddWithAnd(PredicateFactory.CompareValue(BillableItemFieldIndex.HoldIndicator, ComparisonOperator.Equal, false));
            bucket.PredicateExpression.AddWithAnd(PredicateFactory.CompareValue(BillableItemFieldIndex.SuppressIndicator, ComparisonOperator.Equal, false));            
            EntityCollection billableItems = new EntityCollection(new BillableItemEntityFactory());
                        
            using(DataAccessAdapter adapter = new DataAccessAdapter())
            {
                ISortExpression sorterBillableItems = new SortExpression(
                    SortClauseFactory.Create(BillableItemFieldIndex.ClientContractVersionId, SortOperator.Ascending));

                IPrefetchPath2 prefetchPath = new PrefetchPath2((int)EntityType.BillableItemEntity);
                prefetchPath.Add(BillableItemEntity.PrefetchPathBillableItemAttributes);

adapter.ParameterisedPrefetchPathThreshold = 1; 
            adapter.FetchEntityCollection(billableItems,bucket,0,sorterBillableItems,prefetchPath);
        

                    return billableItems;
                
            }

This didn't work either. But by not using the prefetch stuff it works fine. Hope you can reproduce this or tell me I am doing something wrong.

The query returns all these items but if I could figure out how to do a select top 500 on this billable_item query that would help reduce the amount of items. I have not found an example of SELECT TOP for adapter yet though. But the problem of why the older version works and this doesn't will remain.

Thanks, Ren

Ren
User
Posts: 42
Joined: 01-Jul-2005
# Posted on: 02-Nov-2005 01:49:34   

As I have done more testing it seems that different variations that I am testing the one that gives positive results everytime is when I use the generated code from 1.0.2004.2 with the dlls from 4-25-2005 and remove the prefetch path calls from my method.

Otherwise I am getting errors on 2 machines. 1. My Dev machine which is an XP Pro box hitting another XP Pro box with SQL Server 2000 - Here I am watching as the memory runs out. 2. A test machine running windows 2003 server that has the SQL Server 2000 database on it. (non distributed db) This machine does not work with 1.0.2005.1 with prefetch and without prefetch. I get that error that is at the top of this thread. It just does not seem to like either the generated code or the 10-24-2005 dlls.

  1. Now I tested on a 3rd machine which is running windows 2003 server and hitting another window 2003 server for the SQL Server 2000 database. Now on this machine the generated code from 1.0.2005.1 and the 10-24-2005 dll's worked fine. I used the non-prefetch method call...

I am pretty much at a loss, I will try to test against this 3rd machine with the prefetch path put back in the method to see what happens.

Is there something that I could clear out on the other 2 machines that might help that you know of? I am not caching anything that I know of.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39590
Joined: 17-Aug-2003
# Posted on: 02-Nov-2005 08:28:22   

The query returns all these items but if I could figure out how to do a select top 500 on this billable_item query that would help reduce the amount of items. I have not found an example of SELECT TOP for adapter yet though. But the problem of why the older version works and this doesn't will remain.

pass '500' instead of 0 in the FetchEntityCollection.

Thanks for the info. I'll see if I can setup a repro case here. 8000 elements seems like a lot but then again, it also shouldn't be a problem. I pressume none of the 2 entities have heavy blob fields?

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39590
Joined: 17-Aug-2003
# Posted on: 02-Nov-2005 11:19:21   

Ok, when I do a test with the data in this forum (~17000 messages, and I load with a prefetch path all wordmessage objects also (which is a 2 field entity with 2 FKs, one to message and one to word, for the search, there are ~475000 rows in that table) I get a huge memory overload. When I just fetch the messages, it's ok. (the messages contain two blob, one with the text typed, the other with the text in html)

I'll now see if it helps if I load the wordmessages manually instead of with a path.

(edit): that still consumes a lot of memory, with all these entities, but it is possible. The prefetch path queries do fire though, it's the merging of the child entities with the parent entities which is the problem.

When I do a TOP 500 of the messages, it still consumes a lot of memory, which is odd. When I specify a filter which returns roughly 7800 messages, it works ok. Apparently something's not completely correct.

I'll do more testing to see where all the memory is consumed.

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39590
Joined: 17-Aug-2003
# Posted on: 02-Nov-2005 14:13:08   

Ok, here're my results.

The issue is memory consumption through a lot of objects being read into memory. The main cause of the memory consumption is the default collections/hashtables which are allocated for each entity. The main issues are with the 2 hashtables allocated for sync info and the arraylist allocated into each EntityFields2 object for subfield links. This last collection is new in 1.0.2005.1.

Even though these are limited to a capacity, they eat a lot of memory when you load a lot of objects into memory (with lots of objects I mean > 100,000).

When I load the WordMessage objects into an entity collection, which means 420,000 objects, I initially had a memory consumption of roughly 580MB.

I used various tests to see if something was leaking memory, but performance counters showed (together with GC commands) that no objects were leaked, everything was cleaned up.

I then profiled with the CLR profiler what eat all the memory away. I loaded 10,000 objects into a collection and checked the heap. Of the 12MB memory eaten, roughly 50% was taken away by empty, default collections allocated 'for if they're needed', being the 2 hashtables and the arraylist mentioned above. I've to mention that the empty default collections took that percentage because the data inside this entity type is not that huge.

After changing the code in EntityFields2 and EntityBase2, to create these collections on demand, it showed that after I ran the app again with profiling, the memory consumption went down from 12MB to 6.7MB for 10,000 objects. This is with all the overhead inside an object, like change tracking objects etc.

I'd like to mail this new ormsupportclasses version to you so you can use it in your routine to see if this solves your problem.

Keep in mind that fetching 8000 objects into memory together with 100,000 related objects is still not recommended. Please consider paging instead.

The main issue with the current codebase still is the EntityFields2 object (selfservicing has the same problems, but as selfservicing carries around its persistence info in each object anyway, it's not that memory efficient): it creates per entity a Fieldname - FieldPos pair per field. This is stored in a hashtable, which consumes 28% of the memory. This can of course be shared among all entities, though it requires significant changes and I can't make them now. v2.0 will have this solved for good. This will also speed up entity creation time.

Because this was already the case in 1.0.2004.2, this last mentioned issue can't be the cause of the problem you're running into.

I'll now run my unittests to see if the changes I made have any negative effect on stability. Please let me know whereto I can send the .zip file, the address you used to register on the forum?

(edit): It currently fails some unittests, will fix these prior to sending you the file.

(edit2): Ok, fixed that.

Frans Bouma | Lead developer LLBLGen Pro
Marcus avatar
Marcus
User
Posts: 747
Joined: 23-Apr-2004
# Posted on: 02-Nov-2005 17:13:56   

Otis wrote:

Ok, here're my results...

mmm... What about busy web services? It would be very unsual to have 100,000 entities in memory for a given thread, but if you have 200+ concurrent users on a box, each playing with a couple of thousand entities at a given time, then the problem could manifest itself rather quickly. confused

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39590
Joined: 17-Aug-2003
# Posted on: 02-Nov-2005 17:22:00   

Marcus wrote:

Otis wrote:

Ok, here're my results...

mmm... What about busy web services? It would be very unsual to have 100,000 entities in memory for a given thread, but if you have 200+ concurrent users on a box, each playing with a couple of thousand entities at a given time, then the problem could manifest itself rather quickly. confused

With everything related to data, you have to consider: do I need this in memory or not. And if you do, what's the most efficient way to store it into memory considering the actions you want to perform on the data. With entities, you will have overhead, because hte functionality they provide requires some overhead. Often this is nothing more than a couple of hundred bytes, maybe a KB. though with hundreds of thousands of objects it can create some memory hogging.

That's also why I added typedlists, because in some scenario's you just want a stripped down list, stored in a dumb bucket. In other occasions you don't and want the full entity.

The memory gains I got this morning will save you some MB with a lot of entities, so it's a gain. Keep in mind though that often way more is gained by proper scheduling of code and data, for example, lookup stuff can better be cached once and re-used in gui elements than its loaded every time the data is required.

Frans Bouma | Lead developer LLBLGen Pro
Marcus avatar
Marcus
User
Posts: 747
Joined: 23-Apr-2004
# Posted on: 02-Nov-2005 17:43:44   

Otis wrote:

With everything related to data, you have to consider: do I need this in memory or not. And if you do, what's the most efficient way to store it into memory considering the actions you want to perform on the data. With entities, you will have overhead, because hte functionality they provide requires some overhead. Often this is nothing more than a couple of hundred bytes, maybe a KB. though with hundreds of thousands of objects it can create some memory hogging.

Very true...

Otis wrote:

That's also why I added typedlists, because in some scenario's you just want a stripped down list, stored in a dumb bucket. In other occasions you don't and want the full entity.

What would be really handy for 2.0 would be if you could generate a fields interface for each Entity, like "IAccountEntityFields". This interface would be implemented by both the Entity object and the TypedListRow object and hence the two data containers could be consumed by methods which dont care about whether the data is an Entity or a TypedListRows...

Ren
User
Posts: 42
Joined: 01-Jul-2005
# Posted on: 02-Nov-2005 17:51:08   

Thank you for your work looking into this and thanks for the info on limiting the return (Select top).

What this process does is takes billing items for a date range. This can be any number, but with the limiting of amount if items returned this should help (it's configurable, 500 is just a number I am testing with). Each bill item has a 1:n relationship to bill attribute, each bill item has on average 12 attribute items. These attribute items are needed as each bill item is run through a contract tree which uses values from the attributes to direct the item through the contract to get the correct % value to charge. (Yes are contracts are very complicated) The % is applied to the bill item when it completes its trip through the tree. Then the information is saved back to the database and the next 500 are grabbed and the process is re-done until the method (the one above) stops returning items.

When displaying the finished items (items run through the contract tree) I use paging and those items are actually returned via a TypedView/TypedViewManager without the fetching of the attributes at that time.

This particular method above is used in a Calculation process object not for display.

Yes please send the updated dlls to my address used for this forum. (e_renshaw AT yahoo.com)

This is a unique situation where alot processing needs to be done, I'll look for the updated dlls and let you know how it goes.

Ren

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39590
Joined: 17-Aug-2003
# Posted on: 02-Nov-2005 18:35:45   

Mailed simple_smile . If it doesn't arrive, it's probably in the spam/virusscanner bin simple_smile

I've mailed the .NET 1.1 versions.

Frans Bouma | Lead developer LLBLGen Pro
Ren
User
Posts: 42
Joined: 01-Jul-2005
# Posted on: 02-Nov-2005 22:20:18   

Alright I applied the new dlls you sent and the system seems to be rock solid. This with the combination of passing the limit (500 instead of 0) has helped also. I don't know why I never put that together before since I use that all over the place... flushed oh well!

Now should I be using these dll's dated 11-2-05 going forward for all projects? Are you going to update the runtime libraries with this update?

Thanks for you help on this.

Ren

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39590
Joined: 17-Aug-2003
# Posted on: 02-Nov-2005 23:23:09   

Ren wrote:

Alright I applied the new dlls you sent and the system seems to be rock solid. This with the combination of passing the limit (500 instead of 0) has helped also. I don't know why I never put that together before since I use that all over the place... flushed oh well!

simple_smile Glad it's working now simple_smile

Now should I be using these dll's dated 11-2-05 going forward for all projects? Are you going to update the runtime libraries with this update?

You can keep these, the runtimes are updated tomorrow on our site, with the same code as you're using now. simple_smile

Frans Bouma | Lead developer LLBLGen Pro