diff --git a/Objective-C/CBLDatabase.mm b/Objective-C/CBLDatabase.mm index 8cfc9b42f..a0a97040f 100644 --- a/Objective-C/CBLDatabase.mm +++ b/Objective-C/CBLDatabase.mm @@ -1024,6 +1024,8 @@ static BOOL setupDatabaseDirectory(NSString *dir, NSError **outError) static C4DatabaseConfig2 c4DatabaseConfig2 (CBLDatabaseConfiguration *config) { C4DatabaseConfig2 c4config = kDBConfig; + if (config.fullSync) + c4config.flags |= kC4DB_DiskSyncFull; #ifdef COUCHBASE_ENTERPRISE if (config.encryptionKey) c4config.encryptionKey = [CBLDatabase c4EncryptionKey: config.encryptionKey]; @@ -1139,4 +1141,10 @@ - (uint64_t) activeStoppableCount { } } +#pragma mark - Private for test + +- (const C4DatabaseConfig2*) getC4DBConfig { + return c4db_getConfig2(_c4db); +} + @end diff --git a/Objective-C/CBLDatabaseConfiguration.h b/Objective-C/CBLDatabaseConfiguration.h index 90cc82db1..b1b25804e 100644 --- a/Objective-C/CBLDatabaseConfiguration.h +++ b/Objective-C/CBLDatabaseConfiguration.h @@ -30,6 +30,19 @@ NS_ASSUME_NONNULL_BEGIN */ @property (nonatomic, copy) NSString* directory; +/** + As Couchbase Lite normally configures its databases, there is a very + small (though non-zero) chance that a power failure at just the wrong + time could cause the most recently committed transaction's changes to + be lost. This would cause the database to appear as it did immediately + before that transaction. + + Setting this mode true ensures that an operating system crash or + power failure will not cause the loss of any data. FULL synchronous + is very safe but it is also dramatically slower. + */ +@property (nonatomic) BOOL fullSync; + /** Initializes the CBLDatabaseConfiguration object. */ diff --git a/Objective-C/CBLDatabaseConfiguration.m b/Objective-C/CBLDatabaseConfiguration.m index aace931fa..9f0f44d5c 100644 --- a/Objective-C/CBLDatabaseConfiguration.m +++ b/Objective-C/CBLDatabaseConfiguration.m @@ -19,12 +19,13 @@ #import "CBLDatabaseConfiguration.h" #import "CBLDatabase+Internal.h" +#import "CBLDefaults.h" @implementation CBLDatabaseConfiguration { BOOL _readonly; } -@synthesize directory=_directory; +@synthesize directory=_directory, fullSync=_fullSync; #ifdef COUCHBASE_ENTERPRISE @synthesize encryptionKey=_encryptionKey; @@ -47,11 +48,14 @@ - (instancetype) initWithConfig: (nullable CBLDatabaseConfiguration*)config if (config) { _directory = config.directory; + _fullSync = config.fullSync; #ifdef COUCHBASE_ENTERPRISE _encryptionKey = config.encryptionKey; #endif - } else + } else { _directory = [CBLDatabaseConfiguration defaultDirectory]; + _fullSync = kCBLDefaultDatabaseFullSync; + } } return self; } diff --git a/Objective-C/CBLDefaults.h b/Objective-C/CBLDefaults.h index 0536aa1d7..eac560e00 100644 --- a/Objective-C/CBLDefaults.h +++ b/Objective-C/CBLDefaults.h @@ -28,6 +28,11 @@ #endif +#pragma mark - CBLDatabaseConfiguration + +/** [NO] Full sync is off by default because the performance hit is seldom worth the benefit */ +extern const BOOL kCBLDefaultDatabaseFullSync; + #pragma mark - CBLLogFileConfiguration /** [NO] Plaintext is not used, and instead binary encoding is used in log files */ diff --git a/Objective-C/CBLDefaults.m b/Objective-C/CBLDefaults.m index 36140a8cf..5fc203610 100644 --- a/Objective-C/CBLDefaults.m +++ b/Objective-C/CBLDefaults.m @@ -22,6 +22,10 @@ #import "CBLDefaults.h" +#pragma mark - CBLDatabaseConfiguration + +const BOOL kCBLDefaultDatabaseFullSync = NO; + #pragma mark - CBLLogFileConfiguration const BOOL kCBLDefaultLogFileUsePlaintext = NO; diff --git a/Objective-C/Exports/CBL.txt b/Objective-C/Exports/CBL.txt index ad423663c..ef152e06f 100644 --- a/Objective-C/Exports/CBL.txt +++ b/Objective-C/Exports/CBL.txt @@ -91,6 +91,7 @@ _kCBLBlobContentTypeProperty _kCBLBlobDigestProperty _kCBLBlobLengthProperty _kCBLBlobType +_kCBLDefaultDatabaseFullSync _kCBLDefaultCollectionName _kCBLDefaultFullTextIndexIgnoreAccents _kCBLDefaultLogFileMaxRotateCount diff --git a/Objective-C/Exports/Generated/CBL.exp b/Objective-C/Exports/Generated/CBL.exp index 9062cefab..36c0f6db2 100644 --- a/Objective-C/Exports/Generated/CBL.exp +++ b/Objective-C/Exports/Generated/CBL.exp @@ -71,6 +71,7 @@ _kCBLBlobDigestProperty _kCBLBlobLengthProperty _kCBLBlobType _kCBLDefaultCollectionName +_kCBLDefaultDatabaseFullSync _kCBLDefaultFullTextIndexIgnoreAccents _kCBLDefaultLogFileMaxRotateCount _kCBLDefaultLogFileMaxSize diff --git a/Objective-C/Exports/Generated/CBL_EE.exp b/Objective-C/Exports/Generated/CBL_EE.exp index 5084aa65e..dc7d1a526 100644 --- a/Objective-C/Exports/Generated/CBL_EE.exp +++ b/Objective-C/Exports/Generated/CBL_EE.exp @@ -109,6 +109,7 @@ _kCBLCertAttrStateOrProvince _kCBLCertAttrSurname _kCBLCertAttrURL _kCBLDefaultCollectionName +_kCBLDefaultDatabaseFullSync _kCBLDefaultFullTextIndexIgnoreAccents _kCBLDefaultListenerDisableTls _kCBLDefaultListenerEnableDeltaSync diff --git a/Objective-C/Internal/CBLDatabase+Internal.h b/Objective-C/Internal/CBLDatabase+Internal.h index 394d8facb..2ed985d2b 100644 --- a/Objective-C/Internal/CBLDatabase+Internal.h +++ b/Objective-C/Internal/CBLDatabase+Internal.h @@ -46,7 +46,6 @@ NS_ASSUME_NONNULL_BEGIN /// CBLDatabase: - @interface CBLDatabase () @property (readonly, nonatomic, nullable) C4Database* c4db; @@ -80,6 +79,10 @@ NS_ASSUME_NONNULL_BEGIN - (id) mutex; +#pragma mark - Private for test + +- (const C4DatabaseConfig2*) getC4DBConfig; + @end /// CBLDatabaseConfiguration: diff --git a/Objective-C/Tests/CollectionTest.m b/Objective-C/Tests/CollectionTest.m index 01723379e..7ec875d14 100644 --- a/Objective-C/Tests/CollectionTest.m +++ b/Objective-C/Tests/CollectionTest.m @@ -952,7 +952,7 @@ - (void) testUseInvalidCollection: (NSString*)collectionName onAction: (void (^) // get index, get indexes, delete index [self expectError: CBLErrorDomain code: CBLErrorNotOpen in: ^BOOL(NSError** err) { - return [col indexWithName: @"index1" error: err]; + return [col indexWithName: @"index1" error: err] != nil; }]; [self expectError: CBLErrorDomain code: CBLErrorNotOpen in: ^BOOL(NSError** err) { return [col indexes: err] != nil; diff --git a/Objective-C/Tests/DatabaseTest.m b/Objective-C/Tests/DatabaseTest.m index 70d715421..3de019525 100644 --- a/Objective-C/Tests/DatabaseTest.m +++ b/Objective-C/Tests/DatabaseTest.m @@ -2818,6 +2818,79 @@ - (void) testDBEventTrigged { [token remove]; } +#pragma mark - Full Sync Option + +/** + Test Spec for Database Full Sync Option https://github.com/couchbaselabs/couchbase-lite-api/blob/master/spec/tests/T0003-SQLite-Options.md + */ + +/** + 1. TestSQLiteFullSyncConfig + Description + Test that the FullSync default is as expected and that it's setter and getter work. + Steps + 1. Create a DatabaseConfiguration object. + 2. Get and check the value of the FullSync property: it should be false. + 3. Set the FullSync property true. + 4. Get the config FullSync property and verify that it is true. + 5. Set the FullSync property false. + 6. Get the config FullSync property and verify that it is false. + */ +- (void) testSQLiteFullSyncConfig { + CBLDatabaseConfiguration* config = [[CBLDatabaseConfiguration alloc] init]; + AssertFalse(config.fullSync); + + config.fullSync = true; + Assert(config.fullSync); + + config.fullSync = false; + AssertFalse(config.fullSync); +} + +/** + 2. TestDBWithFullSync + Description + Test that a Database respects the FullSync property. + Steps + 1. Create a DatabaseConfiguration object and set Full Sync false. + 2. Create a database with the config. + 3. Get the configuration object from the Database and verify that FullSync is false. + 4. Use c4db_config2 (perhaps necessary only for this test) to confirm that its config does not contain the kC4DB_DiskSyncFull flag. + 5. Set the config's FullSync property true. + 6. Create a database with the config. + 7. Get the configuration object from the Database and verify that FullSync is true. + 8. Use c4db_config2 to confirm that its config contains the kC4DB_DiskSyncFull flag. + */ +- (void) testDBWithFullSync { + NSString* dbName = @"fullsyncdb"; + [CBLDatabase deleteDatabase: dbName inDirectory: self.directory error: nil]; + AssertFalse([CBLDatabase databaseExists: dbName inDirectory: self.directory]); + + CBLDatabaseConfiguration* config = [[CBLDatabaseConfiguration alloc] init]; + config.directory = self.directory; + NSError* error; + CBLDatabase* db = [[CBLDatabase alloc] initWithName: dbName + config: config + error: &error]; + AssertNil(error); + AssertNotNil(db, @"Couldn't open db: %@", error); + AssertFalse([db config].fullSync); + AssertFalse(([db getC4DBConfig]->flags & kC4DB_DiskSyncFull) == kC4DB_DiskSyncFull); + + [self closeDatabase: db]; + + config.fullSync = true; + db = [[CBLDatabase alloc] initWithName: dbName + config: config + error: &error]; + AssertNil(error); + AssertNotNil(db, @"Couldn't open db: %@", error); + Assert([db config].fullSync); + Assert(([db getC4DBConfig]->flags & kC4DB_DiskSyncFull) == kC4DB_DiskSyncFull); + + [self closeDatabase: db]; +} + #pragma clang diagnostic pop @end diff --git a/Swift/DatabaseConfiguration.swift b/Swift/DatabaseConfiguration.swift index c7a89421f..fb25a5e64 100644 --- a/Swift/DatabaseConfiguration.swift +++ b/Swift/DatabaseConfiguration.swift @@ -2,7 +2,7 @@ // DatabaseConfiguration.swift // CouchbaseLite // -// Copyright (c) 2017 Couchbase, Inc All rights reserved. +// Copyright (c) 2024 Couchbase, Inc All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -27,6 +27,17 @@ public struct DatabaseConfiguration { /// Path to the directory to store the database in. public var directory: String = CBLDatabaseConfiguration().directory + /// As Couchbase Lite normally configures its databases, there is a very + /// small (though non-zero) chance that a power failure at just the wrong + /// time could cause the most recently committed transaction's changes to + /// be lost. This would cause the database to appear as it did immediately + /// before that transaction. + /// + /// Setting this mode true ensures that an operating system crash or + /// power failure will not cause the loss of any data. FULL synchronous + /// is very safe but it is also dramatically slower. + public var fullSync: Bool = defaultFullSync + #if COUCHBASE_ENTERPRISE /// The key to encrypt the database with. public var encryptionKey: EncryptionKey? @@ -41,6 +52,7 @@ public struct DatabaseConfiguration { public init(config: DatabaseConfiguration?) { if let c = config { self.directory = c.directory + self.fullSync = c.fullSync #if COUCHBASE_ENTERPRISE self.encryptionKey = c.encryptionKey #endif @@ -52,6 +64,7 @@ public struct DatabaseConfiguration { func toImpl() -> CBLDatabaseConfiguration { let config = CBLDatabaseConfiguration() config.directory = self.directory + config.fullSync = self.fullSync #if COUCHBASE_ENTERPRISE config.encryptionKey = self.encryptionKey?.impl #endif diff --git a/Swift/Defaults.swift b/Swift/Defaults.swift index 75584f8ba..28e245fbd 100644 --- a/Swift/Defaults.swift +++ b/Swift/Defaults.swift @@ -22,6 +22,13 @@ import Foundation +public extension DatabaseConfiguration { + + /// [false] Full sync is off by default because the performance hit is seldom worth the benefit + static let defaultFullSync: Bool = false + +} + public extension LogFileConfiguration { /// [false] Plaintext is not used, and instead binary encoding is used in log files diff --git a/Swift/Tests/DatabaseTest.swift b/Swift/Tests/DatabaseTest.swift index cdc1c55b9..f8e5fa1a9 100644 --- a/Swift/Tests/DatabaseTest.swift +++ b/Swift/Tests/DatabaseTest.swift @@ -2,7 +2,7 @@ // DatabaseTest.swift // CouchbaseLite // -// Copyright (c) 2017 Couchbase, Inc All rights reserved. +// Copyright (c) 2024 Couchbase, Inc All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -1529,4 +1529,57 @@ class DatabaseTest: CBLTestCase { XCTAssertNotNil(db.config.encryptionKey) #endif } + + // MARK: Full Sync Option + /// Test Spec for Database Full Sync Option + /// https://github.com/couchbaselabs/couchbase-lite-api/blob/master/spec/tests/T0003-SQLite-Options.md + + /// 1. TestSQLiteFullSyncConfig + /// Description: + /// Test that the FullSync default is as expected and that it's setter and getter work. + /// Steps + /// 1. Create a DatabaseConfiguration object. + /// 2. Get and check the value of the FullSync property: it should be false. + /// 3. Set the FullSync property true. + /// 4. Get the config FullSync property and verify that it is true. + /// 5. Set the FullSync property false. + /// 6. Get the config FullSync property and verify that it is false. + func testSQLiteFullSyncConfig() { + var config = DatabaseConfiguration() + XCTAssertFalse(config.fullSync) + + config.fullSync = true + XCTAssert(config.fullSync) + + config.fullSync = false + XCTAssertFalse(config.fullSync) + } + + /// 2. TestDBWithFullSync + /// Description: + /// Test that the FullSync default is as expected and that it's setter and getter work. + /// Steps + /// 1. Create a DatabaseConfiguration object and set Full Sync false. + /// 2. Create a database with the config. + /// 3. Get the configuration object from the Database and verify that FullSync is false. + /// 4. Use c4db_config2 (perhaps necessary only for this test) to confirm that its config does not contain the kC4DB_DiskSyncFull flag. - done in Obj-C + /// 5. Set the config's FullSync property true. + /// 6. Create a database with the config. + /// 7. Get the configuration object from the Database and verify that FullSync is true. + /// 8. Use c4db_config2 to confirm that its config contains the kC4DB_DiskSyncFull flag. - done in Obj-C + func testDBWithFullSync() throws { + let dbName = "fullsyncdb" + try deleteDB(name: dbName) + XCTAssertFalse(Database.exists(withName: dbName, inDirectory: self.directory)) + + var config = DatabaseConfiguration() + config.directory = self.directory + db = try Database(name: dbName, config: config) + XCTAssertFalse(DatabaseConfiguration(config: config).fullSync) + + db = nil + config.fullSync = true + db = try Database(name: dbName, config: config) + XCTAssert(DatabaseConfiguration(config: config).fullSync) + } } diff --git a/vendor/couchbase-lite-core b/vendor/couchbase-lite-core index 7f0707145..0b30e0c60 160000 --- a/vendor/couchbase-lite-core +++ b/vendor/couchbase-lite-core @@ -1 +1 @@ -Subproject commit 7f0707145d9db2af04a9494ee7e271a8302a6a7c +Subproject commit 0b30e0c60b89efb879109194e4281012bffbfa98