Thanks for the input. We have one factory per connection string. The repeated calls to factoryMap.ContainsKey are more of a "belt and suspender" approach, with the WaitForCompletion... method acting as a sentinel to prevent multiple threads trying to create a factory simultaneously. (Subsequent calls will find the connection in the factory map and therefore not try to create a new one).
On Wednesday, May 29, 2013 9:39:38 AM UTC-5, PeteA wrote: > > 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] <javascript:> [mailto: > [email protected] <javascript:>] *On Behalf Of *Kerry > *Sent:* 29 May 2013 14:48 > *To:* [email protected] <javascript:> > *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] <javascript:>. > To post to this group, send email to [email protected] <javascript:> > . > 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.
