Couchbase for IDistributedCache

I’m using IDistributedCache to inject the Couchbase cache in a service that is being used by other services which are being used by the controllers, when I try to perform cache operations, it throws exception saying cluster is not ready. I’m wondering if there’s something to do with the registration of my services or couchbase itself.

Here is how my project is structured:

  1. Controller → Calls ExternalApiService
  2. ExternalApiService → registered as singleton, calls ExternalApiHttpClient
  3. ExternalApiHttpClient → registered as singleton, calls AuthorizationService
  4. AuthorizationService → registered as singleton, calls CacheService
  5. CacheService → registered as singleton, IDistributedCache is being injected here

In Program.cs I have:

builder.Services.AddDistributedCouchbaseCache(cacheBucketName, opt => { })

When I use IDistributedCache directly in the Controller, it never throws, using the CacheService directly in the Controller also works. Is the problem in any of the other services? Should they not be singleton?

@marianayamamoto is there any chance you could put together a minimal code example that reproduces what’s going on, and post it here?

Also, version numbers might be helpful: .NET, Couchbase Server, CouchbaseNetClient, Couchbase.Extension.Caching version numbers.

Sure.
The project is using net7.0 target framework
Couchbase.Extensions.Caching 3.3.5
Couchbase.Extensions.DependencyInjection 3.4.8

Couchbase Server Enterprise 7.1.0

Program.cs:

_ = builder.Services.AddSingleton<ICacheService, CacheService>();
_ = builder.Services.AddCouchbase(options =>
	{
		options.ConnectionString = servers;
		options.UserName = username;
		options.Password = password;
		options.KvTimeout = TimeSpan.FromSeconds(10); // Set the KV (key-value) operation timeout to 10 seconds
		options.ViewTimeout = TimeSpan.FromSeconds(20); // Set the view operation timeout to 20 seconds
		options.QueryTimeout = TimeSpan.FromSeconds(30); // Set the N1QL query timeout to 30 seconds
	});
_ = builder.Services.AddDistributedCouchbaseCache(cacheBucketName, opt => { });

CacheService:

    private readonly IDistributedCache cache;

	public CacheService(IDistributedCache cache)
	{
		this.cache = cache;
	}

	/// <inheritdoc />
	public async Task<T?> GetCachedDataAsync<T>(string key)
	{
		try
		{
			var cachedData = await this.cache.GetStringAsync(key);
			if (cachedData != null)
			{
				return JsonConvert.DeserializeObject<T>(cachedData) ?? default;
			}
		}
		catch (Exception exception)
		{
			// Log exception
		}
		return default;
	}

The stack is the following:
Controller calls ExternalApiService:
var cart = await this.cartService.GetCart(cartId);
ExternalApiService builds a request and calls an ApiHttpClient:

public async Task<Cart> GetCart(string cartId)
{
	var request = new HttpRequest()
	{
		Url = this.httpClient.PrepareRequestUri(this.settings.BaseUrl, string.Format(this.settings.GetCartEndpoint, cartId)),
		Method = HttpMethod.Get
	};

	var response = await this.httpClient.ExecuteAuthorizedRequest<CartObject>(request);
	return response.cart;
}

ApiHttpClient needs to add an Authorization header, so it calls AuthorizationService to retrieve an access token:

public async Task<T> ExecuteAuthorizedRequest<T>(IHttpRequest request)
{
	var accessToken = await this.authService.GetAccessTokenAsync(RefreshToken);
	request.AddHeader("Authorization", accessToken);
	var result = await SendRequestAsync<T>(request);
	return result;
}

AuthorizationService tries to get access token from cache using CacheService, if not there, makes the actual request and saves the response to cache:

public async Task<string> GetAccessTokenAsync(string refreshToken)
{
	var result = await this.cacheService.GetCachedDataAsync<string>(refreshToken);
	if (result != null)
	{
		return result;
	}
	var authInfo = await RenewAccessTokenAsync(refreshToken);
	await this.cacheService.SetCachedDataAsync(refreshToken, authInfo.AccessToken, 5);
	return authInfo.AccessToken;
}

Exceptions thrown:

System.AggregateException: Cluster has not yet bootstrapped. Call WaitUntilReadyAsync(…) to wait for it to complete.\r\n at Couchbase.Extensions.DependencyInjection.Internal.BucketProvider.GetBucketFromClusterAsync(String bucketName, IClusterProvider clusterProvider)\r\n at Couchbase.Extensions.Caching.Internal.DefaultCouchbaseCacheCollectionProvider.GetCollectionAsync()\r\n at Couchbase.Extensions.Caching.CouchbaseCache.Microsoft.Extensions.Caching.Distributed.IDistributedCache.GetAsync(String key, CancellationToken token)\r\n at Microsoft.Extensions.Caching.Distributed.DistributedCacheExtensions.GetStringAsync(IDistributedCache cache, String key, CancellationToken token)

System.AggregateException: Cluster has not yet bootstrapped. Call WaitUntilReadyAsync(…) to wait for it to complete.\r\n at Couchbase.Extensions.DependencyInjection.Internal.BucketProvider.GetBucketFromClusterAsync(String bucketName, IClusterProvider clusterProvider)\r\n at Couchbase.Extensions.Caching.Internal.DefaultCouchbaseCacheCollectionProvider.GetCollectionAsync()\r\n at Couchbase.Extensions.Caching.CouchbaseCache.SetAsync(String key, Byte value, DistributedCacheEntryOptions options, CancellationToken token)

I haven’t forgotten about this! I’m going to try out the code and see what I can learn.

1 Like

I’m trying to piece together your code into a working project, but there are some missing pieces, and I have to make a lot of assumptions. Can you put together a minimum working app that demonstrates the bug, and perhaps push it out to GitHub?

Sure, let me see what I can do.

Hey, I created a quick project with the same structure as the real project, but somehow I cannot reproduce the issue. So I’ll have to investigate it by myself. Thanks for the help.

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.