-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 3 posts ] 
Author Message
 Post subject: NHibernate Batch insert memory issues
PostPosted: Sat Apr 12, 2008 4:51 pm 
Newbie

Joined: Mon Jul 02, 2007 7:09 am
Posts: 4
I have quite a large data layer created in NHibernate for a new application/database I have been developing. It works well with the ASP .NET front-end, and there are no issues.

One of my requirements was to create a migration tool to move data from an old DB to this new system. I've done this by creating a windows application that uses my existing data layer (but without Context based sessions for each HTTP request). The migration tool will create about 100,000 rows all in all, spanning several tables/objects.

I create a session factory, and an individual session and a transaction for the entire procedure - wanting only to commit if the entire thing is successful. I've looked into the chapter about batch inserts for Hibernate (Java) and followed that for NHibernate:

_configuration.Properties[NHibernate.Cfg.Environment.BatchSize] = 256; _configuration.Properties[NHibernate.Cfg.Environment.UseSecondLevelCache] = "false";

I call ISession.Flush() and ISession.Clear() periodically to try and keep memory usage down, however this is of no avail. Whenever I save an object, the memory use increases, and easily uses GBs of RAM. If I attempt to commit and then close the session after every object is saved to the database, I still get a lot of memory use. Even if I close and re open the session factory every time a new object is added to the DB, the same problem occurs.

No matter what I do, I can't get the memory use down and it is making it impossible to run the application long enough for it to perform its task.

I haven't posted the code atm because it's quite large, but I can post specific portions if requested. The code for the "DataManager" is included. The SingleThread mode is used for this application.


Hibernate version: 1.2.1 GA

Name and version of the database you are using: SQL Server 2005


