Skip to content

Commit

Permalink
#1082 #1083 add purge document
Browse files Browse the repository at this point in the history
  • Loading branch information
Sandy C committed Nov 21, 2018
1 parent 1df98fe commit f9bd727
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 12 deletions.
69 changes: 62 additions & 7 deletions src/Couchbase.Lite.Shared/API/Database/Database.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
using System.Linq;
using System.Runtime.ExceptionServices;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;

using Couchbase.Lite.DI;
Expand Down Expand Up @@ -94,6 +95,8 @@ public sealed unsafe class Database : IDisposable

#region Variables

private static readonly TimeSpan HousekeepingDelayAfterOpen = TimeSpan.FromSeconds(3);

[NotNull]
private readonly Dictionary<string, Tuple<IntPtr, GCHandle>> _docObs = new Dictionary<string, Tuple<IntPtr, GCHandle>>();

Expand All @@ -111,10 +114,11 @@ public sealed unsafe class Database : IDisposable
[NotNull]
private readonly TaskFactory _callbackFactory = new TaskFactory(new QueueTaskScheduler());

#if false
#if false
private IJsonSerializer _jsonSerializer;
#endif
#endif

private Timer _expirePurgeTimer;
private C4DatabaseObserver* _obs;
private GCHandle _obsContext;
private C4Database* _c4db;
Expand Down Expand Up @@ -745,8 +749,13 @@ public void Purge(Document document)
public void Purge(string docId)
{
CBDebug.MustNotBeNull(Log.To.Database, Tag, nameof(docId), docId);
var document = GetDocument(docId);
Purge(document);
try {
LiteCoreBridge.Check(err => Native.c4db_beginTransaction(_c4db, err));
PurgeDocById(docId);
LiteCoreBridge.Check(e => Native.c4db_endTransaction(_c4db, true, e));
} catch (Exception) {
LiteCoreBridge.Check(e => Native.c4db_endTransaction(_c4db, false, e));
}
}

/// <summary>
Expand All @@ -762,9 +771,6 @@ public void Purge(string docId)
/// doesn't exist</exception>
public bool SetDocumentExpiration(string docId, DateTimeOffset? timestamp)
{
if(GetDocument(docId) == null) {
throw new CouchbaseLiteException(C4ErrorCode.NotFound, "Cannot find the document.");
}
var succeed = false;
ThreadSafety.DoLockedBridge(err =>
{
Expand All @@ -774,6 +780,7 @@ public bool SetDocumentExpiration(string docId, DateTimeOffset? timestamp)
var Timestamp = timestamp?.ToUnixTimeMilliseconds();
succeed = Native.c4doc_setExpiration(_c4db, docId, (ulong)Timestamp, err);
}
SchedulePurgeExpired(TimeSpan.Zero);
return succeed;
});
return succeed;
Expand Down Expand Up @@ -951,6 +958,29 @@ internal void ResolveConflict([NotNull]string docID)
}
});
}

internal void SchedulePurgeExpired(TimeSpan delay)
{
var nextExpiration = Native.c4db_nextDocExpiration(_c4db);
if (nextExpiration > 0) {
var delta = (DateTimeOffset.FromUnixTimeMilliseconds((long)nextExpiration) - DateTimeOffset.UtcNow).Add(TimeSpan.FromSeconds(1));
var expirationTimeSpan = delta > delay ? delta : delay;
if (expirationTimeSpan.TotalMilliseconds >= UInt32.MaxValue) {
_expirePurgeTimer.Change(TimeSpan.FromMilliseconds(UInt32.MaxValue - 1), TimeSpan.FromMilliseconds(-1));
Log.To.Database.I(Tag, "{0:F3} seconds is too far in the future to schedule a document expiration," +
" will run again at the maximum value of {0:F3} seconds", expirationTimeSpan.TotalSeconds, (UInt32.MaxValue - 1) / 1000);
} else if (expirationTimeSpan.TotalSeconds <= Double.Epsilon) {
_expirePurgeTimer.Change(Timeout.Infinite, Timeout.Infinite);
PurgeExpired(null);
} else {
_expirePurgeTimer.Change(expirationTimeSpan, TimeSpan.FromMilliseconds(-1));
Log.To.Database.I(Tag, "Scheduling next doc expiration in {0:F3} seconds", expirationTimeSpan.TotalSeconds);
}
} else {
Log.To.Database.I(Tag, "No pending doc expirations");
}
}

