Couchbase .Net SDK 2.7 not setting document expiry

I am trying to set the document expiry using the Mutation Builder as such:

var mutation = _bucket.MutateIn<PaymentStateDocument>(command.CorrelationId.ToString()) .WithExpiry(_settings.DocumentRetentionPeriodTimespan) .Upsert(nameof(PaymentStateDocument.MovedToAvailableOn), _dateTimeProvider.CurrentUtcDateTime) .ArrayAppend(nameof(PaymentStateDocument.Commands), command) .WithCas(cas);

the expiry duration is 90 days :

TimeSpan DefaultDocumentRetentionPeriodTimespan = TimeSpan.FromDays(90)

however when I use a N1QL query to get the expiry I can see that it is still set to zero:

var query = $"SELECT meta() FROM{_settings.PaymentStateBucketName}AS f WHERE f.Identifier = '{identifier}'"; var result = await _paymentStateBucket.QueryAsync<dynamic>(query);

couchbase:enterprise-6.0.1 running in docker
.Net SDK version 2.7.6

Hi @ftaran,

I noticed that in your mutation, there is no call to Execute. I tried a simple version of your example like this:

          var mutation = bucket.MutateIn<dynamic>("123")
            .WithExpiry(TimeSpan.FromDays(90))
            .Upsert("baz", "qux")
            .ArrayAppend("somearray", "x")
            .WithCas(doc.Cas)
            .Execute();

And the resultant meta data:

{
  "meta": {
    "id": "123",
    "rev": "4-15c3647fad8600005def99ea02000001",
    "expiration": 1575983594,
    "flags": 33554433,
    "type": "json"
  }
}

I tried this in 2.7.6 and the latest 2.7.11 version, and it worked in both.

@matthew.groves I am sorry I should have included the whole code.

` public async Task<AvailabilityResult> AppendStateOnMovedToAvailableAsync(ExecuteFinancialImpactCommand command, ulong cas)
    {
        var mutation = _bucket.MutateIn<PaymentStateDocument>(command.CorrelationId.ToString())
            .Upsert(nameof(PaymentStateDocument.MovedToAvailableOn), _dateTimeProvider.CurrentUtcDateTime)
            .ArrayAppend(nameof(PaymentStateDocument.Commands), command)
            .WithExpiry(_settings.DocumentRetentionPeriodTimespan)
            .WithCas(cas);

        var result = await mutation.ExecuteAsync();

        Log(result);

        return MapVoidResult(result);
    }`

it still does not set the expiry, I have also upgraded to your suggested version.

is it possible that this has anything to do with running couchbase inside a container?

Container seems unlikely, but could you list which ports you’ve exposed (assuming that the .NET program is running outside of the container).

Also, would you mind examining the result of the execute? There may be error/exception information in there. Would you also try it without CAS, and see if that works (that might give us a hint to the nature of the issue)?

Finally, do you think you could try to make a minimum reproducible example? In my example, I didn’t follow exactly what you did, because I don’t have all the details of PaymentStateDocument, _settings, etc. Could you try to see if you can reproduce this behavior with simple literals, just so we can narrow down the issue?

1 Like

@matthew.groves Hi,

it seems this is a bug whereby the expiration always is returned as 0 but it does not actually affect the document being deleted at the correct time,
considering the previous point I still have put a small solution together complete with docker compose file that replicates this behaviour, hope this helps.

CouchbaseTests.zip (315.1 KB)

@ftaran,

Based on your code, this appears to be an issue with N1QL queries that return expiration and is not a problem with the .NET SDK itself. In your test, your mutation DOES correctly set the expiration (you can see this by browsing to the document in the Couchbase UI).

However, your automated test is trying to verify by using SELECT meta().expiration, which does not return the expiration unless there is a covering index. Please see MB-25493 for more information about this. There are a couple of workarounds there.

For instance, I made your test pass by changing your GetExpirationOfDocument method:

public async Task<ulong> GetExpirationOfDocument(Guid identifier)
{
    var query = $"SELECT meta().expiration FROM `bucket` AS f LET v = meta().xattrs.`$document` WHERE f.Identifier = '{identifier}'";
    var result = await _bucket.QueryAsync<dynamic>(query);
    var expiration = result.Rows.FirstOrDefault()?.expiration;
    return expiration;
}

Another option is to create a covering index (as spelled out in MB-25493.

I hope this helps. If none of the workarounds in that ticket are satisfactory, I’d recommend starting a new topic in the N1QL forum.