Skip to content

Commit be068b5

Browse files
authored
Enable version vector functionality and tests, and change Timestamp to ulong instead of DateTimeOffset Also add in an extension to create a DateTimeOffset from the ulong
1 parent 71a66b0 commit be068b5

File tree

3 files changed

+43
-31
lines changed

3 files changed

+43
-31
lines changed

src/Couchbase.Lite.Shared/API/Database/Database.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ public sealed unsafe partial class Database : IDisposable
104104
#region Constants
105105

106106
private static readonly C4DatabaseConfig2 DBConfig = new C4DatabaseConfig2 {
107-
flags = C4DatabaseFlags.Create | C4DatabaseFlags.AutoCompact
107+
flags = C4DatabaseFlags.Create | C4DatabaseFlags.AutoCompact | C4DatabaseFlags.VersionVectors
108108
};
109109

110110
private const string DBExtension = "cblite2";

src/Couchbase.Lite.Shared/API/Document/Document.cs

Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,36 @@
3434

3535
namespace Couchbase.Lite
3636
{
37+
/// <summary>
38+
/// An extension class for helping to turn a nanosecond based timestamp into a
39+
/// <see cref="DateTimeOffset"/> object
40+
/// </summary>
41+
public static class TimestampExtensions
42+
{
43+
private static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
44+
45+
/// <summary>
46+
/// Converts the nanosecond timestamp to a DateTimeOffset in UTC time
47+
/// </summary>
48+
/// <param name="rawVal">The nanosecond timestamp</param>
49+
/// <returns>The DateTimeOffset object using the timestamp, or null if it was invalid</returns>
50+
public static DateTimeOffset? AsDateTimeOffset(this ulong rawVal)
51+
{
52+
if(rawVal == 0) {
53+
return null;
54+
}
55+
56+
// .NET ticks are in 100 nanosecond intervals
57+
return UnixEpoch + TimeSpan.FromTicks((long)(rawVal / 100));
58+
}
59+
}
3760
/// <summary>
3861
/// A class representing a document which cannot be altered
3962
/// </summary>
4063
public unsafe class Document : IDictionaryObject, IJSON, IDisposable
4164
{
4265
private const string Tag = nameof(Document);
4366

44-
private static readonly DateTimeOffset UnixEpoch = new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero);
45-
4667
#region Variables
4768

4869
private string? _revId;
@@ -65,16 +86,16 @@ public unsafe class Document : IDictionaryObject, IJSON, IDisposable
6586
internal C4DatabaseWrapper c4Db
6687
{
6788
get {
68-
Debug.Assert(Database != null && Database.c4db != null);
69-
return Database!.c4db!;
89+
Debug.Assert(Database is { c4db: not null });
90+
return Database.c4db;
7091
}
7192
}
7293

7394
internal C4CollectionWrapper c4Coll
7495
{
7596
get {
76-
Debug.Assert(Collection != null && Collection.c4coll != null);
77-
return Collection!.c4coll!;
97+
Debug.Assert(Collection != null);
98+
return Collection!.c4coll;
7899
}
79100
}
80101

@@ -164,25 +185,16 @@ public string? RevisionID
164185
}
165186

166187
/// <summary>
167-
/// The hybrid logical timestamp that the revision was created.
188+
/// The hybrid logical timestamp that the revision was created, represented in nanoseconds
189+
/// from the unix epoch. If you want this value as a DateTimeOffset you can use the
190+
/// convenience function <see cref="TimestampExtensions.AsDateTimeOffset(ulong)">AsDateTimeOffset</see>.
191+
/// Just be aware that DateTimeOffset only handles 100 nanosecond resolution.
168192
/// </summary>
169-
public DateTimeOffset? Timestamp
193+
public ulong Timestamp
170194
{
171195
get {
172196
using var scope = ThreadSafety.BeginLockedScope();
173-
var rawVal = c4Doc?.HasValue == true ? NativeRaw.c4rev_getTimestamp(c4Doc.RawDoc->selectedRev.revID) : 0;
174-
if(rawVal == 0) {
175-
return null;
176-
}
177-
178-
// .NET ticks are in 100 nanosecond intervals
179-
rawVal /= 100;
180-
181-
if(rawVal > Int64.MaxValue) {
182-
throw new OverflowException("The returned value from LiteCore is too large to be represented by DateTimeOffset");
183-
}
184-
185-
return UnixEpoch + TimeSpan.FromTicks((long)rawVal);
197+
return c4Doc?.HasValue == true ? NativeRaw.c4rev_getTimestamp(c4Doc.RawDoc->selectedRev.revID) : 0;
186198
}
187199
}
188200

