NHibernate Connection Resiliency
Entity Framework 6 included a feature known as connection resiliency. Basically, what it says is, when EF is trying to connect to a database, it will try a number of times before giving up. After each unsuccessful attempt, it will wait some time and then try again. As you can imagine, this is very useful, especially when we are dealing with cloud storage.
NHibernate does not natively offer this, however, because it is highly extensible, it isn’t too hard to build one such mechanism, which is what I did.
The code is below, as you can see, it consists of a custom implementation of DriverConnectionProvider, the component of NHibernate that opens connections for us.
1: public class ResilientDriverConnectionProvider : DriverConnectionProvider
2: {
3: public const String ConnectionDelayBetweenTries = "connection.delay_between_tries";
4: public const String ConnectionMaxTries = "connection.max_tries";
5:
6: private static readonly IInternalLogger log = LoggerProvider.LoggerFor(typeof(ResilientDriverConnectionProvider));
7:
8: public ResilientDriverConnectionProvider()
9: {
10: this.MaxTries = 3;
11: this.DelayBetweenTries = TimeSpan.FromSeconds(5);
12: }
13:
14: public Int32 MaxTries { get; set; }
15:
16: public TimeSpan DelayBetweenTries { get; set; }
17:
18: public override void Configure(IDictionary<String, String> settings)
19: {
20: String maxTries;
21: String delayBetweenTries;
22:
23: if (settings.TryGetValue(ConnectionMaxTries, out maxTries) == true)
24: {
25: this.MaxTries = Int32.Parse(maxTries);
26: }
27:
28: if (settings.TryGetValue(ConnectionDelayBetweenTries, out delayBetweenTries) == true)
29: {
30: this.DelayBetweenTries = TimeSpan.Parse(delayBetweenTries);
31: }
32:
33: base.Configure(settings);
34: }
35:
36: public override IDbConnection GetConnection()
37: {
38: IDbConnection con = null;
39:
40: for (var i = 0; i < this.MaxTries; ++i)
41: {
42: try
43: {
44: log.Debug(String.Format("Attempting to get connection, {0} of {1}", (i + 1), this.MaxTries));
45: con = base.GetConnection();
46: log.Debug(String.Format("Got a connection after {0} tries", (i + 1)));
47:
48: break;
49: }
50: catch(Exception ex)
51: {
52: if (i == this.MaxTries - 1)
53: {
54: log.Error(String.Format("Could not get connection after {0} tries", this.MaxTries), ex);
55: throw;
56: }
57: else
58: {
59: Thread.Sleep(this.DelayBetweenTries);
60: }
61: }
62: }
63:
64: return (con);
65: }
66: }
The code wraps the attempt to open a connection and retries it a number of times, with some delay in between.
The way to configure this, in fluent configuration, would be:
1: var cfg = new Configuration()
2: .DataBaseIntegration(
3: db =>
4: {
5: db.ConnectionProvider<ResilientDriverConnectionProvider>();
6: //...
7: });
Or if you prefer to use string properties, in either XML or fluent configuration, you can do:
1: var cfg = new Configuration()
2: .SetProperty(NHibernate.Cfg.Environment.ConnectionProvider, typeof(ResilientDriverConnectionProvider).AssemblyQualifiedName);
From looking at the class, you can see that it supports two properties:
-
MaxTries: the maximum number of connect attempts;
-
DelayBetweenTries: the amount of time to wait between two connection attempts.
It is possible to supply this values by configuration:
1: var cfg = new Configuration()
2: .SetProperty(NHibernate.Cfg.Environment.ConnectionProvider, typeof(ResilientDriverConnectionProvider).AssemblyQualifiedName)
3: .SetProperties(ResilientDriverConnectionProvider.ConnectionMaxTries, "3")
4: .SetProperties(ResilientDriverConnectionProvider.ConnectionDelayBetweenTries, TimeSpan.FromSeconds(5).ToString());
As usual, hope you find this useful!