Skip to content

Commit 5165fc5

Browse files
authored
Merge 9bc62af into 734adc0
2 parents 734adc0 + 9bc62af commit 5165fc5

31 files changed

+10970
-281
lines changed

firebase-firestore/src/androidTest/java/com/google/firebase/firestore/FirestoreTest.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1298,4 +1298,39 @@ public void testNewCacheConfigForPersistentCache() {
12981298
DocumentSnapshot snap = waitFor(instance.document("coll/doc").get(Source.CACHE));
12991299
assertEquals(map("foo", "bar"), snap.getData());
13001300
}
1301+
1302+
@Test
1303+
public void testCanGetDocumentWithMemoryLruGCEnabled() {
1304+
FirebaseFirestore db = testFirestore();
1305+
db.setFirestoreSettings(
1306+
new FirebaseFirestoreSettings.Builder(db.getFirestoreSettings())
1307+
.setPersistenceEnabled(false)
1308+
.setMemoryLruGcEnabled(true)
1309+
.build());
1310+
1311+
DocumentReference doc = db.collection("abc").document("123");
1312+
waitFor(doc.set(map("key", "value")));
1313+
1314+
DocumentSnapshot snapshot = waitFor(doc.get(Source.CACHE));
1315+
assertTrue(snapshot.exists());
1316+
assertTrue(snapshot.getMetadata().isFromCache());
1317+
assertEquals(snapshot.getData(), map("key", "value"));
1318+
}
1319+
1320+
@Test
1321+
public void testCannotGetDocumentWithMemoryEagerGcEnabled() {
1322+
FirebaseFirestore db = testFirestore();
1323+
db.setFirestoreSettings(
1324+
new FirebaseFirestoreSettings.Builder(db.getFirestoreSettings())
1325+
.setPersistenceEnabled(false)
1326+
.setMemoryLruGcEnabled(false)
1327+
.build());
1328+
1329+
DocumentReference doc = db.collection("abc").document("123");
1330+
waitFor(doc.set(map("key", "value")));
1331+
1332+
Exception e = waitForException(doc.get(Source.CACHE));
1333+
assertTrue(e instanceof FirebaseFirestoreException);
1334+
assertEquals(((FirebaseFirestoreException) e).getCode(), Code.UNAVAILABLE);
1335+
}
13011336
}

