Get *after* GetAndLock expiry time

I was expecting

  • var locked = getAndLock( for x seconds )
  • wait x + 100ms
  • var notlocked = get() // without lock
  • assert notLocked.Cas != 0xffffffffffffffff

but it fails.

The C# source [1] indicates that the expiry unit is seconds, not milliseconds. The only tests in the driver use 15 seconds, the default

[1] https://github.com/couchbase/couchbase-net-client/blob/28b5ca90b2f5bb65d7629fd1bdd595f24a21ac38/Src/Couchbase/CouchbaseBucket.cs#L827

Client: package id=“CouchbaseNetClient” version=“2.3.11” targetFramework="net462"
Server: running version 4.5.1-2844 Enterprise Edition (build-2844).

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using Couchbase;
using Couchbase.Configuration.Client;
using Couchbase.Core;
using Couchbase.IO;
using NUnit.Framework;

namespace couchtesting
{
    [TestFixture]
    public class couchbase_locking
    {
        private Cluster _cluster;
        private IBucket _bucket;

        [OneTimeSetUp]
        public void BucketSetup()
        {
            var bucketName = "default";
            var config = new ClientConfiguration { Servers = new List<Uri>(new[] { new Uri("http://localhost:8091/") }) };
            config.BucketConfigs.Clear();
            config.BucketConfigs.Add(bucketName, new BucketConfiguration { BucketName = bucketName });

            _cluster = new Cluster(config);
            _bucket = _cluster.OpenBucket(bucketName);
        }

        [OneTimeTearDown]
        public void CouchCleanup()
        {
            _cluster?.Dispose();
        }

        [Test]
        public void get_after_expired_getLock_allows_update_from_unlocked_cas()
        {
            uint OneSecond = 1;
            TimeSpan OneAndABitSeconds = TimeSpan.FromMilliseconds(1100);
            var doc = new Doc(Guid.NewGuid().ToString());
            try
            {
                _bucket.Insert(doc.Id, doc);

                var locked = _bucket.GetAndLock<Doc>(doc.Id, OneSecond);

                var timer = Stopwatch.StartNew();
                Thread.Sleep(OneAndABitSeconds);
                timer.Stop();
                Assert.That(timer.ElapsedMilliseconds, Is.GreaterThan(1000));

                var notlocked = _bucket.Get<Doc>(doc.Id);
                Assert.That(notlocked.Cas, Is.Not.EqualTo(ulong.MaxValue));
            }
            finally
            {
                _bucket.Remove(doc.Id);
            }
        }

        public class Doc
        {
            public Doc(string id) { Id = id; }
            public string Id { get; set; }
        }
    }
}

Hi @adam.g.straughan

Thanks for the bug report, I have been able to reproduce the issue using 4.5.0 and 4.5.1. However, it looks to be a server issue as it does work as expected when using the latest DP of 5.0.

I’ve added your provided unit test to our test suite to help prevent future regressions and I’ll raise it with the server team.

https://issues.couchbase.com/browse/NCBC-1313

Thanks

1 Like

Hello @adam.g.straughan,

@MikeGoldsmith and myself have been working on this issue and have found two problems:

  1. NCBC-1311 The .Net SDK is not setting the lock expiry time. At the protocol level this is set in the extra field.
  2. MB-22778 The behaviour of Couchbase Server when accepting a getAndLock operation without the expiry set has changed in Couchbase Server DB 5.0. While this does not effect you, it does describe the behaviour @MikeGoldsmith saw. We believe this is an issue with the DP build and not the previous versions of Couchbase Server.

Thanks,
Patrick