-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial port of Enyim.Caching Memcached provider for NHibernate 2nd l…
…evel caches
- Loading branch information
Showing
211 changed files
with
367,918 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,51 @@ | ||
nhibernate-caches-couchbase | ||
=========================== | ||
|
||
2nd level caching provider for NHibernate using Couchbase | ||
2nd level caching provider for NHibernate using Couchbase. | ||
|
||
This provider is a port of the Enyim.Caching provider that is part of NHContrib - http://sourceforge.net/projects/nhcontrib/. | ||
|
||
#Usage | ||
|
||
Once configured, the Couchbase NHibernate 2nd level cache should be transparent. | ||
|
||
##Configure the Couchbase .NET Client Library | ||
|
||
<section name="couchbase" type="Couchbase.Configuration.CouchbaseClientSection, Couchbase" /> | ||
|
||
<couchbase> | ||
<servers bucket="default"> | ||
<add uri="http://127.0.0.1:8091/pools" /> | ||
</servers> | ||
</couchbase> | ||
|
||
##Configure NHibernate | ||
|
||
<section name="hibernate-configuration" type="NHibernate.Cfg.ConfigurationSectionHandler, NHibernate" /> | ||
|
||
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2"> | ||
<session-factory> | ||
<property name="connection.provider">NHibernate.Connection.DriverConnectionProvider, NHibernate</property> | ||
<property name="dialect">NHibernate.Dialect.MsSql2000Dialect</property> | ||
<property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property> | ||
<property name="connection.connection_string"> | ||
Server=localhost;initial catalog=nhibernate;Integrated Security=SSPI | ||
</property> | ||
<property name="connection.isolation">ReadCommitted</property> | ||
<property name="cache.provider_class"> NHibernate.Caches.Couchbase.MemCacheProvider,NHibernate.Caches.Couchbase</property> | ||
</session-factory> | ||
</hibernate-configuration> | ||
|
||
##Using FluentNHibernate | ||
|
||
return Fluently.Configure() | ||
.Database(MsSqlConfiguration.MsSql2008.ConnectionString(connectionString)) | ||
.Cache(c => c.UseQueryCache().ProviderClass<CouchbaseCacheProvider>()) | ||
.Mappings(m => | ||
m.FluentMappings.AddFromAssemblyOf<Beer>()) | ||
.ExposeConfiguration(c => c.SetProperty("current_session_context_class", "web")) | ||
.BuildSessionFactory(); | ||
|
||
#Defaults | ||
|
||
Without a "couchbase" config section, the provider will connect to a node on localhost using the default bucket. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
<?xml version="1.0" encoding="utf-8" ?> | ||
<configuration> | ||
<configSections> | ||
<section name="couchbase" type="Couchbase.Configuration.CouchbaseClientSection, Couchbase" /> | ||
<section name="hibernate-configuration" type="NHibernate.Cfg.ConfigurationSectionHandler, NHibernate" /> | ||
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net" /> | ||
</configSections> | ||
|
||
<couchbase> | ||
<servers bucket="default"> | ||
<add uri="http://127.0.0.1:8091/pools" /> | ||
</servers> | ||
<socketPool minPoolSize="5" maxPoolSize="20" connectionTimeout="00:00:10" deadTimeout="00:02:00" /> | ||
</couchbase> | ||
|
||
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2"> | ||
<session-factory> | ||
<property name="connection.provider">NHibernate.Connection.DriverConnectionProvider, NHibernate</property> | ||
<property name="dialect">NHibernate.Dialect.MsSql2000Dialect</property> | ||
<property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property> | ||
<property name="connection.connection_string"> | ||
Server=localhost;initial catalog=nhibernate;Integrated Security=SSPI | ||
</property> | ||
<property name="connection.isolation">ReadCommitted</property> | ||
<property name="cache.provider_class">NHibernate.Caches.Couchbase.MemCacheProvider,NHibernate.Caches.Couchbase</property> | ||
</session-factory> | ||
</hibernate-configuration> | ||
|
||
<log4net> | ||
<appender name="rollingFile" type="log4net.Appender.RollingFileAppender,log4net" > | ||
<param name="File" value="log.txt" /> | ||
<param name="AppendToFile" value="true" /> | ||
<param name="RollingStyle" value="Date" /> | ||
<param name="DatePattern" value="yyyy.MM.dd" /> | ||
<param name="StaticLogFileName" value="true" /> | ||
<layout type="log4net.Layout.PatternLayout,log4net"> | ||
<param name="ConversionPattern" value="%d [%t] %-5p %c [%x] <%X{auth}> - %m%n" /> | ||
</layout> | ||
</appender> | ||
<root> | ||
<priority value="ALL" /> | ||
<appender-ref ref="rollingFile" /> | ||
</root> | ||
<logger name="Couchbase.PooledSocket"> | ||
<priority value="ALL" /> | ||
<appender-ref ref="rollingFile" /> | ||
</logger> | ||
</log4net> | ||
|
||
</configuration> |
205 changes: 205 additions & 0 deletions
205
src/NHibernate.Caches.Couchbase.Tests/CouchbaseCacheFixture.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,205 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Threading; | ||
using log4net.Config; | ||
using NHibernate.Cache; | ||
using NUnit.Framework; | ||
|
||
namespace NHibernate.Caches.Couchbase.Tests | ||
{ | ||
public class CouchbaseCacheFixture | ||
{ | ||
private Dictionary<string, string> props; | ||
private ICacheProvider provider; | ||
|
||
[TestFixtureSetUp] | ||
public void FixtureSetup() | ||
{ | ||
XmlConfigurator.Configure(); | ||
props = new Dictionary<string, string> {{"compression_enabled", "false"}, {"expiration", "20"}}; | ||
provider = new CouchbaseCacheProvider(); | ||
provider.Start(props); | ||
} | ||
|
||
[TestFixtureTearDown] | ||
public void FixtureStop() | ||
{ | ||
provider.Stop(); | ||
} | ||
|
||
[Test] | ||
public void TestClear() | ||
{ | ||
string key = "key1"; | ||
string value = "value"; | ||
|
||
ICache cache = provider.BuildCache("nunit", props); | ||
Assert.IsNotNull(cache, "no cache returned"); | ||
|
||
// add the item | ||
cache.Put(key, value); | ||
Thread.Sleep(1000); | ||
|
||
// make sure it's there | ||
object item = cache.Get(key); | ||
Assert.IsNotNull(item, "couldn't find item in cache"); | ||
|
||
// clear the cache | ||
cache.Clear(); | ||
|
||
// make sure we don't get an item | ||
item = cache.Get(key); | ||
Assert.IsNull(item, "item still exists in cache"); | ||
} | ||
|
||
[Test] | ||
public void TestDefaultConstructor() | ||
{ | ||
ICache cache = new CouchbaseCacheClient(); | ||
Assert.IsNotNull(cache); | ||
} | ||
|
||
[Test] | ||
public void TestEmptyProperties() | ||
{ | ||
ICache cache = new CouchbaseCacheClient("nunit", new Dictionary<string, string>()); | ||
Assert.IsNotNull(cache); | ||
} | ||
|
||
[Test] | ||
public void TestNoPropertiesConstructor() | ||
{ | ||
ICache cache = new CouchbaseCacheClient("nunit"); | ||
Assert.IsNotNull(cache); | ||
} | ||
|
||
[Test] | ||
public void TestNullKeyGet() | ||
{ | ||
ICache cache = new CouchbaseCacheClient(); | ||
cache.Put("nunit", "value"); | ||
Thread.Sleep(1000); | ||
object item = cache.Get(null); | ||
Assert.IsNull(item); | ||
} | ||
|
||
[Test] | ||
public void TestNullKeyPut() | ||
{ | ||
ICache cache = new CouchbaseCacheClient(); | ||
Assert.Throws<ArgumentNullException>(() => cache.Put(null, null)); | ||
} | ||
|
||
[Test] | ||
public void TestNullKeyRemove() | ||
{ | ||
ICache cache = new CouchbaseCacheClient(); | ||
Assert.Throws<ArgumentNullException>(() => cache.Remove(null)); | ||
} | ||
|
||
[Test] | ||
public void TestNullValuePut() | ||
{ | ||
ICache cache = new CouchbaseCacheClient(); | ||
Assert.Throws<ArgumentNullException>(() => cache.Put("nunit", null)); | ||
} | ||
|
||
[Test] | ||
public void TestPut() | ||
{ | ||
string key = "key1"; | ||
string value = "value"; | ||
|
||
ICache cache = provider.BuildCache("nunit", props); | ||
Assert.IsNotNull(cache, "no cache returned"); | ||
|
||
Assert.IsNull(cache.Get(key), "cache returned an item we didn't add !?!"); | ||
|
||
cache.Put(key, value); | ||
Thread.Sleep(1000); | ||
object item = cache.Get(key); | ||
Assert.IsNotNull(item); | ||
Assert.AreEqual(value, item, "didn't return the item we added"); | ||
} | ||
|
||
[Test] | ||
public void TestRegions() | ||
{ | ||
string key = "key"; | ||
ICache cache1 = provider.BuildCache("nunit1", props); | ||
ICache cache2 = provider.BuildCache("nunit2", props); | ||
string s1 = "test1"; | ||
string s2 = "test2"; | ||
cache1.Put(key, s1); | ||
cache2.Put(key, s2); | ||
Thread.Sleep(1000); | ||
object get1 = cache1.Get(key); | ||
object get2 = cache2.Get(key); | ||
Assert.IsFalse(get1 == get2); | ||
} | ||
|
||
[Test] | ||
public void TestRemove() | ||
{ | ||
string key = "key1"; | ||
string value = "value"; | ||
|
||
ICache cache = provider.BuildCache("nunit", props); | ||
Assert.IsNotNull(cache, "no cache returned"); | ||
|
||
// add the item | ||
cache.Put(key, value); | ||
Thread.Sleep(1000); | ||
|
||
// make sure it's there | ||
object item = cache.Get(key); | ||
Assert.IsNotNull(item, "item just added is not there"); | ||
|
||
// remove it | ||
cache.Remove(key); | ||
|
||
// make sure it's not there | ||
item = cache.Get(key); | ||
Assert.IsNull(item, "item still exists in cache"); | ||
} | ||
|
||
[Test] | ||
public void TestRemove144() | ||
{ | ||
string key = "key1"; | ||
string value = "value"; | ||
|
||
//memcached 1.4+ drops support for expiration time specified for Delete operations | ||
//therefore if you install memcached 1.4.4 this test will fail unless corresponding fix is implemented in MemCacheClient.cs | ||
//the test will fail because Remove won't actually delete the item from the cache! | ||
//the error you will see in the log is: "Error deleting key: nunit@key1. Server response: CLIENT_ERROR bad command line format. Usage: delete <key> [noreply]" | ||
|
||
//Now, Memcached.ClientLibrary incorrectly divides expiration time for Delete operation by 1000 | ||
//(for Add and Set operations the expiration time is calculated correctly) | ||
//that's why we need to set expiration to 20000, otherwise it will be treated as 20ms which is too small to be sent to server (the minimum value is 1 second) | ||
props["expiration"] = "20000"; | ||
|
||
//disabling lingering delete will cause the item to get immediately deleted | ||
//this parameter is NEW and the code to make it work is part of the proposed fix | ||
props.Add("lingering_delete_disabled", "true"); | ||
|
||
ICache cache = provider.BuildCache("nunit", props); | ||
Assert.IsNotNull(cache, "no cache returned"); | ||
|
||
// add the item | ||
cache.Put(key, value); | ||
Thread.Sleep(1000); | ||
|
||
// make sure it's there | ||
object item = cache.Get(key); | ||
Assert.IsNotNull(item, "item just added is not there"); | ||
|
||
// remove it | ||
cache.Remove(key); | ||
|
||
// make sure it's not there | ||
item = cache.Get(key); | ||
Assert.IsNull(item, "item still exists in cache"); | ||
} | ||
} | ||
} |
Oops, something went wrong.