firebase-firestore/src/main/java/com/google/firebase/firestore/FirebaseFirestoreSettings.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ public static final class Builder {
4040
private String host;
4141
private boolean sslEnabled;
4242
private boolean persistenceEnabled;
43+
4344
private long cacheSizeBytes;
4445
private LocalCacheSettings cacheSettings;
4546

@@ -319,6 +320,12 @@ public long getCacheSizeBytes() {
319320
if (cacheSettings instanceof PersistentCacheSettings) {
320321
return ((PersistentCacheSettings) cacheSettings).getSizeBytes();
321322
} else {
323+
MemoryCacheSettings memorySettings = (MemoryCacheSettings) cacheSettings;
324+
if (memorySettings.getGarbageCollectorSettings() instanceof MemoryLruGcSettings) {
325+
return ((MemoryLruGcSettings) memorySettings.getGarbageCollectorSettings())
326+
.getSizeBytes();
327+
}
328+
322329
return CACHE_SIZE_UNLIMITED;
323330
}
324331
}

firebase-firestore/src/main/java/com/google/firebase/firestore/MemoryCacheSettings.java

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,42 +27,58 @@
2727
* `FirebaseFirestoreSettings` instance to configure Firestore SDK.
2828
*/
2929
public final class MemoryCacheSettings implements LocalCacheSettings {
30+
private MemoryGarbageCollectorSettings gcSettings;
3031

3132
/** Returns a new instance of {@link MemoryCacheSettings.Builder} with default configurations. */
3233
@NonNull
3334
public static MemoryCacheSettings.Builder newBuilder() {
3435
return new MemoryCacheSettings.Builder();
3536
}
3637

37-
private MemoryCacheSettings() {}
38+
private MemoryCacheSettings(MemoryGarbageCollectorSettings settings) {
39+
gcSettings = settings;
40+
}
3841

3942
@Override
4043
public int hashCode() {
41-
return super.hashCode();
44+
return gcSettings.hashCode();
4245
}
4346

4447
@Override
4548
public boolean equals(@Nullable Object obj) {
4649
if (this == obj) return true;
4750
if (obj == null || getClass() != obj.getClass()) return false;
4851

49-
return true;
52+
return getGarbageCollectorSettings()
53+
.equals(((MemoryCacheSettings) obj).getGarbageCollectorSettings());
5054
}
5155

5256
@Override
5357
public String toString() {
54-
return "MemoryCacheSettings{}";
58+
return "MemoryCacheSettings{gcSettings " + getGarbageCollectorSettings() + "}";
59+
}
60+
61+
@NonNull
62+
public MemoryGarbageCollectorSettings getGarbageCollectorSettings() {
63+
return gcSettings;
5564
}
5665

5766
/** A Builder for creating {@code MemoryCacheSettings} instance. */
5867
public static class Builder {
68+
private MemoryGarbageCollectorSettings gcSettings = MemoryEagerGcSettings.newBuilder().build();
5969

6070
private Builder() {}
6171

6272
/** Creates a {@code MemoryCacheSettings} instance. */
6373
@NonNull
6474
public MemoryCacheSettings build() {
65-
return new MemoryCacheSettings();
75+
return new MemoryCacheSettings(gcSettings);
76+
}
77+
78+
@NonNull
79+
public Builder setGcSettings(@NonNull MemoryGarbageCollectorSettings gcSettings) {
80+
this.gcSettings = gcSettings;
81+
return this;
6682
}
6783
}
6884
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Copyright 2023 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.google.firebase.firestore;
16+
17+
import androidx.annotation.NonNull;
18+
import androidx.annotation.Nullable;
19+
20+
public final class MemoryEagerGcSettings implements MemoryGarbageCollectorSettings {
21+
private MemoryEagerGcSettings() {}
22+
23+
public static class Builder {
24+
private Builder() {}
25+
26+
@NonNull
27+
public MemoryEagerGcSettings build() {
28+
return new MemoryEagerGcSettings();
29+
}
30+
}
31+
32+
@Override
33+
public int hashCode() {
34+
return super.hashCode();
35+
}
36+
37+
@Override
38+
public boolean equals(@Nullable Object obj) {
39+
if (this == obj) return true;
40+
if (obj == null || getClass() != obj.getClass()) return false;
41+
42+
return true;
43+
}
44+
45+
@NonNull
46+
@Override
47+
public String toString() {
48+
return "MemoryEagerGcSettings{}";
49+
}
50+
51+
@NonNull
52+
public static MemoryEagerGcSettings.Builder newBuilder() {
53+
return new Builder();
54+
}
55+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Copyright 2023 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.google.firebase.firestore;
16+
17+
public interface MemoryGarbageCollectorSettings {}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package com.google.firebase.firestore;
2+
3+
import androidx.annotation.NonNull;
4+
import androidx.annotation.Nullable;
5+
6+
public final class MemoryLruGcSettings implements MemoryGarbageCollectorSettings {
7+
8+
private long sizeBytes;
9+
10+
public static class Builder {
11+
private long sizeBytes = FirebaseFirestoreSettings.DEFAULT_CACHE_SIZE_BYTES;
12+
13+
private Builder() {}
14+
15+
@NonNull
16+
public MemoryLruGcSettings build() {
17+
return new MemoryLruGcSettings(sizeBytes);
18+
}
19+
20+
public void setSizeBytes(long size) {
21+
sizeBytes = size;
22+
}
23+
}
24+
25+
private MemoryLruGcSettings(long size) {
26+
sizeBytes = size;
27+
}
28+
29+
@NonNull
30+
public static MemoryLruGcSettings.Builder newBuilder() {
31+
return new Builder();
32+
}
33+
34+
public long getSizeBytes() {
35+
return sizeBytes;
36+
}
37+
38+
@Override
39+
public int hashCode() {
40+
return super.hashCode();
41+
}
42+
43+
@Override
44+
public boolean equals(@Nullable Object obj) {
45+
if (this == obj) return true;
46+
if (obj == null || getClass() != obj.getClass()) return false;
47+
48+
return true;
49+
}
50+
51+
@NonNull
52+
@Override
53+
public String toString() {
54+
return "MemoryLruGcSettings{cacheSize= " + getSizeBytes() + "}";
55+
}
56+
}

firebase-firestore/src/main/java/com/google/firebase/firestore/core/MemoryComponentProvider.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,13 @@
1616

1717
import androidx.annotation.Nullable;
1818
import com.google.firebase.database.collection.ImmutableSortedSet;
19+
import com.google.firebase.firestore.FirebaseFirestoreSettings;
20+
import com.google.firebase.firestore.MemoryCacheSettings;
21+
import com.google.firebase.firestore.MemoryLruGcSettings;
1922
import com.google.firebase.firestore.local.IndexBackfiller;
23+
import com.google.firebase.firestore.local.LocalSerializer;
2024
import com.google.firebase.firestore.local.LocalStore;
25+
import com.google.firebase.firestore.local.LruGarbageCollector;
2126
import com.google.firebase.firestore.local.MemoryPersistence;
2227
import com.google.firebase.firestore.local.Persistence;
2328
import com.google.firebase.firestore.local.QueryEngine;
@@ -26,6 +31,7 @@
2631
import com.google.firebase.firestore.model.mutation.MutationBatchResult;
2732
import com.google.firebase.firestore.remote.AndroidConnectivityMonitor;
2833
import com.google.firebase.firestore.remote.RemoteEvent;
34+
import com.google.firebase.firestore.remote.RemoteSerializer;
2935
import com.google.firebase.firestore.remote.RemoteStore;
3036
import io.grpc.Status;
3137

@@ -62,8 +68,28 @@ protected AndroidConnectivityMonitor createConnectivityMonitor(Configuration con
6268
return new AndroidConnectivityMonitor(configuration.getContext());
6369
}
6470

71+
private boolean isMemoryLruGcEnabled(FirebaseFirestoreSettings settings) {
72+
if (settings.getCacheSettings() != null
73+
&& settings.getCacheSettings() instanceof MemoryCacheSettings) {
74+
MemoryCacheSettings memorySettings = (MemoryCacheSettings) settings.getCacheSettings();
75+
return memorySettings.getGarbageCollectorSettings() instanceof MemoryLruGcSettings;
76+
}
77+
78+
return false;
79+
}
80+
6581
@Override
6682
protected Persistence createPersistence(Configuration configuration) {
83+
if (isMemoryLruGcEnabled(configuration.getSettings())) {
84+
LocalSerializer serializer =
85+
new LocalSerializer(
86+
new RemoteSerializer(configuration.getDatabaseInfo().getDatabaseId()));
87+
LruGarbageCollector.Params params =
88+
LruGarbageCollector.Params.WithCacheSizeBytes(
89+
configuration.getSettings().getCacheSizeBytes());
90+
return MemoryPersistence.createLruGcMemoryPersistence(params, serializer);
91+
}
92+
6793
return MemoryPersistence.createEagerGcMemoryPersistence();
6894
}
6995

firebase-firestore/src/main/java/com/google/firebase/firestore/local/LruGarbageCollector.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,10 @@ public static Params WithCacheSizeBytes(long cacheSizeBytes) {
6161
return new Params(cacheSizeBytes, 10, 1000);
6262
}
6363

64-
final long minBytesThreshold;
65-
final int percentileToCollect;
64+
// Not final for testing purposes.
65+
long minBytesThreshold;
66+
// Not final for testing purposes.
67+
int percentileToCollect;
6668
final int maximumSequenceNumbersToCollect;
6769

6870
Params(long minBytesThreshold, int percentileToCollect, int maximumSequenceNumbersToCollect) {
@@ -163,6 +165,13 @@ public GCScheduler newScheduler(AsyncQueue asyncQueue, LocalStore localStore) {
163165
return new GCScheduler(asyncQueue, localStore);
164166
}
165167

168+
// Visible for testing purposes only.
169+
public LruGarbageCollector withNewThreshold(long cacheThreshold) {
170+
this.params.minBytesThreshold = cacheThreshold;
171+
this.params.percentileToCollect = 100;
172+
return this;
173+
}
174+
166175
/** Given a percentile of target to collect, returns the number of targets to collect. */
167176
int calculateQueryCount(int percentile) {
168177
long targetCount = delegate.getSequenceNumberCount();

firebase-firestore/src/main/java/com/google/firebase/firestore/local/MemoryLruReferenceDelegate.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
import java.util.Map;
2828

2929
/** Provides LRU garbage collection functionality for MemoryPersistence. */
30-
class MemoryLruReferenceDelegate implements ReferenceDelegate, LruDelegate {
30+
public class MemoryLruReferenceDelegate implements ReferenceDelegate, LruDelegate {
3131
private final MemoryPersistence persistence;
3232
private final LocalSerializer serializer;
3333
private final Map<DocumentKey, Long> orphanedSequenceNumbers;

firebase-firestore/src/main/java/com/google/firebase/firestore/local/MemoryPersistence.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ public boolean isStarted() {
8585
}
8686

8787
@Override
88-
ReferenceDelegate getReferenceDelegate() {
88+
public ReferenceDelegate getReferenceDelegate() {
8989
return referenceDelegate;
9090
}
9191

firebase-firestore/src/main/java/com/google/firebase/firestore/local/Persistence.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,8 @@ public abstract class Persistence {
6767

6868
public abstract boolean isStarted();
6969

70-
abstract ReferenceDelegate getReferenceDelegate();
70+
// Visible for testing purposes.
71+
public abstract ReferenceDelegate getReferenceDelegate();
7172

7273
/**
7374
* Returns a MutationQueue representing the persisted mutations for the given user.

firebase-firestore/src/main/java/com/google/firebase/firestore/local/SQLiteLruReferenceDelegate.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
import java.util.List;
2626

2727
/** Provides LRU functionality for SQLite persistence. */
28-
class SQLiteLruReferenceDelegate implements ReferenceDelegate, LruDelegate {
28+
public class SQLiteLruReferenceDelegate implements ReferenceDelegate, LruDelegate {
2929
/**
3030
* The batch size for orphaned document GC in `removeOrphanedDocuments()`.
3131
*

firebase-firestore/src/test/java/com/google/firebase/firestore/spec/MemorySpecTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,12 @@ protected boolean isExcluded(Set<String> tags) {
4040

4141
@Override
4242
protected MemoryComponentProvider initializeComponentProvider(
43-
ComponentProvider.Configuration configuration, boolean garbageCollectionEnabled) {
43+
ComponentProvider.Configuration configuration, boolean useEagerGc) {
4444
MemoryComponentProvider provider =
4545
new MemoryComponentProvider() {
4646
@Override
4747
protected Persistence createPersistence(Configuration configuration) {
48-
if (garbageCollectionEnabled) {
48+
if (useEagerGc) {
4949
return MemoryPersistence.createEagerGcMemoryPersistence();
5050
} else {
5151
DatabaseId databaseId = DatabaseId.forProject("projectId");

0 commit comments

Comments
 (0)