I am working on a .NET Webservice which will fetch some Config, do userAuthentication and log all requests and Responses to Couchbase. The question is what is the best Aproach to create the bucket ?
Should i create a Connection and Bucket for Each request or is it recommended to create Connection at Startup and Bucket and then reuse this one over and over. If so what would be the recommended way to check if the bucket is still open to accept new calls like upsert etc before issuing the command So i can create a new one if no longer valid.
If a Connection per Request is the way to go how do i clean up the Bucket and Connection to not end up with 1000 th of dead connection objects ?
The recommend approach is a single Cluster object shared throughout your application lifetime. It is self-repairing, but does have some diagnostic functions to check status.
The ClusterHelper object is a useful tool for managing this singleton.
Brant
You say Cluster object, how about the bucket ? Do i close and dispose after each transaction ?
Do you know if there is a good sample somewhere avail ?
The Cluster object manages the Bucket objects for you, just keep calling OpenBucket, and donât dispose the buckets.
There are definitely some examples out there, are you on .NET Framework or .NET Core?
.NET Framework.
I would be a bit concerned about calling openBucket over and over as i did this some time ago in NodeJs and run into a hell of issues as i was hovering around 50K connections and it became slow and died. After cleaning up my connection and buckets i never got above 50 open ones
Hereâs an example for .NET Framework: https://docs.couchbase.com/dotnet-sdk/current/sample-app-backend.html
This example uses ClusterHelper.GetBucket, which will only open the bucket once and returns it from a dictionary for every subsequent request. However, the same is true for Cluster.OpenBucket. Itâs perfectly safe to call repeatedly, itâs somewhat poorly named. It doesnât actually open a bucket unless it hasnât been opened before, otherwise it returns a singleton.
Ok, i tried the code but have little to no luck getting past the authentication. I was able based on the sample code to move it to vb.net for my project and i am able to get the Cluster Initialized. But when i make the actual call to get a doc from couchbase
Here is how i call it
return CouchbaseStorageHelper.Instance.Get("uriEndPoint::C985544D-2A31-44A0-8228-3318A56DB8E9", "SOAPAPI");
Here is the Error. I know the User and password is correct
Couchbase.Configuration.Server.Serialization.BootstrapException
HResult=0x80131500
Message=Could not bootstrap - check inner exceptions for details.
Source=Couchbase.NetClient
StackTrace:
at Couchbase.Core.ClusterController.CreateBucketImpl(String bucketName, String password, IAuthenticator authenticator)
at Couchbase.Core.ClusterController.CreateBucket(String bucketName, String password, IAuthenticator authenticator)
at Couchbase.Cluster.OpenBucket(String bucketName, String password)
at Couchbase.ClusterHelper.GetBucket(String bucketName, String password)
at Couchbase.ClusterHelper.GetBucket(String bucketName)
at CouchTest.Storage.Couchbase.CouchbaseStorageHelper.Get(String id, String bucket) in C:\Users\Alex Ponnath\source\repos\CouchTest\CouchTest\Storage\Couchbase\CouchbaseStorageHelper.cs:line 83
at CouchTest.Controllers.TestController.Get() in C:\Users\Alex Ponnath\source\repos\CouchTest\CouchTest\Controllers\TestController.cs:line 17
at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.<>c__DisplayClass6_1.b__3(Object instance, Object methodParameters)
at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.Execute(Object instance, Object arguments)
at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ExecuteAsync(HttpControllerContext controllerContext, IDictionary`2 arguments, CancellationToken cancellationToken)Inner Exception 1:
AuthenticationException: Authentication failed for bucket âSOAPAPIâ
First i thought i did maybe have a problem with my C# to VB.net translation but i get the same when running the C# code. Could this be due to a mismatch or compatibility between code and Server ? I am using 6.5 Server and the 2.7.16 version of the .NET SDK.
@aponnath this doesnât seem to be any issue with compatibility, according to the compatibility matrix looks like the server version you are using is compatible with the SDK.
Did you get a chance to look at the example code @btburnett3 was referring to ? the reason I am asking is that âCouchbaseStorageHelperâ is not available in that project so I am wondering if you have any kind of special implementation of ClusterHelper ?
The documentation does advise and instructs to minimize the use of cluster connections and the sample code shows how to authenticate to a cluster using ClusterHelper class. This would hold a connection to the server (Singleton) and would manage it.
The App_Start and App_End in global.asax handles the opening and disposing of the cluster.
Can you share the snippet of your code that basically authenticates to the cluster ? Does the same UI and password work with CB UI ?
Not sure what you are referring to as far as the CouchbaseStorageHelper not part of project. Below is the class in question with is part of the GITHUB Repo
And yes i use the CouchbaseConfig to setup the Cluster Helper but this does not create the initial connection, that happens only when you make the first call to the cluster instance. And thatâs where i get the rejection do to authentication failure. If i go and do this via my code without clusterhelper the credentials work just fine
Ok after some more playing around i think i found the source of the issue. In the current version Couchbase 6.x uses a username and password authentication. At one time there was only a basket password. When debugging the code i found that the code sets the Username = bucketname. When i go and create a user with the bucketname the authentication passes.
The code i am referring to is in the ClusterController.cs starting with line 210
private KeyValuePair<string, string> ResolveCredentials(string bucketName, string password, IAuthenticator authenticator = null)
{
var username = bucketName;
if (authenticator == null)
{
//try to find a password in configuration
BucketConfiguration bucketConfig;
if (_clientConfig.BucketConfigs.TryGetValue(bucketName, out bucketConfig)
&& bucketConfig.Password != null)
{
bucketName = bucketConfig.BucketName;
password = bucketConfig.Password;
}
}
else
{
if (authenticator is CertAuthenticator)
{
return new KeyValuePair<string, string>(username ?? bucketName, null);
}
var bucketCredentials = authenticator.GetCredentials(AuthContext.BucketKv, bucketName);
switch (authenticator.AuthenticatorType)
{
case AuthenticatorType.Classic:
if (bucketCredentials.ContainsKey(bucketName))
{
username = bucketName;
password = bucketCredentials.First().Value;
}
else
{
throw new BucketNotFoundException(string.Format("Could not find credentials for bucket: {0}", bucketName));
}
break;
case AuthenticatorType.Password:
username = bucketCredentials.First().Key;
password = bucketCredentials.First().Value;
break;
default:
throw new ArgumentOutOfRangeException();
}
}
return new KeyValuePair<string, string>(username ?? bucketName, password);
}
i observed that the username was never set âŠ
So how does one overcome this issue other then going and creating a user with the same name as bucket ?
@aponnath I was about to reply back.
-
The reason I was not seeing âCouchbaseStorageHelperâ class is because cloning the repo is not bringing that down on my local. I do see this now in the remote repository under storage/couchbase folder. Not sure where its used in the project.
-
I am running a CB 6.5 EE locally , I pulled down the repo and called the API directly and it works.
You will have to authenticate to the Cluster passing in username and password. Just like the code does in the Register method in APP_START (yes that gets called the first time with the very first request). If you step through the register code, it has ensure index which basically attempts to get the bucket does that work for you ? (Sanity check).
I am not sure why username should be equal to the bucket name. I dont have any setup like that.
I have username = âAdministratorâ
Password = âpasswordâ (default that comes with any CB installation)
Bucket Name = travel-sample
Couchbase Server has used Role Based Access Control (RBAC) since version 5.0; previously authentication was done with a âbucket name/passwordâ combination. RBAC requires you to use a ârole name/passwordâ combination.
var cluster = new Cluster(new ClientConfiguration
{
Servers = new List<Uri> { new Uri("http://10.112.170.101") }
});
var authenticator = new PasswordAuthenticator("username", "password");
cluster.Authenticate(authenticator);
var bucket = cluster.OpenBucket("bucketname");
More details here under the âCreating Cluster and Bucketâ section: Start Using the .NET SDK | Couchbase Docs
-Jeff
Thanks thatâs how i use it currently without the ClusterHelper, but how you use this with the ClusterHelper as one specifys the username password like
Dim config = New ClientConfiguration()
config.BucketConfigs.Clear()
config.Servers = New List(Of Uri)(New Uri() {New Uri(serverUri)})
config.BucketConfigs.Add("SOAPAPI", New BucketConfiguration With {
.BucketName = "SOAPAPI",
.Username = user,
.Password = password
})
ClusterHelper.Initialize(config)
Dim bucket = ClusterHelper.GetBucket("SOAPAPI")
Dim myData = bucket.GetDocument(Of Object)("uriEndPoint::C985544D-2A31-44A0-8228-3318A56DB8E9")
Debug.WriteLine(myData)
So where would i tell the ClusterHelper to use RBAC Authentication when i try to open the bucket ?
Dim config = New ClientConfiguration()
config.BucketConfigs.Clear() // optional
config.Servers = New List(Of Uri)(New Uri() {New Uri(serverUri)})
Dim credentials = new PasswordAuthenticator("Administrator", "password");
//ClusterHelper.Initialize(config)
ClusterHelper.Initialize(config, credentials);
Dim bucket = ClusterHelper.GetBucket("SOAPAPI");
Please see if this helps!!!
Two ways:
1 - ClusterHelper has an overload that takes an IAuthenticator
you can replace the PasswordAuthenticator
in my example as the second parameter.
or
2 - Set the ClientConfiguration.Password
and ClientConfiguration.Username
fields with your RBAC role and password.
Additionally, remove the config.BucketConfigs.Add
line, its for backwards compatibility with non-RBAC clusters and implies bucket authentication, which you cannot do with RBAC servers.
-Jeff
But isnât it the point of the BucketConfig to add possibly multiple bucket configs to the ClusterHelper so when you call the bucket you can call it by just passing the name of the bucket without credentials since they are stored in the ClusterHelper instance. So as there could be the case that there is multiple usernames and passwords for different buckets how else would you add them to the BucketConfigs ?
Dim config = New ClientConfiguration()
config.BucketConfigs.Clear()
config.Servers = New List(Of Uri)(New Uri() {New Uri(serverUri)})
Dim credentials = New PasswordAuthenticator(user, password)
ClusterHelper.Initialize(config, credentials)
Dim bucket = ClusterHelper.GetBucket("SOAPAPI")
Dim bucket2 = ClusterHelper.GetBucket("default")
Dim myData = bucket.GetDocument(Of Object)("uriEndPoint::C985544D-2A31-44A0-8228-3318A56DB8E9")
Debug.WriteLine(myData)
Dim myData2 = bucket2.GetDocument(Of Object)("cname::0151080f-8c0a-4f2f-9927-a818245cd681")
Debug.WriteLine(myData2)
The above code works as it seems as you can pass the actual bucketname without having it configured in the bucketConfig but it does not allow for different Username and Passwords for Buckets anymore.
That is the whole point of RBAC; you create a role and assign privileges to it which are associated with a resource. The method you are following is the older bucket/password approach, which has many limitations.
You can read more here.
-Jeff
Jeff, i was able to make it work. I think it would be a good idea to somewhere mention in this sample project that it will default to the old bucket password. That would safe someone some time not having to debug the source code like i did to find that out.
Good to hear you got its straightened out; yeah understood, its somewhat of a gotcha. Weâll work on improving things in the future.
-Jeff