Search code examples
mongodbmongodb-.net-drivermongodb-csharp-2.0

Documents not expiring using TTL in mongodb 3.0 (.Net driver 2.0)


I have the following code. The EnsureIndexes is called by the constructor to make sure that a TTL index has been created on the _expireAt field. Then when a document is inserted by calling the AddOrUpdateItem method, it adds a future date to the _expireAt field. However, that date passes and the document never expires. What am I doing wrong?

private void EnsureIndexes()
    {
        if (!_indexChecked)
        {
            // TTL index
            var tsk = 
                MongoCollection.Indexes.CreateOneAsync(Builders<BsonDocument>.IndexKeys.Ascending("_expireAt"),
                        new CreateIndexOptions() { ExpireAfter = TimeSpan.FromSeconds(0) });

            tsk.Wait();

            _indexChecked = true;
        }
    }
public void AddOrUpdateItem(string key, TValue value, TimeSpan timeout)
    {
        var json = value.ToJson();
        dynamic jObject = JObject.Parse(json);
        jObject._expireAt = DateTime.UtcNow.Add(timeout);
        json = jObject.ToString();

        var replacementDocument = BsonSerializer.Deserialize<BsonDocument>(json);
        var filter = new BsonDocument("_id", key);
        var options = new UpdateOptions {IsUpsert = true};
        var tsk = MongoCollection.ReplaceOneAsync(filter, replacementDocument, options);

        try
        {
            tsk.Wait();
        }
        catch (AggregateException ex)
        {
            // TODO: Log

            throw;
        }
    }

The following is returned with the command to getIndices on the Mongo collection.

> db.Users.getIndices()

[ { "v" : 1, "key" : { "_id" : 1 }, "name" : "id", "ns" : "AuditDemo.Users" }, { "v" : 1, "key" : { "_expireAt" : 1 }, "name" : "_expireAt_1", "ns" : "AuditDemo.Users", "expireAfterSeconds" : 0 } ] >

In my AddOrUpdateItem method I first serialize a generic type to json in order to be able to add a dynamic element for the expireAt. Then I use the BsonSerializer to deserialize this modified json into a BsonDocument. At this point, does the BsonDocument translate the datetime json string to a BSon date type in order for the TTL index to work?

Result from the findOne command

> db.Users.findOne({"_expireAt":{$exists: true}})

{ "_id" : "0", "UserGuid" : { "Value" : "054f6141-e655-41dd-a9d5-39382d3360ab" }, "UserName" : null, "FirstName" : { "Value" : "JORDAN" }, "LastName" : { "Value" : "ACEVEDO" }, "Email" : { "Value" : "JORDAN.ACEVEDO@fake.com" }, "__typ" : "AuditDemo.ConsoleApplication.Models.Wss.UserInfo, ConsoleTest App, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null", "_expireAt" : "2015-05-31T10:23:15.8979321Z" } >


Solution

  • OK, I figured out how to correct this date issue. The date time string that I got from JSON.Net, was not getting stored a BSON date object. So I had to call the BsonDateTime.create() method on the deserialized BsonDocument property and force it to be a BSON date. When it gets stored in the correct data type, the TTL index works as expected.

            DateTime expiresDate = new DateTime(DateTime.UtcNow.Ticks, DateTimeKind.Utc).Add(timeout);
            var replacementDocument = BsonSerializer.Deserialize<BsonDocument>(json);
            replacementDocument["_expireAt"] = BsonDateTime.Create(expiresDate);
            var filter = new BsonDocument("_id", key);
            var options = new UpdateOptions {IsUpsert = true};
            var tsk = MongoCollection.ReplaceOneAsync(filter, replacementDocument, options);