Skip to end of metadata
Go to start of metadata

Official information may be found at the .NET Client Library page.

Using Multiple Buckets with the CouchbaseClient

It is not possible to configure (in app|web.config) a single instance of a CouchbaseClient to work with multiple buckets. Though it is possible to programmatically reconstruct a client to work with multiple buckets, it is not recommended. The process of creating a client is expensive (relative to other Couchbase operations) and should ideally be done once per app domain.

It is possible however to set multiple config sections in app|web.config to allow for multiple client instances to be created, while still maintaining bucket affinity.

<?xml version="1.0"?>
<configuration>
  <configSections>
    <sectionGroup name="couchbase">
      <section name="bucket-a" type="Couchbase.Configuration.CouchbaseClientSection, Couchbase"/>
      <section name="bucket-b" type="Couchbase.Configuration.CouchbaseClientSection, Couchbase"/>
    </sectionGroup>
  </configSections>
  
  <couchbase>
    <bucket-a>
      <servers bucket="default">
        <add uri="http://127.0.0.1:8091/pools" />
      </servers>
    </bucket-a>
    <bucket-b>
      <servers bucket="beernique" bucketPassword="b33rs">
        <add uri="http://127.0.0.1:8091/pools" />
      </servers>
    </bucket-b>
  </couchbase>
  
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
  </startup>
</configuration>

After defining the config sections, bucket specific clients are created by reading the appropriate config sections and passing the config section reference to the constructor of the CouchbaseClient. Again, constructing the client should not be done per operation, but rather per app domain.

var bucketASection = (CouchbaseClientSection)ConfigurationManager.GetSection("couchbase/bucket-a");
var bucketBSection = (CouchbaseClientSection)ConfigurationManager.GetSection("couchbase/bucket-b");

var clientA = new CouchbaseClient(bucketASection);
var clientB = new CouchbaseClient(bucketBSection);

clientA.ExecuteStore(StoreMode.Set, "fooA", "barA");
var itemA = clientA.Get<string>("fooA");
Console.WriteLine(itemA);

clientB.ExecuteStore(StoreMode.Set, "fooB", "barB");
var itemB = clientB.Get<string>("fooB");
Console.WriteLine(itemB);

Handling Failures with the Operation Results API

The standard public API (i.e., Get, Store, etc.) exposed by the .NET Client Library is based largely on the .NET Memcached client library Enyim.Caching. These methods were created to support the use case of working with a distributed cache - not a persistent store. As a result, these methods return simple values and swallow exceptions.

For example, if an I/O exception occurred during the following call, then the value of getResult would be null.

var getResult = client.Get("SomeKey");

Similarly, if the key already existed when the following snippet executes, the value of storeResult would simply be false.

var storeResult = client.Store(StoreMode.Add, "ExistingKey", "SomeValue");

In summary, for the standard API, retrieve operations will return null for cache misses, I/O exceptions and all other error conditions. Store operations will return false for key errors and I/O exceptions. No exceptions should bubble up to the caller when using the standard API.

In an effort to expose more about an operation's success or failure, additions to the API were introduced starting in client version 1.1. These new methods mirror the existing (while maintaining backwards compatibility), but are prefixed with "Execute." The return value of each of these methods is an instance of an IOperationResult implementation.

With the new API, a caller may use ExecuteGet to learn more about the state of an operation.

var getResult = client.ExecuteGet("foo");

//some examples of how properties are set on the results follow
//sample is not meant to imply flow control for your application

//check the success of the operation
if (getResult.Success) {
   Console.WriteLine("Get result successful and the value was {0}", getResult.Value);
}

//exceptions will typically be I/O related and are not always present for a failure
if (getResult.Exception != null) {   
   Console.WriteLine("Exception occurred: {0}", getResult.Exception.Message);
}

//Check if the nullable status code has a value, 
//on a cache miss, for example, this value will be 1
if (getResult.StatusCode != null && getResult.HasValue) {
    Console.WriteLine("Server sent StatusCode {0}", getResult.StatusCode.Value);
}

//Check the message (note - the constants will be released in v1.1.7)
//success would also be false, but no exception
//StatusCode would also be equal to (int)StatusCodeEnums.NotFound 
if (getResult.Message == StatusCodeMessages.NOT_FOUND) {
    Console.WriteLine("Key not found");
}

In summary:

Property Description
Success false when key is not found or exception is thrown
Exception not null when a handled I/O exception is swallowed, null on operation failures
StatusCode not null when server returns valid status code, null on I/O exceptions
Message null when server returns valid results, not null on I/O exceptions or operation failures

Similarly, Store operations return StoreOperationResult instances.

var storeResult = client.ExecuteStore(StoreMode.Add, "foo", "bar");

//some examples of how properties are set on the results follow
//again, sample is not meant to imply flow control for your application

//check the success of the operation
if (storeResult.Success) {
   Console.WriteLine("Store result successful and the cas value was {0}", storeResult.Cas);
}

//exceptions will typically be I/O related and are not always present for a failure
if (storeResult.Exception != null) {   
   Console.WriteLine("Exception occurred: {0}", storeResult.Exception.Message);
}

