We’re getting an intermittent ObjectDisposedException (with message “Safe handle has been closed”) on the Exception property of the result when calling Upsert on a bucket. This seems to occur mostly when the document does not yet exist on the bucket. We’ve been able to reproduce the issue in a staging or development environment making it difficult to pin down.
Environment:
- Couchbase .NET Client SDK 2.7.12
- .NET Framework 4.5.2
- Couchbase Server CE 6.0 (3 node cluster)
- Sync Gateway Server 2.6
Code:
We’re using our own multiton implementation which wraps Cluster, Bucket, and Sync Gateway access methods and instantiates new objects based on differing cluster and bucket configurations (i.e. singletons by config). This is required since the application needs to communicate with several clusters with differing bucket and Sync Gateway configurations. It does, however, mean that it is possible to have more than one object that is connecting to the same cluster, although this is not a common use case. An excerpt from the multiton implementation is below:
public sealed class ClusterBucket : IClusterBucket
{
private IBucket Bucket { get; set; }
private ICluster Cluster { get; set; }
public ClusterBucketSyncGateway SyncGateway { get; private set; }
#region Multiton
private static Dictionary<string, ClusterBucket> clusterBuckets = new Dictionary<string, ClusterBucket>();
private static readonly object padlock = new object();
private ClusterBucket(ClusterBucketConfiguration config)
{
var clientConfig = new ClientConfiguration
{
Servers = config.CouchbaseServerUriBuilders.Select(x => x.Uri).ToList()
};
var authenticator = new PasswordAuthenticator(config.Username, config.Password);
Cluster = new Cluster(clientConfig);
Cluster.Authenticate(authenticator);
Bucket = Cluster.OpenBucket(config.BucketName);
SyncGateway = ClusterBucketSyncGateway.GetClusterBucketSyncGateway(config);
}
public static ClusterBucket GetClusterBucket(ClusterBucketConfiguration config)
{
lock (padlock)
{
if (clusterBuckets.ContainsKey(config.Key) == false)
{
clusterBuckets.Add(config.Key, new ClusterBucket(config));
}
}
return clusterBuckets[config.Key];
}
#endregion
...
}
The ObjectDisposedException (with message “Safe handle has been closed”) is thrown from this calling code:
var result = Bucket.Upsert<T>(id, doc);
if (!result.Success)
{
throw result.Exception;
}
Is this a reasonable architectural choice and implementation given our requirements? Looking at the SDK code, it seems like this is not the ObjectDisposedException thrown when the bucket has been disposed, but seems like it might rather been another object that the bucket is referencing that might have been disposed. Does anyone have an idea of what object it might be or the mechanism which might be disposing of it?