Permalink
Browse files

NCBC-149: Delay in view requests when FindServicePoint is called

Change-Id: I1b2616552a2e8c86b1cb05885d30d6409dc7a818
Reviewed-on: http://review.couchbase.org/22205
Reviewed-by: Matt Ingenthron <matt@couchbase.com>
Tested-by: John C. Zablocki <john@couchbase.com>
  • Loading branch information...
1 parent 52e912b commit 51aba515d878f40a954b547c2e9f4587a76a40c4 @johnzablocki johnzablocki committed with John C. Zablocki Nov 1, 2012
View
16 src/Couchbase.Tests/App.config
@@ -11,6 +11,8 @@
<section name="httptimeout-explicit-config" type="Couchbase.Configuration.CouchbaseClientSection, Couchbase"/>
<section name="observe-default-config" type="Couchbase.Configuration.CouchbaseClientSection, Couchbase"/>
<section name="observe-explicit-config" type="Couchbase.Configuration.CouchbaseClientSection, Couchbase"/>
+ <section name="httpclient-config-initconn" type="Couchbase.Configuration.CouchbaseClientSection, Couchbase"/>
+ <section name="httpclient-config-noinitconn" type="Couchbase.Configuration.CouchbaseClientSection, Couchbase"/>
<sectionGroup name="enyim.com">
<section name="log" type="Enyim.Caching.Configuration.LoggerSection, Enyim.Caching" />
</sectionGroup>
@@ -102,4 +104,18 @@
</servers>
<heartbeatMonitor enabled="false" />
</heartbeat-config-off>
+
+ <httpclient-config-initconn>
+ <servers bucket="default">
+ <add uri="http://localhost:8091/pools" />
+ </servers>
+ <httpClient initializeConnection="false" />
+ </httpclient-config-initconn>
+
+ <httpclient-config-noinitconn>
+ <servers bucket="default">
+ <add uri="http://localhost:8091/pools" />
+ </servers>
+ <httpClient initializeConnection="false" />
+ </httpclient-config-noinitconn>
</configuration>
View
1 src/Couchbase.Tests/Couchbase.Tests.csproj
@@ -51,6 +51,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="ConfigHelperTests.cs" />
+ <Compile Include="HttpClientConfigTests.cs" />
<Compile Include="CouchbaseAuthenticatedViewTests.cs" />
<Compile Include="CouchbaseClientGenericViewTests.cs" />
<Compile Include="CouchbaseClientObserveTests.cs" />
View
30 src/Couchbase.Tests/DefaultCouchbaseClientConfigurationTests.cs
@@ -25,7 +25,7 @@ public void When_Using_Code_Config_And_Http_Client_Factory_Is_Not_Set_Hammock_Fa
//HammockHttpClient is an internal class to the Couchbase assembly,
//therefore the explicit type can't be checked for using Is.InstanceOf<T>
- var typeName = (config.HttpClientFactory.Create(config.Urls[0], "", "").GetType().Name);
+ var typeName = (config.HttpClientFactory.Create(config.Urls[0], "", "", true).GetType().Name);
Assert.That(typeName, Is.StringContaining("HammockHttpClient"));
}
@@ -39,7 +39,7 @@ public void When_Using_App_Config_And_Http_Client_Factory_Is_Not_Set_Hammock_Fac
//HammockHttpClient is an internal class to the Couchbase assembly,
//therefore the explicit type can't be checked for using Is.InstanceOf<T>
- var typeName = (config.HttpClientFactory.CreateInstance().Create(config.Servers.Urls.ToUriCollection()[0], "", "").GetType().Name);
+ var typeName = (config.HttpClientFactory.CreateInstance().Create(config.Servers.Urls.ToUriCollection()[0], "", "", true).GetType().Name);
Assert.That(typeName, Is.StringContaining("HammockHttpClient"));
}
@@ -155,6 +155,32 @@ public void When_Observe_Timeout_Is_Set_To_30_And_Using_App_Config_Value_Is_30_S
Assert.That(config.Servers.ObserveTimeout, Is.EqualTo(TimeSpan.FromSeconds(30)));
}
#endregion
+
+ #region HttpClient
+ [Test]
+ public void When_Initialize_Connection_Is_Not_Set_In_App_Config_Default_Is_True()
+ {
+ var config = ConfigurationManager.GetSection("min-config") as CouchbaseClientSection;
+ Assert.That(config, Is.Not.Null, "Config was null");
+ Assert.That(config.HttpClient.InitializeConnection, Is.True);
+ }
+
+ [Test]
+ public void When_Initialize_Connection_Is_Set_In_App_Config_Property_Changes_From_Default()
+ {
+ var config = ConfigurationManager.GetSection("httpclient-config-noinitconn") as CouchbaseClientSection;
+ Assert.That(config, Is.Not.Null, "Config was null");
+ Assert.That(config.HttpClient.InitializeConnection, Is.False);
+ }
+
+ [Test]
+ public void When_Initialize_Connection_Is_Not_Set_In_Code_Default_Is_True()
+ {
+ var config = new CouchbaseClientConfiguration();
+ Assert.That(config, Is.Not.Null, "Config was null");
+ Assert.That(config.HttpClient.InitializeConnection, Is.True);
+ }
+ #endregion
}
}
View
78 src/Couchbase.Tests/HttpClientConfigTests.cs
@@ -0,0 +1,78 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using NUnit.Framework;
+using Couchbase.Configuration;
+using Enyim.Caching.Memcached;
+using Couchbase.Tests.Utils;
+
+namespace Couchbase.Tests
+{
+ [TestFixture]
+ public class HttpClientConfigTests : CouchbaseClientViewTestsBase
+ {
+ [Test]
+ public void View_Operations_Succeed_When_Initialize_Connection_Is_True()
+ {
+ var config = ConfigSectionUtils.GetConfigSection<CouchbaseClientSection>("httpclient-config-initconn");
+ var client = new CouchbaseClient(config);
+ var view = client.GetView<City>("cities", "by_name", true).Stale(StaleMode.False);
+ viewPass(view);
+ }
+
+ [Test]
+ public void View_Operations_Succeed_When_Initialize_Connection_Is_False()
+ {
+ var config = ConfigSectionUtils.GetConfigSection<CouchbaseClientSection>("httpclient-config-noinitconn");
+ var client = new CouchbaseClient(config);
+
+ var view = client.GetView<City>("cities", "by_name", true).Stale(StaleMode.False);
+ viewPass(view);
+
+ }
+
+ [Test]
+ public void View_Operations_Succeed_When_HTTP_Client_Is_Not_Configured_In_App_Config()
+ {
+ var view = _Client.GetView<City>("cities", "by_name", true).Stale(StaleMode.False);
+ viewPass(view);
+
+ }
+
+ private void viewPass(IView<City> view)
+ {
+ foreach (var item in view)
+ {
+ Assert.That(item.Id, Is.Not.Null, "Item Id was null");
+ Assert.That(item.Name, Is.Not.Null, "Item Name was null");
+ Assert.That(item.State, Is.Not.Null, "Item State was null");
+ Assert.That(item.Type, Is.EqualTo("city"), "Type was not city");
+ Assert.That(item, Is.InstanceOf<City>(), "Item was not a City instance");
+ }
+
+ Assert.That(view.Count(), Is.GreaterThan(0), "View count was 0");
+ }
+ }
+}
+
+#region [ License information ]
+/* ************************************************************
+ *
+ * @author Couchbase <info@couchbase.com>
+ * @copyright 2012 Couchbase, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * ************************************************************/
+#endregion
View
2 src/Couchbase.Tests/Mocks/MockHttpClientFactory.cs
@@ -9,7 +9,7 @@ public class MockHttpClientFactory : IHttpClientFactory
{
public IHttpClient Client { get; set; }
- public IHttpClient Create(Uri baseUri, string username, string password)
+ public IHttpClient Create(Uri baseUri, string username, string password, bool shouldInitializeConnection)
{
return (Client = new MockHttpClient() { BaseUri = baseUri });
}
View
30 src/Couchbase/Configuration/CouchbaseClientConfiguration.cs
@@ -32,6 +32,8 @@ public CouchbaseClientConfiguration()
this.SocketPool = new SocketPoolConfiguration();
this.HeartbeatMonitor = new HeartbeatMonitorElement();
+
+ this.HttpClient = new HttpClientElement();
}
/// <summary>
@@ -48,7 +50,7 @@ INameTransformer ICouchbaseClientConfiguration.CreateDesignDocumentNameTransform
IHttpClient ICouchbaseClientConfiguration.CreateHttpClient(Uri baseUri)
{
- return this.HttpClientFactory.Create(baseUri, Bucket, BucketPassword);
+ return this.HttpClientFactory.Create(baseUri, Bucket, BucketPassword, HttpClient.InitializeConnection);
}
#endregion
@@ -92,6 +94,11 @@ IHttpClient ICouchbaseClientConfiguration.CreateHttpClient(Uri baseUri)
public IHeartbeatMonitorConfiguration HeartbeatMonitor { get; set; }
/// <summary>
+ /// Gets or sets the configuration of the http client.
+ /// </summary>
+ public IHttpClientConfiguration HttpClient { get; set; }
+
+ /// <summary>
/// Gets or sets the <see cref="T:Enyim.Caching.Memcached.IMemcachedKeyTransformer"/> which will be used to convert item keys for Memcached.
/// </summary>
public IMemcachedKeyTransformer KeyTransformer
@@ -237,6 +244,7 @@ internal class ReadOnlyConfig : ICouchbaseClientConfiguration
private TimeSpan httpRequestTimeout;
private ISocketPoolConfiguration spc;
private IHeartbeatMonitorConfiguration hbm;
+ private IHttpClientConfiguration hcc;
private ICouchbaseClientConfiguration original;
@@ -255,6 +263,7 @@ public ReadOnlyConfig(ICouchbaseClientConfiguration original)
this.spc = new SPC(original.SocketPool);
this.hbm = new HBM(original.HeartbeatMonitor);
+ this.hcc = new HCC(original.HttpClient);
this.original = original;
}
@@ -300,6 +309,11 @@ IHeartbeatMonitorConfiguration ICouchbaseClientConfiguration.HeartbeatMonitor
get { return this.hbm; }
}
+ IHttpClientConfiguration ICouchbaseClientConfiguration.HttpClient
+ {
+ get { return this.hcc; }
+ }
+
IMemcachedKeyTransformer ICouchbaseClientConfiguration.CreateKeyTransformer()
{
return this.original.CreateKeyTransformer();
@@ -401,6 +415,20 @@ public HBM(IHeartbeatMonitorConfiguration original)
int IHeartbeatMonitorConfiguration.Interval { get { return this.interval; } set { } }
bool IHeartbeatMonitorConfiguration.Enabled { get { return this.enabled; } set { } }
}
+
+ private class HCC : IHttpClientConfiguration
+ {
+ private bool initializeConnectio;
+
+ public HCC(IHttpClientConfiguration original)
+ {
+ this.initializeConnectio = original.InitializeConnection;
+ }
+
+ bool IHttpClientConfiguration.InitializeConnection { get { return this.initializeConnectio; } set { } }
+
+ }
+
}
}
View
17 src/Couchbase/Configuration/CouchbaseClientSection.cs
@@ -43,6 +43,16 @@ public HeartbeatMonitorElement HeartbeatMonitor
}
/// <summary>
+ /// Gets or sets the configuration of the http client.
+ /// </summary>
+ [ConfigurationProperty("httpClient", IsRequired = false)]
+ public HttpClientElement HttpClient
+ {
+ get { return (HttpClientElement)base["httpClient"]; }
+ set { base["httpClient"] = value; }
+ }
+
+ /// <summary>
/// Gets or sets the <see cref="T:Enyim.Caching.Memcached.IMemcachedNodeLocator"/> which will be used to assign items to Memcached nodes.
/// </summary>
[ConfigurationProperty("locator", IsRequired = false)]
@@ -151,7 +161,7 @@ protected override void PostDeserialize()
Debug.Assert(this.clientFactory != null);
- return this.clientFactory.Create(baseUri, Servers.Bucket, Servers.BucketPassword);
+ return this.clientFactory.Create(baseUri, Servers.Bucket, Servers.BucketPassword, HttpClient.InitializeConnection);
}
#endregion
@@ -173,6 +183,11 @@ IHeartbeatMonitorConfiguration ICouchbaseClientConfiguration.HeartbeatMonitor
get { return this.HeartbeatMonitor; }
}
+ IHttpClientConfiguration ICouchbaseClientConfiguration.HttpClient
+ {
+ get { return this.HttpClient; }
+ }
+
IMemcachedKeyTransformer ICouchbaseClientConfiguration.CreateKeyTransformer()
{
return this.KeyTransformer.CreateInstance() ?? new DefaultKeyTransformer();
View
42 src/Couchbase/Configuration/HttpClientElement.cs
@@ -0,0 +1,42 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Configuration;
+
+namespace Couchbase.Configuration
+{
+ public class HttpClientElement : ConfigurationElement, IHttpClientConfiguration
+ {
+ /// <summary>
+ /// Gets or sets the endpoint uri for the heartbeat request
+ /// </summary>
+ [ConfigurationProperty("initializeConnection", IsRequired = false, DefaultValue = true)]
+ public bool InitializeConnection
+ {
+ get { return (bool)base["initializeConnection"]; }
+ set { base["initializeConnection"] = value; }
+ }
+ }
+}
+
+#region [ License information ]
+/* ************************************************************
+ *
+ * @author Couchbase <info@couchbase.com>
+ * @copyright 2012 Couchbase, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * ************************************************************/
+#endregion
View
5 src/Couchbase/Configuration/ICouchbaseClientConfiguration.cs
@@ -45,6 +45,11 @@ public interface ICouchbaseClientConfiguration
IHeartbeatMonitorConfiguration HeartbeatMonitor { get; }
/// <summary>
+ /// Gets the configuration of the http client.
+ /// </summary>
+ IHttpClientConfiguration HttpClient { get; }
+
+ /// <summary>
/// Creates an <see cref="T:Enyim.Caching.Memcached.IMemcachedKeyTransformer"/> instance which will be used to convert item keys for Memcached.
/// </summary>
IMemcachedKeyTransformer CreateKeyTransformer();
View
37 src/Couchbase/Configuration/IHttpClientConfiguration.cs
@@ -0,0 +1,37 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Couchbase.Configuration
+{
+ public interface IHttpClientConfiguration
+ {
+ /// <summary>
+ /// When true, instructs the client to pre-fetch a given URI
+ /// to initialize the ServicePoint for future requests
+ /// </summary>
+ bool InitializeConnection { get; set; }
+ }
+}
+
+#region [ License information ]
+/* ************************************************************
+ *
+ * @author Couchbase <info@couchbase.com>
+ * @copyright 2012 Couchbase, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * ************************************************************/
+#endregion
View
2 src/Couchbase/Couchbase.csproj
@@ -69,6 +69,8 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
+ <Compile Include="Configuration\HttpClientElement.cs" />
+ <Compile Include="Configuration\IHttpClientConfiguration.cs" />
<Compile Include="Extensions\CouchbaseClientExtensions.cs" />
<Compile Include="Helpers\JsonHelper.cs" />
<Compile Include="Management\Bucket.cs" />
View
13 src/Couchbase/HammockHttpClient.cs
@@ -18,7 +18,7 @@ internal class HammockHttpClient : IHttpClient
private RestClient client;
- public HammockHttpClient(Uri baseUri, string username, string password)
+ public HammockHttpClient(Uri baseUri, string username, string password, bool shouldInitConnection)
{
this.client = new RestClient { Authority = baseUri.ToString() };
@@ -47,6 +47,13 @@ public HammockHttpClient(Uri baseUri, string username, string password)
};
client.BeforeRetry += new EventHandler<RetryEventArgs>(client_BeforeRetry);
+
+ //The first time a request is made to a URI, the ServicePointManager
+ //will create a ServicePoint to manage connections to a particular host
+ //This process is expensive and slows down the first created view.
+ //The call to BeginRequest is basically an async, no-op HTTP request to
+ //initialize the ServicePoint before the first view request is made.
+ if (shouldInitConnection) client.BeginRequest();
}
void client_BeforeRetry(object sender, RetryEventArgs e)
@@ -150,9 +157,9 @@ public class HammockHttpClientFactory : IHttpClientFactory
{
public static readonly IHttpClientFactory Instance = new HammockHttpClientFactory();
- IHttpClient IHttpClientFactory.Create(Uri baseUri, string username, string password)
+ IHttpClient IHttpClientFactory.Create(Uri baseUri, string username, string password, bool shouldInitializeConnection)
{
- return new HammockHttpClient(baseUri, username, password);
+ return new HammockHttpClient(baseUri, username, password, shouldInitializeConnection);
}
}
}
View
2 src/Couchbase/IHttpClient.cs
@@ -54,7 +54,7 @@ public interface IHttpRequest
/// </summary>
public interface IHttpClientFactory
{
- IHttpClient Create(Uri baseUri, string username, string password);
+ IHttpClient Create(Uri baseUri, string username, string password, bool shouldInitializeConnection);
}
public enum HttpMethod

0 comments on commit 51aba51

Please sign in to comment.