//Check if the nullable status code has a value, 
//when trying to replace an non-existing key, for example, this value will be 1
if (storeResult.StatusCode != null && getResult.HasValue) {
    Console.WriteLine("Server sent StatusCode {0}", getResult.StatusCode.Value);
}

//Check the message (note - the constants will be released in v1.1.7)
//success would also be false, but no exception
//StatusCode would also be equal to (int)StatusCodeEnums.NotFound 
if (storeResult.Message == StatusCodeMessages.NOT_FOUND) {
    Console.WriteLine("Key not found");
}

Generally speaking, testing the Success property is the starting point for checking for any failures. Success should never be false if an operation succeeded on the server (status code is 0). The various properties described above could then be used to test for swallowed exceptions or failed operations that didn't raise an exception.

Also, please note that there were missing code paths in the current version of the Enyim.Caching client that was released with the .NET 1.1 library. Specifically, operation failures that should report a StatusCode were falling through to a "Node not found condition." An upcoming 1.1.7 release will patch Enyim and resolve this issue - also reducing (or eliminating) the need for the InnerResult structure that currently is in place.

Using the CouchbaseClient in a WCF Application

Unlike web applications, Windows services and console applications, WFC services are stateless. Typically, each client request is handled by a new instance of the service. This behavior makes it hard to manage a CouchbaseClient in the recommended way - once per app domain (or HTTPApplication instance).

If possible, you can avoid the performance penalties associated with client instantiation by setting your service to use the single instance context mode. When set to single, there will be a single instance of your service handling all client requests.

[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)] 
public class Service : IService
{
}

If hosting in IIS using HTTP, you can also set your WCF application to use ASP.NET compatibility mode. In addition to enabling ASP.NET compatibility mode in your web.config, your service will need to specify it allows or requires ASP.NET compatibility.

[AspNetCompatibilityRequirements(RequirementsMode=AspNetCompatibilityRequirementsMode.Allowed)]
public class Service : IService
{
}
<system.serviceModel>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
</system.serviceModel>

With ASP.NET compatibility enabled, you will be able to add a Global.asax to your project and hook into its events.

public static CouchbaseClient Client = null;

protected void Application_Start(object sender, EventArgs e)
{
    Client = new CouchbaseClient();
}

Bulk Loading

The code below is ported from the Java discussion on bulk loading in this wiki. See Bulk Loading for more information.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Couchbase;
using Couchbase.Configuration;
using Enyim.Caching.Memcached.Results;
using Enyim.Caching.Memcached;
using System.Threading;

namespace BulkLoader
{
	public class StoreHandler
	{
		CouchbaseClient _cbc;

		public StoreHandler(IList<Uri> uris, string bucketName, string bucketPassword)
		{
			var config = new CouchbaseClientConfiguration();
			foreach (var uri in uris)
			{
				config.Urls.Add(uri);
			}
            config.Bucket = bucketName;
			config.BucketPassword = bucketPassword;

			_cbc = new CouchbaseClient(config);
		}

		/// <summary>
		/// Performa  regular Store with storeMode.Set
		/// </summary>
		/// <param name="key"></param>
		/// <param name="value"></param>
		/// <returns></returns>
		public IStoreOperationResult Set(string key, object value)
		{
			return _cbc.ExecuteStore(StoreMode.Set, key, value);
		}

		/// <summary>
		/// Continuously try a set with exponential backoff until number of tries or
		/// successful.  The exponential backoff will wait a maximum of 1 second, or whatever
		/// </summary>
		/// <param name="key"></param>
		/// <param name="value"></param>
		/// <param name="tries">Number of tries before giving up</param>
		/// <returns></returns>
		public IStoreOperationResult Set(string key, object value, int tries)
		{
			var backoffExp = 0;
			var tryAgain = false;
			IStoreOperationResult result = null;			

			try
			{
				do
				{
					if (backoffExp > tries)
					{
						throw new ApplicationException("Could not perform a set after " + tries + " tries.");
					}

					result = _cbc.ExecuteStore(StoreMode.Set, key, value);
					if (result.Success) break;

					if (backoffExp > 0)
					{
						var backOffMillis = Math.Pow(2, backoffExp);
						backOffMillis = Math.Min(1000, backOffMillis); //1 sec max
						Thread.Sleep((int)backOffMillis);
						Console.WriteLine("Backing off, tries so far: " + backoffExp);
					}
					backoffExp++;

					if (! result.Success)
					{
						var message = result.InnerResult != null ? result.InnerResult.Message : result.Message;						 
						Console.WriteLine("Failed with status: " + message);						
					}

					//Future versions of the .NET client will flatten the results and make checking for 
					//InnerResult objects unnecessary
					tryAgain = (result.Message != null && result.Message.Contains("Temporary failure") ||
								result.InnerResult != null && result.InnerResult.Message.Contains("Temporary failure"));

				} while (tryAgain);
				
			}
			catch (Exception ex)
			{
				Console.WriteLine("Interrupted while trying to set.  Exception:" + ex.Message);
			}

			// note that other failure cases fall through.  status.isSuccess() can be
			// checked for success or failure or the message can be retrieved.
			return result;

		}
	}
}

Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.