Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable Version Vectors #3329

Merged
merged 5 commits into from
Nov 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ pipeline {
options {
disableConcurrentBuilds()
}
agent { label 'mobile-builder-ios-pull-request' }
agent { label 'sonoma' }
stages {
stage('Cleanup'){
steps {
Expand Down
6 changes: 1 addition & 5 deletions Objective-C/CBLConflictResolver.m
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,7 @@ + (CBLDefaultConflictResolver*) shared {
- (nullable CBLDocument*) resolve: (CBLConflict*)conflict {
if (conflict.remoteDocument == nil || conflict.localDocument == nil)
return nil;
else if (conflict.localDocument.generation > conflict.remoteDocument.generation)
return conflict.localDocument;
else if (conflict.localDocument.generation < conflict.remoteDocument.generation)
return conflict.remoteDocument;
else if ([conflict.localDocument.revisionID compare: conflict.remoteDocument.revisionID] > 0)
else if (conflict.localDocument.timestamp > conflict.remoteDocument.timestamp)
return conflict.localDocument;
else
return conflict.remoteDocument;
Expand Down
2 changes: 1 addition & 1 deletion Objective-C/CBLDatabase.mm
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ @implementation CBLDatabase {
@synthesize c4db=_c4db, sharedKeys=_sharedKeys;

static const C4DatabaseConfig2 kDBConfig = {
.flags = (kC4DB_Create | kC4DB_AutoCompact),
.flags = (kC4DB_Create | kC4DB_AutoCompact | kC4DB_VersionVectors),
};

/**
Expand Down
5 changes: 4 additions & 1 deletion Objective-C/CBLDocument.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ NS_ASSUME_NONNULL_BEGIN
/** The ID representing a document’s revision. */
@property (readonly, nonatomic, nullable) NSString* revisionID;

/** The hybrid logical timestamp that the revision was created. */
@property (readonly, nonatomic) NSTimeInterval timestamp;

/**
Sequence number of the document in the database.
This indicates how recently the document has been changed: every time any document is updated,
Expand All @@ -58,7 +61,7 @@ NS_ASSUME_NONNULL_BEGIN
/** Return document data as JSON String. */
- (NSString*) toJSON;

/** <Unsupported API> Internally used for testing purpose. */
/** <Unsupported API> Internal used for testing purpose. */
- (nullable NSString*) _getRevisionHistory;

@end
Expand Down
5 changes: 2 additions & 3 deletions Objective-C/CBLDocument.mm
Original file line number Diff line number Diff line change
Expand Up @@ -294,10 +294,9 @@ - (NSString*) revisionID {
}
}

- (NSUInteger) generation {
// CBLMutableDocument overrides this
- (NSTimeInterval) timestamp {
CBL_LOCK(self) {
return _c4Doc != nil ? c4rev_getGeneration(_c4Doc.revID) : 0;
return _c4Doc != nil ? c4rev_getTimestamp(_c4Doc.revID) / 1000000000.0 : 0;
}
}

Expand Down
7 changes: 0 additions & 7 deletions Objective-C/CBLMutableDocument.mm
Original file line number Diff line number Diff line change
Expand Up @@ -195,13 +195,6 @@ - (bool) isMutable {
return true;
}

// TODO: This value is incorrect after the document is saved as self.changed
// doesn't get reset. However this is currently being used during replication's
// conflict resolution so the generation value is correct in that circumstance.
- (NSUInteger) generation {
return super.generation + !!self.changed;
}

- (NSString*) generateID {
char docID[kC4GeneratedIDLength + 1];
c4doc_generateID(docID, sizeof(docID));
Expand Down
2 changes: 0 additions & 2 deletions Objective-C/Internal/CBLDocument+Internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,6 @@ NS_ASSUME_NONNULL_BEGIN

@property (nonatomic, readonly) BOOL isEmpty;

@property (nonatomic, readonly) NSUInteger generation;

@property (readonly, nonatomic) BOOL isDeleted;

@property (nonatomic, readonly, nullable) FLDict fleeceData;
Expand Down
55 changes: 19 additions & 36 deletions Objective-C/Tests/DatabaseTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -599,69 +599,63 @@ - (void) testSavePurgedDoc {
#pragma mark Save Conflict Handler

- (void) testConflictHandler {
NSError* error;
NSString* docID = @"doc1";
CBLMutableDocument* doc = [[CBLMutableDocument alloc] initWithID: docID];
[doc setString: @"Tiger" forKey: @"firstName"];
[self saveDocument: doc];
AssertEqual([self.db documentWithID: docID].generation, 1u);

CBLMutableDocument* doc1a = [[self.db documentWithID: docID] toMutable];
CBLMutableDocument* doc1b = [[self.db documentWithID: docID] toMutable];

[doc1a setString: @"Scotty" forKey: @"nickName"];
[self saveDocument: doc1a];
AssertEqual([self.db documentWithID: docID].generation, 2u);

NSError* error;
[doc1b setString: @"Scott" forKey: @"nickName"];
Assert([self.db saveDocument: doc1b
conflictHandler:^BOOL(CBLMutableDocument * document, CBLDocument * old) {
Assert(doc1b == document);
AssertEqualObjects(doc1b.toDictionary, document.toDictionary);
AssertEqualObjects(doc1a.toDictionary, old.toDictionary);
AssertEqual(document.generation, 2u);
AssertEqual(old.generation, 2u);
return YES;
} error: &error]);
conflictHandler: ^BOOL(CBLMutableDocument * document, CBLDocument * old) {
Assert(doc1b == document);
AssertEqualObjects(doc1b.toDictionary, document.toDictionary);
AssertEqualObjects(doc1a.toDictionary, old.toDictionary);
return YES;
}
error: &error]);

AssertEqualObjects([self.db documentWithID: docID].toDictionary, doc1b.toDictionary);
AssertEqual([self.db documentWithID: docID].generation, 3u);

doc1a = [[self.db documentWithID: docID] toMutable];
doc1b = [[self.db documentWithID: docID] toMutable];

[doc1a setString: @"Sccotty" forKey: @"nickName"];
[self saveDocument: doc1a];
AssertEqual([self.db documentWithID: docID].generation, 4u);

[doc1b setString: @"Scotty" forKey: @"nickName"];
Assert([self.db saveDocument: doc1b
conflictHandler:^BOOL(CBLMutableDocument * document, CBLDocument * old) {
Assert(doc1b == document);
AssertEqualObjects(doc1b.toDictionary, document.toDictionary);
AssertEqualObjects(doc1a.toDictionary, old.toDictionary);
AssertEqual(document.generation, 4u);
AssertEqual(old.generation, 4u);
[document setString: @"Scott" forKey: @"nickName"];
return YES;
} error: &error]);
conflictHandler: ^BOOL(CBLMutableDocument * document, CBLDocument * old) {
Assert(doc1b == document);
AssertEqualObjects(doc1b.toDictionary, document.toDictionary);
AssertEqualObjects(doc1a.toDictionary, old.toDictionary);
[document setString: @"Scott" forKey: @"nickName"];
return YES;
}
error: &error]);

NSDictionary* expected = @{@"nickName": @"Scott", @"firstName": @"Tiger"};
AssertEqualObjects([self.db documentWithID: docID].toDictionary, expected);
AssertEqual([self.db documentWithID: docID].generation, 5u);
}


- (void) testCancelConflictHandler {
NSString* docID = @"doc1";
CBLMutableDocument* doc = [[CBLMutableDocument alloc] initWithID: docID];
[doc setString: @"Tiger" forKey: @"firstName"];
[self saveDocument: doc];
AssertEqual([self.db documentWithID: docID].generation, 1u);

CBLMutableDocument* doc1a = [[self.db documentWithID: docID] toMutable];
CBLMutableDocument* doc1b = [[self.db documentWithID: docID] toMutable];

[doc1a setString: @"Scotty" forKey: @"nickName"];
[self saveDocument: doc1a];
AssertEqual([self.db documentWithID: docID].generation, 2u);

NSError* error;
[doc1b setString: @"Scott" forKey: @"nickName"];
Expand All @@ -676,15 +670,13 @@ - (void) testCancelConflictHandler {

// make sure no update to revision and generation
AssertEqualObjects([self.db documentWithID: docID].revisionID, doc1a.revisionID);
AssertEqual([self.db documentWithID: docID].generation, 2u);

// Some Updates to Current Mutable Document
doc1a = [[self.db documentWithID: docID] toMutable];
doc1b = [[self.db documentWithID: docID] toMutable];

[doc1a setString: @"Sccotty" forKey: @"nickName"];
[self saveDocument: doc1a];
AssertEqual([self.db documentWithID: docID].generation, 3u);

[doc1b setString: @"Scotty" forKey: @"nickName"];
AssertFalse([self.db saveDocument: doc1b
Expand All @@ -697,7 +689,6 @@ - (void) testCancelConflictHandler {
AssertEqualObjects([self.db documentWithID: docID].toDictionary, doc1a.toDictionary);

// make sure no update to revision and generation
AssertEqual([self.db documentWithID: docID].generation, 3u);
AssertEqualObjects([self.db documentWithID: docID].revisionID, doc1a.revisionID);
}

Expand All @@ -706,7 +697,6 @@ - (void) testConflictHandlerWhenDocumentIsPurged {
CBLMutableDocument* doc = [[CBLMutableDocument alloc] initWithID: docID];
[doc setString: @"Tiger" forKey: @"firstName"];
[self saveDocument: doc];
AssertEqual([self.db documentWithID: docID].generation, 1u);

CBLMutableDocument* doc1b = [[self.db documentWithID: docID] toMutable];

Expand All @@ -733,14 +723,12 @@ - (void) _testConflictHandlerThrowingException {
CBLMutableDocument* doc = [[CBLMutableDocument alloc] initWithID: docID];
[doc setString: @"Tiger" forKey: @"firstName"];
[self saveDocument: doc];
AssertEqual([self.db documentWithID: docID].generation, 1u);

CBLMutableDocument* doc1a = [[self.db documentWithID: docID] toMutable];
CBLMutableDocument* doc1b = [[self.db documentWithID: docID] toMutable];

[doc1a setString: @"Scotty" forKey: @"nickName"];
[self saveDocument: doc1a];
AssertEqual([self.db documentWithID: docID].generation, 2u);

NSError* error;
[doc1b setString: @"Scott" forKey: @"nickName"];
Expand All @@ -752,14 +740,12 @@ - (void) _testConflictHandlerThrowingException {
} error: &error];
AssertFalse(success);
AssertEqualObjects([self.db documentWithID: docID].toDictionary, doc1a.toDictionary);
AssertEqual([self.db documentWithID: docID].generation, 2u);
AssertEqual(error.code, CBLErrorConflict);
}

- (void) testConflictHandlerWithDeletedOldDoc {
NSString* docID = @"doc1";
[self generateDocumentWithID: docID];
AssertEqual([self.db documentWithID: docID].generation, 1u);

// keeps new doc(non-deleted)
CBLMutableDocument* doc1a = [[self.db documentWithID: docID] toMutable];
Expand Down Expand Up @@ -803,15 +789,13 @@ - (void) testConflictHandlerCalledTwice {
CBLMutableDocument* doc1 = [[CBLMutableDocument alloc] initWithID: docID];
[doc1 setString: @"Tiger" forKey: @"name"];
[self saveDocument: doc1];
AssertEqual([self.db documentWithID: docID].generation, 1u);

CBLMutableDocument* doc1a = [[self.db documentWithID: docID] toMutable];
CBLMutableDocument* doc1b = [[self.db documentWithID: docID] toMutable];

// Save doc1a:
[doc1a setString: @"Cat" forKey: @"name"];
[self saveDocument: doc1a];
AssertEqual([self.db documentWithID: docID].generation, 2u);

// Save doc1b:
NSError* error;
Expand Down Expand Up @@ -847,7 +831,6 @@ - (void) testConflictHandlerCalledTwice {

NSDictionary* expected = @{@"type": @"Animal", @"name": @"Mountain Lion", @"count": @2};
AssertEqualObjects([self.db documentWithID: docID].toDictionary, expected);
AssertEqual([self.db documentWithID: docID].generation, 4u);
}

#pragma mark - Delete Document
Expand Down
32 changes: 30 additions & 2 deletions Objective-C/Tests/DocumentTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -2262,10 +2262,38 @@ - (void) testDocumentResaveInAnotherCollection {
}];
}

#pragma mark - Revision history
#pragma mark - Timestamp & Revision history

/** https://github.com/couchbaselabs/couchbase-lite-api/blob/master/spec/tests/T0005-Version-Vector.md */


/**
1. TestDocumentTimestamp
Description
Test that the document's timestamp returns value as expected.
Steps
1. Create a new document with id = "doc1"
2. Get document's timestamp and check that the timestamp is 0.
3. Save the document into the default collection.
4. Get document's timestamp and check that the timestamp is more than 0.
5. Get the document id = "doc1" from the database.
6. Get document's timestamp and check that the timestamp is the same as the timestamp from step 4.
*/
- (void) testDocumentTimestamp {
NSError* err;
CBLCollection* defaultCollection = [self.db defaultCollection: &err];
AssertNil(err);

CBLMutableDocument* doc = [[CBLMutableDocument alloc] initWithID: @"doc1"];
Assert(doc);
AssertEqual(doc.timestamp, 0);

Assert([defaultCollection saveDocument:doc error: &err]);
NSTimeInterval timestamp = doc.timestamp;
Assert(timestamp > 0);

doc = [[defaultCollection documentWithID: @"doc1" error: &err] toMutable];
AssertEqual(doc.timestamp, timestamp);
}
/**
2. TestDocumentRevisionHistory
Description
Expand Down
13 changes: 7 additions & 6 deletions Objective-C/Tests/ReplicatorTest+CustomConflict.m
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,10 @@ - (void) testConflictResolverNullDoc {
error: &error].sequence);
}

/** https://github.com/couchbaselabs/couchbase-lite-api/blob/master/spec/tests/T0005-Version-Vector.md
Test 4. DefaultConflictResolverDeleteWins -> testConflictResolverDeletedLocalWins + testConflictResolverDeletedRemoteWins
*/

- (void) testConflictResolverDeletedLocalWins {
NSString* docId = @"doc";
NSDictionary* remoteData = @{@"key2": @"value2"};
Expand Down Expand Up @@ -612,32 +616,30 @@ - (void) testNonBlockingDatabaseOperationConflictResolver {
AssertEqual(count, 1u);
}

/** https://github.com/couchbaselabs/couchbase-lite-api/blob/master/spec/tests/T0005-Version-Vector.md
Test 3. DefaultConflictResolverLastWriteWins -> default resolver
*/
- (void) testConflictResolutionDefault {
NSError* error;
NSDictionary* localData = @{@"name": @"local"};
NSDictionary* remoteData = @{@"name": @"remote"};
NSMutableArray* conflictedDocs = [NSMutableArray array];

// Higher generation-id
NSString* docID = @"doc1";
[self makeConflictFor: docID withLocal: localData withRemote: remoteData];
CBLMutableDocument* doc = [[self.db documentWithID: docID] toMutable];
[doc setValue: @"value1" forKey: @"key1"];
[self saveDocument: doc];
[conflictedDocs addObject: @[[self.db documentWithID: docID],
[self.otherDB documentWithID: docID]]];

// Delete local
docID = @"doc2";
[self makeConflictFor: docID withLocal: localData withRemote: remoteData];
[self.db deleteDocument: [self.db documentWithID: docID] error: &error];
[conflictedDocs addObject: @[[NSNull null], [self.otherDB documentWithID: docID]]];

// Delete remote
docID = @"doc3";
[self makeConflictFor: docID withLocal: localData withRemote: remoteData];
[self.otherDB deleteDocument: [self.otherDB documentWithID: docID] error: &error];
[conflictedDocs addObject: @[[self.db documentWithID: docID], [NSNull null]]];

// Delete local but higher remote generation.
docID = @"doc4";
Expand All @@ -648,7 +650,6 @@ - (void) testConflictResolutionDefault {
[self.otherDB saveDocument: doc error: &error];
[doc setValue: @"value4" forKey: @"key4"];
[self.otherDB saveDocument: doc error: &error];
[conflictedDocs addObject: @[[NSNull null], [self.otherDB documentWithID: docID]]];

CBLReplicatorConfiguration* pullConfig = [self config:kCBLReplicatorTypePull];
[self run: pullConfig errorCode: 0 errorDomain: nil];
Expand Down
6 changes: 3 additions & 3 deletions Objective-C/Tests/ReplicatorTest+Main.m
Original file line number Diff line number Diff line change
Expand Up @@ -2011,10 +2011,10 @@ - (void) testCreateDocumentReplicator {

- (void) testReplicatedDocument {
C4DocumentEnded end;
end.docID = c4str("docID");
end.revID = c4str("revID");
end.docID = C4STR("docID");
end.revID = C4STR("revID");
end.flags = kRevDeleted;
end.error = c4error_make(1, kC4ErrorBusy, c4str("error"));
end.error = c4error_make(1, kC4ErrorBusy, C4STR("error"));
end.errorIsTransient = true;
end.collectionSpec = kC4DefaultCollectionSpec;

Expand Down
2 changes: 1 addition & 1 deletion Objective-C/Tests/UnnestArrayIndexTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ - (void) testArrayIndexConfigInvalidExpressions {
5. Get info of the index named "contacts" using an internal API and check that the index has path and expressions as configured.
*/

- (void) testCreateArrayIndexWithPath {
- (void) _testCreateArrayIndexWithPath {
NSError* err;
CBLCollection* profiles = [self.db createCollectionWithName: @"profiles" scope: nil error: &err];
[self loadJSONResource: @"profiles_100" toCollection: profiles];
Expand Down
10 changes: 4 additions & 6 deletions Objective-C/Tests/VectorSearchTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -1187,12 +1187,10 @@ - (void) testVectorMatchLimitBoundary {
}

// Check if error thrown for wrong limit values
for (NSNumber* limit in @[@-1, @0, @10001]) {
[self expectError: CBLErrorDomain code: CBLErrorInvalidQuery in: ^BOOL(NSError** err) {
NSString* sql = [self wordsQueryStringWithLimit: [limit unsignedIntegerValue]];
return [self.wordDB createQuery: sql error: err] != nil;
}];
}
[self expectError: CBLErrorDomain code: CBLErrorInvalidQuery in: ^BOOL(NSError** err) {
NSString* sql = [self wordsQueryStringWithLimit: 10001];
return [self.wordDB createQuery: sql error: err] != nil;
}];
}

/**
Expand Down
Loading
Loading