#endregion

#region Private Methods
Expand Down Expand Up @@ -1089,6 +1119,8 @@ private void Open()
return Native.c4db_open(path, &localConfig2, err);
});
});

_expirePurgeTimer = new Timer(PurgeExpired, null, HousekeepingDelayAfterOpen, TimeSpan.FromMilliseconds(-1));
}

private void PostDatabaseChanged()
Expand Down Expand Up @@ -1365,6 +1397,29 @@ private void VerifyDB([NotNull]Document document)
}
}

private void PurgeDocById(string id)
{
ThreadSafety.DoLockedBridge(err =>
{
return Native.c4db_purgeDoc(_c4db, id, err);
});
}

private void PurgeExpired(object state)
{
var cnt = 0L;
ThreadSafety.DoLockedBridge(err =>
{
CheckOpen();
cnt = Native.c4db_purgeExpiredDocs(_c4db, err);
Log.To.Database.I(Tag, "{0} purged {1} expired documents", this, cnt);
SchedulePurgeExpired(TimeSpan.FromSeconds(1));
if (err->code>0 && err->domain>0)
return false;
return true;
});
}

#endregion

/// <inheritdoc />
Expand Down
44 changes: 39 additions & 5 deletions src/Couchbase.Lite.Tests.Shared/DocumentTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1808,21 +1808,21 @@ public void TestDeleteDocAndGetDoc()
[Fact]
public void TestSetAndGetExpirationFromDoc()
{
var dto30 = DateTimeOffset.Now.AddSeconds(30);
var dto0 = DateTimeOffset.Now;
var dto30 = DateTimeOffset.UtcNow.AddSeconds(30);
var dto0 = DateTimeOffset.UtcNow;

using (var doc1a = new MutableDocument("doc1"))
using (var doc1b = new MutableDocument("doc2"))
using (var doc1c = new MutableDocument("doc3")) {
doc1a.SetInt("answer", 42);
doc1a.SetInt("answer", 12);
doc1a.SetValue("options", new[] { 1, 2, 3 });
Db.Save(doc1a);

doc1b.SetInt("answer", 42);
doc1b.SetInt("answer", 22);
doc1b.SetValue("options", new[] { 1, 2, 3 });
Db.Save(doc1b);

doc1c.SetInt("answer", 42);
doc1c.SetInt("answer", 32);
doc1c.SetValue("options", new[] { 1, 2, 3 });
Db.Save(doc1c);

Expand All @@ -1836,6 +1836,22 @@ public void TestSetAndGetExpirationFromDoc()
Db.GetDocumentExpiration("doc3").Should().Be(null);
}

[Fact]
public void TestSetExpirationFromDoc()
{
var dto3 = DateTimeOffset.UtcNow.AddSeconds(3);
using (var doc1a = new MutableDocument("doc1")) {
doc1a.SetInt("answer", 12);
doc1a.SetValue("options", new[] { 1, 2, 3 });
Db.Save(doc1a);

Db.SetDocumentExpiration("doc1", dto3).Should().Be(true);

}
Thread.Sleep(4000);
var doc = Db.GetDocument("doc1").Should().BeNull();
}

[Fact]
public void TestSetExpirationOnNoneExistDoc()
{
Expand All @@ -1852,6 +1868,24 @@ public void TestGetExpirationFromNoneExistDoc()
badAction.ShouldThrow<CouchbaseLiteException>("Cannot find the document.");
}

[Fact]
public void TestLongExpiration()
{
var now = DateTime.UtcNow;
using (var doc = new MutableDocument("doc")) {
doc.SetInt("answer", 42);
doc.SetValue("options", new[] { 1, 2, 3 });
Db.Save(doc);

Db.GetDocumentExpiration("doc").Should().BeNull();
Db.SetDocumentExpiration("doc", new DateTimeOffset(DateTime.UtcNow.AddDays(60)));

var exp = Db.GetDocumentExpiration("doc");
exp.Should().NotBeNull();
(Math.Abs((exp.Value - now).TotalDays - 60.0) < 1.0).Should().BeTrue();
}
}

private void PopulateData(MutableDocument doc)
{
var date = DateTimeOffset.Now;
Expand Down

0 comments on commit f9bd727

Please sign in to comment.