Skip to content

LRU Gc for memory #4724

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

Merged
merged 15 commits into from
Apr 19, 2023
Merged
23 changes: 23 additions & 0 deletions firebase-firestore/api.txt
Original file line number Diff line number Diff line change
Expand Up @@ -316,11 +316,34 @@ package com.google.firebase.firestore {
}

public final class MemoryCacheSettings implements com.google.firebase.firestore.LocalCacheSettings {
method @NonNull public com.google.firebase.firestore.MemoryGarbageCollectorSettings getGarbageCollectorSettings();
method @NonNull public static com.google.firebase.firestore.MemoryCacheSettings.Builder newBuilder();
}

public static class MemoryCacheSettings.Builder {
method @NonNull public com.google.firebase.firestore.MemoryCacheSettings build();
method @NonNull public com.google.firebase.firestore.MemoryCacheSettings.Builder setGcSettings(@NonNull com.google.firebase.firestore.MemoryGarbageCollectorSettings);
}

public final class MemoryEagerGcSettings implements com.google.firebase.firestore.MemoryGarbageCollectorSettings {
method @NonNull public static com.google.firebase.firestore.MemoryEagerGcSettings.Builder newBuilder();
}

public static class MemoryEagerGcSettings.Builder {
method @NonNull public com.google.firebase.firestore.MemoryEagerGcSettings build();
}

public interface MemoryGarbageCollectorSettings {
}

public final class MemoryLruGcSettings implements com.google.firebase.firestore.MemoryGarbageCollectorSettings {
method public long getSizeBytes();
method @NonNull public static com.google.firebase.firestore.MemoryLruGcSettings.Builder newBuilder();
}

public static class MemoryLruGcSettings.Builder {
method @NonNull public com.google.firebase.firestore.MemoryLruGcSettings build();
method public void setSizeBytes(long);
}