src/Couchbase.Lite.Tests.Shared/VersionVectorTests.cs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -48,16 +48,16 @@ public VersionVectorTest(ITestOutputHelper output) : base(output)
4848
/// 5. Get the document id = "doc1" from the database.
4949
/// 6. Get document's timestamp and check that the timestamp is the same as the timestamp from step 4.
5050
/// </summary>
51-
[Fact(Skip = "Version vectors not turned on yet")]
51+
[Fact]
5252
public void TestDocumentTimestamp()
5353
{
5454
using var doc = new MutableDocument("doc1");
55-
doc.Timestamp.ShouldBeNull("because the doc has not been saved yet");
55+
doc.Timestamp.ShouldBe(0UL, "because the doc has not been saved yet");
5656
DefaultCollection.Save(doc);
57-
doc.Timestamp.ShouldNotBeNull("because the doc is now saved");
57+
doc.Timestamp.ShouldNotBe(0UL, "because the doc is now saved");
5858
using var savedDoc = DefaultCollection.GetDocument("doc1");
5959
savedDoc.ShouldNotBeNull("because the document was just saved");
60-
savedDoc!.Timestamp.ShouldBe(doc.Timestamp, "because the timestamp should not change just from a read");
60+
savedDoc.Timestamp.ShouldBe(doc.Timestamp, "because the timestamp should not change just from a read");
6161
}
6262

6363
/// <summary>
@@ -72,7 +72,7 @@ public void TestDocumentTimestamp()
7272
/// 5. Get the document id = "doc1" from the database.
7373
/// 6. Get document's _revisionIDs and check that the value returned is not null
7474
/// </summary>
75-
[Fact(Skip = "Version vectors not turned on yet")]
75+
[Fact]
7676
public void TestDocumentRevisionHistory()
7777
{
7878
using var doc = new MutableDocument("doc1");
@@ -82,7 +82,7 @@ public void TestDocumentRevisionHistory()
8282
using var savedDoc = DefaultCollection.GetDocument("doc1");
8383
savedDoc.ShouldNotBeNull("because the document was just saved");
8484

85-
savedDoc!.RevisionIDs().ShouldNotBeNull("because the saved document should contain at least one revision ID");
85+
savedDoc.RevisionIDs().ShouldNotBeNull("because the saved document should contain at least one revision ID");
8686
}
8787

8888
public enum DefaultConflictLWWMode
@@ -110,7 +110,7 @@ public enum DefaultConflictLWWMode
110110
/// 6.Start a single shot pull replicator to pull documents from "db2" to "db1".
111111
/// 7. Get the document "doc2" from "db1" and check that the content is {"key": "value2"}.
112112
/// </summary>
113-
[Theory(Skip = "Version vectors not turned on yet")]
113+
[Theory]
114114
[InlineData(DefaultConflictLWWMode.SaveDB2First)]
115115
[InlineData(DefaultConflictLWWMode.SaveDB1First)]
116116
public void TestDefaultConflictResolver(DefaultConflictLWWMode lwwMode)
@@ -148,7 +148,7 @@ public void TestDefaultConflictResolver(DefaultConflictLWWMode lwwMode)
148148

149149
using var doc = db1.GetDefaultCollection().GetDocument("doc1");
150150
doc.ShouldNotBeNull("because it was just saved");
151-
doc!["key"].Value.ShouldBe(expectedValue, "because otherwise the conflict resolver behaved unexpectedly");
151+
doc["key"].Value.ShouldBe(expectedValue, "because otherwise the conflict resolver behaved unexpectedly");
152152
}
153153

154154
/// <summary>
@@ -168,7 +168,7 @@ public void TestDefaultConflictResolver(DefaultConflictLWWMode lwwMode)
168168
/// 4.Start a single shot pull replicator to pull documents from "db2" to "db1".
169169
/// 5. Get the document "doc1" from "db1" and check that the returned document is null.
170170
/// </summary>
171-
[Fact(Skip = "Version vectors not turned on yet")]
171+
[Fact]
172172
public void TestDefaultConflictResolverDeleteWins()
173173
{
174174
Database.Delete("db1", null);

0 commit comments

Comments
 (0)