Question About .NET SDK Performance

Hello,

We are using CouchbaseNetClient, Version 3.1.6 and INamedBucketProvider via DependencyInjection.

We have a repository like this:

public class EntityRepository : IEntityRepository
{
	private readonly IEntityBucketProvider _entityBucketProvider;

	public EntityRepository(IEntityBucketProvider entityBucketProvider)
	{
		_entityBucketProvider = entityBucketProvider;
	}

	public async Task Upsert(Entity entity)
	{
		var bucket = await GetBucket();
		var defaultCollection = await GetDefaultCollection(bucket);
		await defaultCollection.UpsertAsync(entity.Id, entity);
	}

	public async Task<Entity> Get(string id)
	{
		try
		{
			var bucket = await GetBucket();
			var defaultCollection = await GetDefaultCollection(bucket);
			var result = await defaultCollection.GetAsync(id);
			return result.ContentAs<Entity>();
		}
		catch (DocumentNotFoundException)
		{
			return null;
		}
	}

	protected async Task<IBucket> GetBucket()
	{
		var bucket = await _entityBucketProvider.GetBucketAsync();
		return bucket;
	}

	protected static async Task<ICouchbaseCollection> GetDefaultCollection(IBucket bucket)
	{
		var defaultCollection = await bucket.DefaultCollectionAsync();
		return defaultCollection;
	}
}

We call GetBucket and GetDefaultCollection every time we want to Upsert or Get a document.

Does this cause a performance penalty in our client?
Another question is should we hold _defaultCollection as a class field instead of _entityBucketProvider in our repository class and use that in our Get and Upsert methods?

What solution here gives us the best performance?

Thank you

The performance penalty of GetBucketAsync and DefaultCollectionAsync are pretty small, they are optimized to pull from a cache and return ValueTask<T> in order to reduce heap allocations. We have some room for further optimization we’ll probably do at some point, but even now it’s pretty fast. I typically recommend caching them on a per-method basis, rather than per class, unless you’re dealing with a tight inner loop.

That said, you are adding more overhead with your GetBucket and GetDefaultCollection methods. Since they are doing their own async/awaits you’re adding more CPU and heap allocations to each call. I would recommend dropping the async/await on each of those methods, and return ValueTask directly. This will probably then be inlined by the JIT compiler and more efficient.

The only reason to keep your approach is if you need to be certain your methods are included in any stack traces if there is an exception. Dropping the async/await can cause it to disappear from stack traces in some scenarios.

protected ValueTask<IBucket> GetBucket()
{
    return _entityBucketProvider.GetBucketAsync();
}

protected static ValueTask<ICouchbaseCollection> GetDefaultCollection(IBucket bucket)
{
    return bucket.DefaultCollectionAsync();
}

Thanks for your extensive answer. @btburnett3