The first thing that strikes me is that you're mixing the Task API with
some non-thread-safe dictionary operations (WaitForCompletionIf....) -
the repeated calls factoryMap.ContainsKey feel like an attempt to
mitigate this?

 

My reading is that you're aiming to create one ISessionFactory per
connection string, but potentially have many different instances of the
same connection string with different names; am I reading this right?
If this is the case then how about flattening the connectionMap into a
sequence of distinct connection strings together with the 1st 'map' for
each one so that you don't have to worry about duplicates, e.g.

 

                     IEnumerable<KeyValuePair<string,
ConnectionElement>> oneMapPerConnectionString =
connectionMap.Map.Join(connectionNames, map => map.Key, name => name,
(map, name) => map)

                           .GroupBy(map => map.Key)

                           .Select(group => group.First());

 

That should remove the need for any mutices *except* the final addition
to factoryMap (in case you happen to get a hash-code clash - bad things
would happen if this weren't serialised)

 

/Pete

 

From: [email protected] [mailto:[email protected]] On
Behalf Of Kerry
Sent: 29 May 2013 14:48
To: [email protected]
Subject: [nhusers] Bootstrapping with multiple Session Factories

 

We have a need in our app to support multiple "connections" to different
databases, with each connection supporting the same set of mappings.  We
have a huge number of mappings (this is a decade-old legacy
application), and each connection and database has the same schema.  We
recently added a feature to the application that allows a user on one
connection to view data on all of the other connections, which requires
having all of the connections available at the time of the request.

 

Bootstrapping a single connection can take up to 20 seconds over a slow
wire.  When one of our customers tried to view data on 7 connections,
the application (unsurprisingly) timed out.  To address this, we are
taking a two-pronged approach:

1.      bootstrap all connections prior to login of the first user (at
app_start); and
2.      at bootstrap, create the session factories for each connection
in a multi-threaded environment to reduce the timeout problem.

 

The second approach is proving to be problematic.  When calling
BuildSessionFactory in a multi-threaded environment, the logs show that
the factories are being constructed serially.  (i.e. I spin off the
threads at time A, and they come back at times B, B+10, B+20, B+30,
etc).  Another developer reporting setting up an integration environment
to test this, and received a duplicate key exception from within
NHIbernate.  I have not yet had time to review that error or his code,
but it points to something problematic with using nHibernate in a
multi-threaded environment.

 

Would somebody mind taking a look at the following code and commenting
on our approach?  Thanks very much.

 

        public void ConstructSessionFactories(IEnumerable<string>
connectionNames)
        {
            var maps = connectionMap.Map.Where(m =>
connectionNames.Contains(m.Key)).Select(m => m.Value);
            maps.ForEach(info =>
                {
                    if (factoryMap.ContainsKey(info.ConnectionString))
return;
 
WaitForCompletionIfFactoryUnderConstruction(info.ConnectionString);
 
                    if (factoryMap.ContainsKey(info.ConnectionString))
return;
                    var task = new Task(() => CreateNewFactory(info));
                    task.ContinueWith(FactoryConstructed,
TaskContinuationOptions.OnlyOnRanToCompletion);
                    task.ContinueWith(FactoryNotConstructed,
TaskContinuationOptions.NotOnRanToCompletion);
                    task.ContinueWith(FactoryConstructionCausedError,
TaskContinuationOptions.OnlyOnFaulted);
                    factoriesUnderConstruction[info.ConnectionString] =
task;
                    task.Start();
                    
                });
        }
 
        protected virtual ISessionFactory
CreateNewFactory(ConnectionElement info)
        {
            if (factoryMap.ContainsKey(info.ConnectionString)) return
factoryMap[info.ConnectionString];
            logger.DebugFormat("Creating new SessionFactory for
connection '{0}'", info.Name);
 
            var cfg = configurationProvider.Get(info);
            ExposeConfiguration(cfg);
            configData.AddOrUpdate(info.Name, cfg, (key, oldValue) =>
cfg);
 
            var factory = cfg.BuildSessionFactory();
            factoryMap[info.ConnectionString] = factory;
            return factory;
        }
 
        private void WaitForCompletionIfFactoryUnderConstruction(string
connection)
        {
            while (factoriesUnderConstruction.ContainsKey(connection))
            {
                Task task;
                factoriesUnderConstruction.TryGetValue(connection, out
task);
                if (task != null) task.Wait();
            }            
        }

-- 
You received this message because you are subscribed to the Google
Groups "nhusers" group.
To unsubscribe from this group and stop receiving emails from it, send
an email to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/nhusers?hl=en-US.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

-- 
You received this message because you are subscribed to the Google Groups 
"nhusers" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/nhusers?hl=en-US.
For more options, visit https://groups.google.com/groups/opt_out.


Reply via email to