Skip to content

Commit c39ef81

Browse files
authored
Add option to allow SDK create cache indexes automatically to improve query execution locally (#11596)
1 parent ab7228e commit c39ef81

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+1615
-32
lines changed

Firestore/Example/Tests/Integration/API/FIRDatabaseTests.mm

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1829,4 +1829,63 @@ - (void)testUnlimitedCacheSize {
18291829
XCTAssertEqualObjects(result.data, data);
18301830
}
18311831

1832+
- (void)testGetValidPersistentCacheIndexManager {
1833+
[FIRApp configure];
1834+
1835+
FIRFirestore *db1 = [FIRFirestore firestoreForDatabase:@"PersistentCacheIndexManagerDB1"];
1836+
FIRFirestoreSettings *settings1 = [db1 settings];
1837+
[settings1 setCacheSettings:[[FIRPersistentCacheSettings alloc] init]];
1838+
[db1 setSettings:settings1];
1839+
1840+
XCTAssertNotNil(db1.persistentCacheIndexManager);
1841+
1842+
// Use persistent disk cache (default)
1843+
FIRFirestore *db2 = [FIRFirestore firestoreForDatabase:@"PersistentCacheIndexManagerDB2"];
1844+
XCTAssertNotNil(db2.persistentCacheIndexManager);
1845+
1846+
// Disable persistent disk cache
1847+
FIRFirestore *db3 = [FIRFirestore firestoreForDatabase:@"PersistentCacheIndexManagerDB3"];
1848+
FIRFirestoreSettings *settings3 = [db3 settings];
1849+
[settings3 setCacheSettings:[[FIRMemoryCacheSettings alloc] init]];
1850+
[db3 setSettings:settings3];
1851+
1852+
XCTAssertNil(db3.persistentCacheIndexManager);
1853+
1854+
// Disable persistent disk cache (deprecated)
1855+
FIRFirestore *db4 = [FIRFirestore firestoreForDatabase:@"PersistentCacheIndexManagerDB4"];
1856+
FIRFirestoreSettings *settings4 = [db4 settings];
1857+
settings4.persistenceEnabled = NO;
1858+
[db4 setSettings:settings4];
1859+
XCTAssertNil(db4.persistentCacheIndexManager);
1860+
}
1861+
1862+
- (void)testCanGetSameOrDifferentPersistentCacheIndexManager {
1863+
[FIRApp configure];
1864+
// Use persistent disk cache (explicit)
1865+
FIRFirestore *db1 = [FIRFirestore firestoreForDatabase:@"PersistentCacheIndexManagerDB5"];
1866+
FIRFirestoreSettings *settings1 = [db1 settings];
1867+
[settings1 setCacheSettings:[[FIRPersistentCacheSettings alloc] init]];
1868+
[db1 setSettings:settings1];
1869+
XCTAssertEqual(db1.persistentCacheIndexManager, db1.persistentCacheIndexManager);
1870+
1871+
// Use persistent disk cache (default)
1872+
FIRFirestore *db2 = [FIRFirestore firestoreForDatabase:@"PersistentCacheIndexManagerDB6"];
1873+
XCTAssertEqual(db2.persistentCacheIndexManager, db2.persistentCacheIndexManager);
1874+
1875+
XCTAssertNotEqual(db1.persistentCacheIndexManager, db2.persistentCacheIndexManager);
1876+
1877+
FIRFirestore *db3 = [FIRFirestore firestoreForDatabase:@"PersistentCacheIndexManagerDB7"];
1878+
FIRFirestoreSettings *settings3 = [db3 settings];
1879+
[settings3 setCacheSettings:[[FIRPersistentCacheSettings alloc] init]];
1880+
[db3 setSettings:settings3];
1881+
XCTAssertNotEqual(db1.persistentCacheIndexManager, db3.persistentCacheIndexManager);
1882+
XCTAssertNotEqual(db2.persistentCacheIndexManager, db3.persistentCacheIndexManager);
1883+
1884+
// Use persistent disk cache (default)
1885+
FIRFirestore *db4 = [FIRFirestore firestoreForDatabase:@"PersistentCacheIndexManagerDB8"];
1886+
XCTAssertNotEqual(db1.persistentCacheIndexManager, db4.persistentCacheIndexManager);
1887+
XCTAssertNotEqual(db2.persistentCacheIndexManager, db4.persistentCacheIndexManager);
1888+
XCTAssertNotEqual(db3.persistentCacheIndexManager, db4.persistentCacheIndexManager);
1889+
}
1890+
18321891
@end

Firestore/Example/Tests/Integration/API/FIRIndexingTests.mm

Lines changed: 114 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@
1818

1919
#import <XCTest/XCTest.h>
2020

21+
#import "Firestore/Source/API/FIRFirestore+Internal.h"
22+
#import "Firestore/Source/API/FIRPersistentCacheIndexManager+Internal.h"
23+
24+
#import "Firestore/Example/Tests/Util/FSTHelpers.h"
2125
#import "Firestore/Example/Tests/Util/FSTIntegrationTestCase.h"
2226

2327
@interface FIRIndexingTests : FSTIntegrationTestCase
@@ -29,15 +33,15 @@ @implementation FIRIndexingTests
2933
- (void)setUp {
3034
[super setUp];
3135
self.db = [self firestore];
32-
XCTestExpectation* exp = [self expectationWithDescription:@"clear persistence"];
33-
[self.db clearPersistenceWithCompletion:^(NSError*) {
36+
XCTestExpectation *exp = [self expectationWithDescription:@"clear persistence"];
37+
[self.db clearPersistenceWithCompletion:^(NSError *) {
3438
[exp fulfill];
3539
}];
3640
[self awaitExpectation:exp];
3741
}
3842

3943
- (void)testCanConfigureIndexes {
40-
NSString* json = @"{\n"
44+
NSString *json = @"{\n"
4145
"\t\"indexes\": [{\n"
4246
"\t\t\t\"collectionGroup\": \"restaurants\",\n"
4347
"\t\t\t\"queryScope\": \"COLLECTION\",\n"
@@ -64,22 +68,22 @@ - (void)testCanConfigureIndexes {
6468
"}";
6569

6670
[self.db setIndexConfigurationFromJSON:json
67-
completion:^(NSError* error) {
71+
completion:^(NSError *error) {
6872
XCTAssertNil(error);
6973
}];
7074
}
7175

7276
- (void)testBadJsonDoesNotCrashClient {
7377
[self.db setIndexConfigurationFromJSON:@"{,"
74-
completion:^(NSError* error) {
78+
completion:^(NSError *error) {
7579
XCTAssertNotNil(error);
7680
XCTAssertEqualObjects(error.domain, FIRFirestoreErrorDomain);
7781
XCTAssertEqual(error.code, FIRFirestoreErrorCodeInvalidArgument);
7882
}];
7983
}
8084

8185
- (void)testBadIndexDoesNotCrashClient {
82-
NSString* json = @"{\n"
86+
NSString *json = @"{\n"
8387
"\t\"indexes\": [{\n"
8488
"\t\t\"collectionGroup\": \"restaurants\",\n"
8589
"\t\t\"queryScope\": \"COLLECTION\",\n"
@@ -92,11 +96,114 @@ - (void)testBadIndexDoesNotCrashClient {
9296
"}";
9397

9498
[self.db setIndexConfigurationFromJSON:json
95-
completion:^(NSError* error) {
99+
completion:^(NSError *error) {
96100
XCTAssertNotNil(error);
97101
XCTAssertEqualObjects(error.domain, FIRFirestoreErrorDomain);
98102
XCTAssertEqual(error.code, FIRFirestoreErrorCodeInvalidArgument);
99103
}];
100104
}
101105

106+
/**
107+
* After Auto Index Creation is enabled, through public API there is no way to see the indexes
108+
* sitting inside SDK. So this test only checks the API of auto index creation.
109+
*/
110+
- (void)testAutoIndexCreationSetSuccessfully {
111+
// Use persistent disk cache (explict)
112+
FIRFirestoreSettings *settings = [self.db settings];
113+
[settings setCacheSettings:[[FIRPersistentCacheSettings alloc] init]];
114+
[self.db setSettings:settings];
115+
116+
FIRCollectionReference *coll = [self collectionRef];
117+
NSDictionary *testDocs = @{
118+
@"a" : @{@"match" : @YES},
119+
@"b" : @{@"match" : @NO},
120+
@"c" : @{@"match" : @NO},
121+
};
122+
[self writeAllDocuments:testDocs toCollection:coll];
123+
124+
FIRQuery *query = [coll queryWhereField:@"match" isEqualTo:@YES];
125+
126+
[query getDocumentsWithSource:FIRFirestoreSourceCache
127+
completion:^(FIRQuerySnapshot *results, NSError *error) {
128+
XCTAssertNil(error);
129+
XCTAssertEqual(results.count, 1);
130+
}];
131+
132+
XCTAssertNoThrow([self.db.persistentCacheIndexManager enableIndexAutoCreation]);
133+
[query getDocumentsWithSource:FIRFirestoreSourceCache
134+
completion:^(FIRQuerySnapshot *results, NSError *error) {
135+
XCTAssertNil(error);
136+
XCTAssertEqual(results.count, 1);
137+
}];
138+
139+
XCTAssertNoThrow([self.db.persistentCacheIndexManager disableIndexAutoCreation]);
140+
[query getDocumentsWithSource:FIRFirestoreSourceCache
141+
completion:^(FIRQuerySnapshot *results, NSError *error) {
142+
XCTAssertNil(error);
143+
XCTAssertEqual(results.count, 1);
144+
}];
145+
146+
XCTAssertNoThrow([self.db.persistentCacheIndexManager deleteAllIndexes]);
147+
[query getDocumentsWithSource:FIRFirestoreSourceCache
148+
completion:^(FIRQuerySnapshot *results, NSError *error) {
149+
XCTAssertNil(error);
150+
XCTAssertEqual(results.count, 1);
151+
}];
152+
}
153+
154+
- (void)testAutoIndexCreationSetSuccessfullyUsingDefault {
155+
// Use persistent disk cache (default)
156+
FIRCollectionReference *coll = [self collectionRef];
157+
NSDictionary *testDocs = @{
158+
@"a" : @{@"match" : @YES},
159+
@"b" : @{@"match" : @NO},
160+
@"c" : @{@"match" : @NO},
161+
};
162+
[self writeAllDocuments:testDocs toCollection:coll];
163+
164+
FIRQuery *query = [coll queryWhereField:@"match" isEqualTo:@YES];
165+
166+
[query getDocumentsWithSource:FIRFirestoreSourceCache
167+
completion:^(FIRQuerySnapshot *results, NSError *error) {
168+
XCTAssertNil(error);
169+
XCTAssertEqual(results.count, 1);
170+
}];
171+
172+
XCTAssertNoThrow([self.db.persistentCacheIndexManager enableIndexAutoCreation]);
173+
[query getDocumentsWithSource:FIRFirestoreSourceCache
174+
completion:^(FIRQuerySnapshot *results, NSError *error) {
175+
XCTAssertNil(error);
176+
XCTAssertEqual(results.count, 1);
177+
}];
178+
179+
XCTAssertNoThrow([self.db.persistentCacheIndexManager disableIndexAutoCreation]);
180+
[query getDocumentsWithSource:FIRFirestoreSourceCache
181+
completion:^(FIRQuerySnapshot *results, NSError *error) {
182+
XCTAssertNil(error);
183+
XCTAssertEqual(results.count, 1);
184+
}];
185+
186+
XCTAssertNoThrow([self.db.persistentCacheIndexManager deleteAllIndexes]);
187+
[query getDocumentsWithSource:FIRFirestoreSourceCache
188+
completion:^(FIRQuerySnapshot *results, NSError *error) {
189+
XCTAssertNil(error);
190+
XCTAssertEqual(results.count, 1);
191+
}];
192+
}
193+
194+
- (void)testAutoIndexCreationAfterFailsTermination {
195+
[self terminateFirestore:self.db];
196+
197+
FSTAssertThrows([self.db.persistentCacheIndexManager enableIndexAutoCreation],
198+
@"The client has already been terminated.");
199+
200+
FSTAssertThrows([self.db.persistentCacheIndexManager disableIndexAutoCreation],
201+
@"The client has already been terminated.");
202+
203+
FSTAssertThrows([self.db.persistentCacheIndexManager deleteAllIndexes],
204+
@"The client has already been terminated.");
205+
}
206+
207+
// TODO(b/296100693) Add testing hooks to verify indexes are created as expected.
208+
102209
@end

Firestore/Source/API/FIRFirestore+Internal.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
@class FIRApp;
2727
@class FSTUserDataReader;
28+
@class FIRPersistentCacheIndexManager;
2829

2930
namespace firebase {
3031
namespace firestore {
@@ -78,6 +79,9 @@ NS_ASSUME_NONNULL_BEGIN
7879

7980
- (const std::shared_ptr<firebase::firestore::util::AsyncQueue> &)workerQueue;
8081

82+
// TODO(csi): make this function public
83+
@property(nonatomic, readonly) FIRPersistentCacheIndexManager *persistentCacheIndexManager;
84+
8185
@property(nonatomic, assign, readonly) std::shared_ptr<api::Firestore> wrapped;
8286

8387
@property(nonatomic, assign, readonly) const model::DatabaseId &databaseID;

Firestore/Source/API/FIRFirestore.mm

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include <utility>
2222

2323
#import "FIRFirestoreSettings+Internal.h"
24+
#import "FIRPersistentCacheIndexManager+Internal.h"
2425
#import "FIRTransactionOptions+Internal.h"
2526
#import "FIRTransactionOptions.h"
2627

@@ -106,6 +107,7 @@ @implementation FIRFirestore {
106107
std::shared_ptr<Firestore> _firestore;
107108
FIRFirestoreSettings *_settings;
108109
__weak id<FSTFirestoreInstanceRegistry> _registry;
110+
FIRPersistentCacheIndexManager *_indexManager;
109111
}
110112

111113
+ (void)initialize {
@@ -533,6 +535,17 @@ @implementation FIRFirestore (Internal)
533535
return _firestore->worker_queue();
534536
}
535537

538+
- (FIRPersistentCacheIndexManager *)persistentCacheIndexManager {
539+
if (!_indexManager) {
540+
auto index_manager = _firestore->persistent_cache_index_manager();
541+
if (index_manager) {
542+
_indexManager = [[FIRPersistentCacheIndexManager alloc]
543+
initWithPersistentCacheIndexManager:index_manager];
544+
}
545+
}
546+
return _indexManager;
547+
}
548+
536549
- (const DatabaseId &)databaseID {
537550
return _firestore->database_id();
538551
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*
2+
* Copyright 2023 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#import <Foundation/Foundation.h>
18+
19+
#include <memory>
20+
21+
#include "Firestore/core/src/api/persistent_cache_index_manager.h"
22+
23+
NS_ASSUME_NONNULL_BEGIN
24+
25+
// TODO(sum/avg) move the contents of this category to
26+
// ../Public/FirebaseFirestore/FIRPersistentCacheIndexManager.h
27+
/**
28+
* A PersistentCacheIndexManager which you can config persistent cache indexes used for
29+
* local query execution.
30+
*/
31+
NS_SWIFT_NAME(PersistentCacheIndexManager)
32+
@interface FIRPersistentCacheIndexManager : NSObject
33+
34+
/** :nodoc: */
35+
- (instancetype)init
36+
__attribute__((unavailable("FIRPersistentCacheIndexManager cannot be created directly.")));
37+
38+
/**
39+
* Enables SDK to create persistent cache indexes automatically for local query execution when SDK
40+
* believes cache indexes can help improves performance.
41+
*
42+
* This feature is disabled by default.
43+
*/
44+
- (void)enableIndexAutoCreation NS_SWIFT_NAME(enableIndexAutoCreation());
45+
46+
/**
47+
* Stops creating persistent cache indexes automatically for local query execution. The indexes
48+
* which have been created by calling `enableIndexAutoCreation` still take effect.
49+
*/
50+
- (void)disableIndexAutoCreation NS_SWIFT_NAME(disableIndexAutoCreation());
51+
52+
/**
53+
* Removes all persistent cache indexes. Please note this function will also deletes indexes
54+
* generated by [[FIRFirestore firestore] setIndexConfigurationFromJSON] and [[FIRFirestore
55+
* firestore] setIndexConfigurationFromStream], which are deprecated.
56+
*/
57+
- (void)deleteAllIndexes NS_SWIFT_NAME(deleteAllIndexes());
58+
59+
@end
60+
61+
@interface FIRPersistentCacheIndexManager (/* Init */)
62+
63+
- (instancetype)initWithPersistentCacheIndexManager:
64+
(std::shared_ptr<const firebase::firestore::api::PersistentCacheIndexManager>)indexManager
65+
NS_DESIGNATED_INITIALIZER;
66+
67+
@end
68+
69+
NS_ASSUME_NONNULL_END

0 commit comments

Comments
 (0)