Code:
public enum DataManagerMode
    {
        MultipleThreadsWeb, // Will provide multiple sessions/transactions to the application, but only one per request
        SingleThread // Will provide one session/transaction at a time to the entire application
    }

    /// <summary>
    /// Manages access to the data/business entities.
    /// </summary>
    public class DataManager
    {
        // http://www.hibernate.org/hib_docs/nhibernate/1.2/reference/en/html/

        private const string _currentSessionKey = "nhibernate.current_session";
        private const string _currentTransactionKey = "nhibernate.current_transaction";
        private static ISessionFactory _sessionFactory;
        private static Configuration _configuration;

        private static DataManagerMode _mode = DataManagerMode.MultipleThreadsWeb;
        public static DataManagerMode Mode
        {
            get { return _mode; }
            set { _mode = value; }
        }

        private static ISession _singleThreadSession;
        private static ITransaction _singleThreadTransaction;

        static DataManager()
        {
            _configuration = new Configuration();

            try
            {
                _configuration = _configuration.Configure();

            }
            catch (Exception)
            {
                // If it cannot configure from the config file, then try and populate the
                // details.  SetCustomConnectionString can be used after this.

                _configuration.Properties[NHibernate.Cfg.Environment.Dialect] = "NHibernate.Dialect.MsSql2005Dialect";
                _configuration.Properties[NHibernate.Cfg.Environment.ConnectionProvider] = "NHibernate.Connection.DriverConnectionProvider";
                _configuration.Properties[NHibernate.Cfg.Environment.ConnectionDriver] = "NHibernate.Driver.SqlClientDriver";
                _configuration.AddAssembly(Assembly.GetCallingAssembly());
            }
        }

        /// <summary>
        /// Sets a connection string that overrides that written in the configuration file.
        /// Must be called before any other method otherwise an exception will occur.
        /// </summary>
        public static void SetConnectionString(string connectionString)
        {
            _configuration.Properties[NHibernate.Cfg.Environment.ConnectionString] = connectionString;
        }


        /// <summary>
        /// Sets the DataManager to work in batch mode.
        /// Must be called before any other method otherwise an exception will occur.
        /// </summary>
        public static void SetBatchMode(int size)
        {
            _configuration.Properties[NHibernate.Cfg.Environment.BatchSize] = size.ToString();
            _configuration.Properties[NHibernate.Cfg.Environment.UseSecondLevelCache] = "false";
        }

        /// <summary>
        /// (Internal) The current session
        /// </summary>
        private static ISession CurrentSession
        {
            get
            {
                if (Mode == DataManagerMode.SingleThread) //Added for single-thread mode
                    return _singleThreadSession;

                if (CurrentContext.Items[_currentSessionKey] == null)
                    return null;
                else
                    return (CurrentContext.Items[_currentSessionKey] as ISession);
            }
            set
            {
                if (Mode == DataManagerMode.SingleThread) //Added for single-thread mode
                {
                    _singleThreadSession = value;
                    return;
                }

                if (value == null && CurrentContext.Items.Contains(_currentSessionKey))
                    CurrentContext.Items.Remove(_currentSessionKey);
                else
                    CurrentContext.Items[_currentSessionKey] = value;
            }
        }

        /// <summary>
        /// (Internal) The current transaction
        /// </summary>
        private static ITransaction CurrentTransaction
        {
            get
            {
                if (Mode == DataManagerMode.SingleThread) //Added for single-thread mode
                    return _singleThreadTransaction;

                if (CurrentContext.Items[_currentTransactionKey] == null)
                    return null;
                else
                    return (CurrentContext.Items[_currentTransactionKey] as ITransaction);
            }
            set
            {
                if (Mode == DataManagerMode.SingleThread) //Added for single-thread mode
                {
                    _singleThreadTransaction = value;
                    return;
                }

                if (value == null && CurrentContext.Items.Contains(_currentTransactionKey))
                    CurrentContext.Items.Remove(_currentTransactionKey);
                else
                    CurrentContext.Items[_currentTransactionKey] = value;
            }
        }

        /// <summary>
        /// (Internal) The current context
        /// </summary>
        private static HttpContext CurrentContext
        {
            get
            {
                return HttpContext.Current;
            }
        }

        /// <summary>
        /// Retrieves the session factory
        /// </summary>
        public static ISessionFactory SessionFactory
        {
            get {
                   
                if (_sessionFactory == null)
                    _sessionFactory = _configuration.BuildSessionFactory();

                return _sessionFactory;
            }
        }

        /// <summary>
        /// Retrieves the current session
        /// </summary>
        public static ISession Session
        {
            get
            {
                if (CurrentSession == null)
                    CurrentSession = SessionFactory.OpenSession();

                return CurrentSession;
            }
        }

        /// <summary>
        /// Closes the current session
        /// </summary>
        public static void CloseSession()
        {
            if (CurrentSession == null)
                return;

            CurrentSession.Close();
            CurrentSession = null;
        }

        /// <summary>
        /// Closes the session factory
        /// </summary>
        public static void CloseSessionFactory()
        {
            if (_sessionFactory != null)
            {
                _sessionFactory.Close();
                _sessionFactory = null;
            }
        }

        /// <summary>
        /// Begins a transaction
        /// </summary>
        public static void BeginTransaction()
        {
            if (CurrentTransaction == null)
                CurrentTransaction = Session.BeginTransaction();
        }

        /// <summary>
        /// Commits a transaction
        /// </summary>
        public static void CommitTransaction()
        {
            try
            {
                if (CurrentTransaction != null && !CurrentTransaction.WasCommitted
                    && !CurrentTransaction.WasRolledBack)
                {
                    CurrentTransaction.Commit();
                    CurrentTransaction = null;
                }
            }
            catch (HibernateException ex)
            {
                RollbackTransaction();
                throw ex;
            }
        }

        /// <summary>
        /// Rolls back a transaction
        /// </summary>
        public static void RollbackTransaction()
        {
            try
            {
                if (CurrentTransaction != null && !CurrentTransaction.WasCommitted
                    && !CurrentTransaction.WasRolledBack)
                {
                    CurrentTransaction.Rollback();
                    CurrentTransaction = null;
                }
            }
            catch (HibernateException ex)
            {
                throw ex;
            }
        }
    }
[/code]


Top
 Profile  
 
 Post subject:
PostPosted: Sat Apr 12, 2008 9:33 pm 
Newbie

Joined: Mon Jul 02, 2007 7:09 am
Posts: 4
I think I have found the root cause of the problem with a bit of memory profiling.

I was using a Microsoft WPF textbox to store details of the migration progress. It appears that by default, the textboxes will store unlimited undo data! Thus, if you add a line to it several times a second like I have been doing, you will experience an exponential increase in the memory used to store the undo data.

I've spent hours tracking this down too :( Bad design choice by Microsoft IMHO!


Top
 Profile  
 
 Post subject:
PostPosted: Tue Dec 09, 2008 9:56 am 
Beginner
Beginner

Joined: Fri Jul 06, 2007 9:27 pm
Posts: 22
I'm not sure I'd agree on the 'bad design choice' call. I don't think MS really means for the WPF text box to be used in the way you describe, There are a number of other controls that can display a large volume of text in a scollable environment. Most of those controls didn't exist in the WinForms environment. I think MS would prefer that you use one of those other controls.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 3 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.