public enum MetadataChanges {
Expand Down
2 changes: 2 additions & 0 deletions firebase-firestore/ktx/api.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ package com.google.firebase.firestore.ktx {
method public static inline <reified T> T getField(@NonNull com.google.firebase.firestore.DocumentSnapshot, @NonNull com.google.firebase.firestore.FieldPath fieldPath, @NonNull com.google.firebase.firestore.DocumentSnapshot.ServerTimestampBehavior serverTimestampBehavior);
method @NonNull public static com.google.firebase.firestore.FirebaseFirestore getFirestore(@NonNull com.google.firebase.ktx.Firebase);
method @NonNull public static com.google.firebase.firestore.MemoryCacheSettings memoryCacheSettings(@NonNull kotlin.jvm.functions.Function1<? super com.google.firebase.firestore.MemoryCacheSettings.Builder,kotlin.Unit> init);
method @NonNull public static com.google.firebase.firestore.MemoryEagerGcSettings memoryEagerGcSettings(@NonNull kotlin.jvm.functions.Function1<? super com.google.firebase.firestore.MemoryEagerGcSettings.Builder,kotlin.Unit> init);
method @NonNull public static com.google.firebase.firestore.MemoryLruGcSettings memoryLruGcSettings(@NonNull kotlin.jvm.functions.Function1<? super com.google.firebase.firestore.MemoryLruGcSettings.Builder,kotlin.Unit> init);
method @NonNull public static com.google.firebase.firestore.PersistentCacheSettings persistentCacheSettings(@NonNull kotlin.jvm.functions.Function1<? super com.google.firebase.firestore.PersistentCacheSettings.Builder,kotlin.Unit> init);
method @NonNull public static kotlinx.coroutines.flow.Flow<com.google.firebase.firestore.DocumentSnapshot> snapshots(@NonNull com.google.firebase.firestore.DocumentReference, @NonNull com.google.firebase.firestore.MetadataChanges metadataChanges = com.google.firebase.firestore.MetadataChanges.EXCLUDE);
method @NonNull public static kotlinx.coroutines.flow.Flow<com.google.firebase.firestore.QuerySnapshot> snapshots(@NonNull com.google.firebase.firestore.Query, @NonNull com.google.firebase.firestore.MetadataChanges metadataChanges = com.google.firebase.firestore.MetadataChanges.EXCLUDE);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,18 @@ fun memoryCacheSettings(init: MemoryCacheSettings.Builder.() -> Unit): MemoryCac
return builder.build()
}

fun memoryEagerGcSettings(init: MemoryEagerGcSettings.Builder.() -> Unit): MemoryEagerGcSettings {
val builder = MemoryEagerGcSettings.newBuilder()
builder.init()
return builder.build()
}

fun memoryLruGcSettings(init: MemoryLruGcSettings.Builder.() -> Unit): MemoryLruGcSettings {
val builder = MemoryLruGcSettings.newBuilder()
builder.init()
return builder.build()
}

fun persistentCacheSettings(
init: PersistentCacheSettings.Builder.() -> Unit
): PersistentCacheSettings {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,34 @@ class FirestoreTests : BaseTestCase() {
assertThat(otherSettings.isSslEnabled).isEqualTo(true)
assertThat(otherSettings.isPersistenceEnabled).isFalse()
}

@Test
fun `MemoryCacheSettings Garbage Collector builder works`() {
val host = "http://10.0.2.2:8080"
val isSslEnabled = false

val settings = firestoreSettings {
this.host = host
this.isSslEnabled = isSslEnabled
this.setLocalCacheSettings(memoryCacheSettings {})
}

assertThat(host).isEqualTo(settings.host)
assertThat(isSslEnabled).isEqualTo(settings.isSslEnabled)
assertThat(settings.isPersistenceEnabled).isFalse()
assertThat(settings.cacheSettings)
.isEqualTo(memoryCacheSettings { setGcSettings(memoryEagerGcSettings {}) })

val otherSettings = firestoreSettings {
this.setLocalCacheSettings(
memoryCacheSettings { setGcSettings(memoryLruGcSettings { setSizeBytes(1_000) }) }
)
}

assertThat(otherSettings.host).isEqualTo(FirebaseFirestoreSettings.DEFAULT_HOST)
assertThat(otherSettings.isPersistenceEnabled).isFalse()
assertThat(otherSettings.cacheSizeBytes).isEqualTo(1_000)
}
}

@RunWith(RobolectricTestRunner::class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1298,4 +1298,43 @@ public void testNewCacheConfigForPersistentCache() {
DocumentSnapshot snap = waitFor(instance.document("coll/doc").get(Source.CACHE));
assertEquals(map("foo", "bar"), snap.getData());
}

@Test
public void testCanGetDocumentWithMemoryLruGCEnabled() {
FirebaseFirestore db = testFirestore();
db.setFirestoreSettings(
new FirebaseFirestoreSettings.Builder(db.getFirestoreSettings())
.setLocalCacheSettings(
MemoryCacheSettings.newBuilder()
.setGcSettings(MemoryLruGcSettings.newBuilder().build())
.build())
.build());

DocumentReference doc = db.collection("abc").document("123");
waitFor(doc.set(map("key", "value")));

DocumentSnapshot snapshot = waitFor(doc.get(Source.CACHE));
assertTrue(snapshot.exists());
assertTrue(snapshot.getMetadata().isFromCache());
assertEquals(snapshot.getData(), map("key", "value"));
}

@Test
public void testCannotGetDocumentWithMemoryEagerGcEnabled() {
FirebaseFirestore db = testFirestore();
db.setFirestoreSettings(
new FirebaseFirestoreSettings.Builder(db.getFirestoreSettings())
.setLocalCacheSettings(
MemoryCacheSettings.newBuilder()
.setGcSettings(MemoryEagerGcSettings.newBuilder().build())
.build())
.build());

DocumentReference doc = db.collection("abc").document("123");
waitFor(doc.set(map("key", "value")));

Exception e = waitForException(doc.get(Source.CACHE));
assertTrue(e instanceof FirebaseFirestoreException);
assertEquals(((FirebaseFirestoreException) e).getCode(), Code.UNAVAILABLE);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public static final class Builder {
private String host;
private boolean sslEnabled;
private boolean persistenceEnabled;

private long cacheSizeBytes;
private LocalCacheSettings cacheSettings;

Expand Down Expand Up @@ -319,6 +320,12 @@ public long getCacheSizeBytes() {
if (cacheSettings instanceof PersistentCacheSettings) {
return ((PersistentCacheSettings) cacheSettings).getSizeBytes();
} else {
MemoryCacheSettings memorySettings = (MemoryCacheSettings) cacheSettings;
if (memorySettings.getGarbageCollectorSettings() instanceof MemoryLruGcSettings) {
return ((MemoryLruGcSettings) memorySettings.getGarbageCollectorSettings())
.getSizeBytes();
}

return CACHE_SIZE_UNLIMITED;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,42 +27,58 @@
* `FirebaseFirestoreSettings` instance to configure Firestore SDK.
*/
public final class MemoryCacheSettings implements LocalCacheSettings {
private MemoryGarbageCollectorSettings gcSettings;

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

private MemoryCacheSettings() {}
private MemoryCacheSettings(MemoryGarbageCollectorSettings settings) {
gcSettings = settings;
}

@Override
public int hashCode() {
return super.hashCode();
return gcSettings.hashCode();
}

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

return true;
return getGarbageCollectorSettings()
.equals(((MemoryCacheSettings) obj).getGarbageCollectorSettings());
}

@Override
public String toString() {
return "MemoryCacheSettings{}";
return "MemoryCacheSettings{gcSettings=" + getGarbageCollectorSettings() + "}";
}

@NonNull
public MemoryGarbageCollectorSettings getGarbageCollectorSettings() {
return gcSettings;
}

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

private Builder() {}

/** Creates a {@code MemoryCacheSettings} instance. */
@NonNull
public MemoryCacheSettings build() {
return new MemoryCacheSettings();
return new MemoryCacheSettings(gcSettings);
}

@NonNull
public Builder setGcSettings(@NonNull MemoryGarbageCollectorSettings gcSettings) {
this.gcSettings = gcSettings;
return this;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.firebase.firestore;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

public final class MemoryEagerGcSettings implements MemoryGarbageCollectorSettings {
private MemoryEagerGcSettings() {}

public static class Builder {
private Builder() {}

@NonNull
public MemoryEagerGcSettings build() {
return new MemoryEagerGcSettings();
}
}

@Override
public int hashCode() {
return super.hashCode();
}

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

return true;
}

@NonNull
@Override
public String toString() {
return "MemoryEagerGcSettings{}";
}

@NonNull
public static MemoryEagerGcSettings.Builder newBuilder() {
return new Builder();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.firebase.firestore;

public interface MemoryGarbageCollectorSettings {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.firebase.firestore;

import androidx.annotation.NonNull;

public final class MemoryLruGcSettings implements MemoryGarbageCollectorSettings {

private long sizeBytes;

public static class Builder {
private long sizeBytes = FirebaseFirestoreSettings.DEFAULT_CACHE_SIZE_BYTES;

private Builder() {}

@NonNull
public MemoryLruGcSettings build() {
return new MemoryLruGcSettings(sizeBytes);
}

public void setSizeBytes(long size) {
sizeBytes = size;
}
}

private MemoryLruGcSettings(long size) {
sizeBytes = size;
}

@NonNull
public static MemoryLruGcSettings.Builder newBuilder() {
return new Builder();
}

public long getSizeBytes() {
return sizeBytes;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;

MemoryLruGcSettings that = (MemoryLruGcSettings) o;

return sizeBytes == that.sizeBytes;
}

@Override
public int hashCode() {
return (int) (sizeBytes ^ (sizeBytes >>> 32));
}

@NonNull
@Override
public String toString() {
return "MemoryLruGcSettings{cacheSize=" + getSizeBytes() + "}";
}
}
Loading