Merge "Add SearchContext#setPersistToDiskRecoveryProof." into androidx-main
diff --git a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/AppSearchImplTest.java b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/AppSearchImplTest.java
index af20e81..898e680 100644
--- a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/AppSearchImplTest.java
+++ b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/AppSearchImplTest.java
@@ -3877,17 +3877,27 @@
Collections.emptyMap());
assertThat(getResult).isEqualTo(document);
- // That document should be visible even from another instance.
+ // Inialize a new instance of AppSearch to test initialization.
+ InitializeStats.Builder initStatsBuilder = new InitializeStats.Builder();
AppSearchImpl appSearchImpl2 = AppSearchImpl.create(
mAppSearchDir,
new AppSearchConfigImpl(
new UnlimitedLimitConfig(),
new LocalStorageIcingOptionsConfig()
),
- /*initStatsBuilder=*/ null,
+ /*initStatsBuilder=*/initStatsBuilder,
/*visibilityChecker=*/ null,
/*revocableFileDescriptorStore=*/ null,
ALWAYS_OPTIMIZE);
+
+ // Initialization should trigger a recovery
+ InitializeStats initStats = initStatsBuilder.build();
+ assertThat(initStats.getDocumentStoreRecoveryCause())
+ .isEqualTo(InitializeStats.RECOVERY_CAUSE_IO_ERROR);
+ assertThat(initStats.getIndexRestorationCause())
+ .isEqualTo(InitializeStats.RECOVERY_CAUSE_IO_ERROR);
+
+ // That document should be visible even from another instance.
getResult = appSearchImpl2.getDocument("package", "database", "namespace1",
"id1",
Collections.emptyMap());
@@ -3950,17 +3960,27 @@
Collections.emptyMap());
assertThat(getResult).isEqualTo(document2);
- // Only the second document should be retrievable from another instance.
+ // Inialize a new instance of AppSearch to test initialization.
+ InitializeStats.Builder initStatsBuilder = new InitializeStats.Builder();
AppSearchImpl appSearchImpl2 = AppSearchImpl.create(
mAppSearchDir,
new AppSearchConfigImpl(
new UnlimitedLimitConfig(),
new LocalStorageIcingOptionsConfig()
),
- /*initStatsBuilder=*/ null,
+ /*initStatsBuilder=*/initStatsBuilder,
/*visibilityChecker=*/ null,
/*revocableFileDescriptorStore=*/ null,
ALWAYS_OPTIMIZE);
+
+ // Initialization should trigger a recovery
+ InitializeStats initStats = initStatsBuilder.build();
+ assertThat(initStats.getDocumentStoreRecoveryCause())
+ .isEqualTo(InitializeStats.RECOVERY_CAUSE_IO_ERROR);
+ assertThat(initStats.getIndexRestorationCause())
+ .isEqualTo(InitializeStats.RECOVERY_CAUSE_IO_ERROR);
+
+ // Only the second document should be retrievable from another instance.
assertThrows(AppSearchException.class, () -> appSearchImpl2.getDocument("package",
"database",
"namespace1",
@@ -4030,17 +4050,263 @@
Collections.emptyMap());
assertThat(getResult).isEqualTo(document2);
- // Only the second document should be retrievable from another instance.
+ // Inialize a new instance of AppSearch to test initialization.
+ InitializeStats.Builder initStatsBuilder = new InitializeStats.Builder();
AppSearchImpl appSearchImpl2 = AppSearchImpl.create(
mAppSearchDir,
new AppSearchConfigImpl(
new UnlimitedLimitConfig(),
new LocalStorageIcingOptionsConfig()
),
- /*initStatsBuilder=*/ null,
+ /*initStatsBuilder=*/initStatsBuilder,
/*visibilityChecker=*/ null,
/*revocableFileDescriptorStore=*/ null,
ALWAYS_OPTIMIZE);
+
+ // Initialization should trigger a recovery
+ InitializeStats initStats = initStatsBuilder.build();
+ assertThat(initStats.getDocumentStoreRecoveryCause())
+ .isEqualTo(InitializeStats.RECOVERY_CAUSE_IO_ERROR);
+ assertThat(initStats.getIndexRestorationCause())
+ .isEqualTo(InitializeStats.RECOVERY_CAUSE_IO_ERROR);
+
+ // Only the second document should be retrievable from another instance.
+ assertThrows(AppSearchException.class, () -> appSearchImpl2.getDocument("package",
+ "database",
+ "namespace1",
+ "id1",
+ Collections.emptyMap()));
+ getResult = appSearchImpl2.getDocument("package", "database", "namespace2",
+ "id2",
+ Collections.emptyMap());
+ assertThat(getResult).isEqualTo(document2);
+ appSearchImpl2.close();
+ }
+
+ @Test
+ public void testPutPersistsWithoutRecoveryWithRecoveryProofFlush() throws Exception {
+ List<AppSearchSchema> schemas =
+ Collections.singletonList(new AppSearchSchema.Builder("type").build());
+ InternalSetSchemaResponse internalSetSchemaResponse = mAppSearchImpl.setSchema(
+ "package",
+ "database",
+ schemas,
+ /*visibilityConfigs=*/ Collections.emptyList(),
+ /*forceOverride=*/ false,
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
+ assertThat(internalSetSchemaResponse.isSuccess()).isTrue();
+
+ // Add a document and persist it.
+ GenericDocument document =
+ new GenericDocument.Builder<>("namespace1", "id1", "type").build();
+ mAppSearchImpl.putDocument(
+ "package",
+ "database",
+ document,
+ /*sendChangeNotifications=*/ false,
+ /*logger=*/ null);
+ mAppSearchImpl.persistToDisk(PersistType.Code.RECOVERY_PROOF);
+
+ GenericDocument getResult = mAppSearchImpl.getDocument("package", "database", "namespace1",
+ "id1",
+ Collections.emptyMap());
+ assertThat(getResult).isEqualTo(document);
+
+ // Inialize a new instance of AppSearch to test initialization.
+ InitializeStats.Builder initStatsBuilder = new InitializeStats.Builder();
+ AppSearchImpl appSearchImpl2 = AppSearchImpl.create(
+ mAppSearchDir,
+ new AppSearchConfigImpl(
+ new UnlimitedLimitConfig(),
+ new LocalStorageIcingOptionsConfig()
+ ),
+ /*initStatsBuilder=*/initStatsBuilder,
+ /*visibilityChecker=*/ null,
+ /*revocableFileDescriptorStore=*/ null,
+ ALWAYS_OPTIMIZE);
+
+ // Initialization should NOT trigger a recovery
+ InitializeStats initStats = initStatsBuilder.build();
+ assertThat(initStats.getDocumentStoreRecoveryCause())
+ .isEqualTo(InitializeStats.RECOVERY_CAUSE_NONE);
+ assertThat(initStats.getIndexRestorationCause())
+ .isEqualTo(InitializeStats.RECOVERY_CAUSE_NONE);
+
+ // That document should be visible even from another instance.
+ getResult = appSearchImpl2.getDocument("package", "database", "namespace1",
+ "id1",
+ Collections.emptyMap());
+ assertThat(getResult).isEqualTo(document);
+ appSearchImpl2.close();
+ }
+
+ @Test
+ public void testDeletePersistsWithoutRecoveryWithRecoveryProofFlush() throws Exception {
+ List<AppSearchSchema> schemas =
+ Collections.singletonList(new AppSearchSchema.Builder("type").build());
+ InternalSetSchemaResponse internalSetSchemaResponse = mAppSearchImpl.setSchema(
+ "package",
+ "database",
+ schemas,
+ /*visibilityConfigs=*/ Collections.emptyList(),
+ /*forceOverride=*/ false,
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
+ assertThat(internalSetSchemaResponse.isSuccess()).isTrue();
+
+ // Add two documents and persist them.
+ GenericDocument document1 =
+ new GenericDocument.Builder<>("namespace1", "id1", "type").build();
+ mAppSearchImpl.putDocument(
+ "package",
+ "database",
+ document1,
+ /*sendChangeNotifications=*/ false,
+ /*logger=*/ null);
+ GenericDocument document2 =
+ new GenericDocument.Builder<>("namespace1", "id2", "type").build();
+ mAppSearchImpl.putDocument(
+ "package",
+ "database",
+ document2,
+ /*sendChangeNotifications=*/ false,
+ /*logger=*/ null);
+ mAppSearchImpl.persistToDisk(PersistType.Code.RECOVERY_PROOF);
+
+ GenericDocument getResult = mAppSearchImpl.getDocument("package", "database", "namespace1",
+ "id1",
+ Collections.emptyMap());
+ assertThat(getResult).isEqualTo(document1);
+ getResult = mAppSearchImpl.getDocument("package", "database", "namespace1",
+ "id2",
+ Collections.emptyMap());
+ assertThat(getResult).isEqualTo(document2);
+
+ // Delete the first document
+ mAppSearchImpl.remove("package", "database", "namespace1", "id1", /*statsBuilder=*/ null);
+ mAppSearchImpl.persistToDisk(PersistType.Code.RECOVERY_PROOF);
+ assertThrows(AppSearchException.class, () -> mAppSearchImpl.getDocument("package",
+ "database",
+ "namespace1",
+ "id1",
+ Collections.emptyMap()));
+ getResult = mAppSearchImpl.getDocument("package", "database", "namespace1",
+ "id2",
+ Collections.emptyMap());
+ assertThat(getResult).isEqualTo(document2);
+
+ // Inialize a new instance of AppSearch to test initialization.
+ InitializeStats.Builder initStatsBuilder = new InitializeStats.Builder();
+ AppSearchImpl appSearchImpl2 = AppSearchImpl.create(
+ mAppSearchDir,
+ new AppSearchConfigImpl(
+ new UnlimitedLimitConfig(),
+ new LocalStorageIcingOptionsConfig()
+ ),
+ /*initStatsBuilder=*/initStatsBuilder,
+ /*visibilityChecker=*/ null,
+ /*revocableFileDescriptorStore=*/ null,
+ ALWAYS_OPTIMIZE);
+
+ // Initialization should NOT trigger a recovery.
+ InitializeStats initStats = initStatsBuilder.build();
+ assertThat(initStats.getDocumentStoreRecoveryCause())
+ .isEqualTo(InitializeStats.RECOVERY_CAUSE_NONE);
+ assertThat(initStats.getIndexRestorationCause())
+ .isEqualTo(InitializeStats.RECOVERY_CAUSE_NONE);
+
+ // Only the second document should be retrievable from another instance.
+ assertThrows(AppSearchException.class, () -> appSearchImpl2.getDocument("package",
+ "database",
+ "namespace1",
+ "id1",
+ Collections.emptyMap()));
+ getResult = appSearchImpl2.getDocument("package", "database", "namespace1",
+ "id2",
+ Collections.emptyMap());
+ assertThat(getResult).isEqualTo(document2);
+ appSearchImpl2.close();
+ }
+
+ @Test
+ public void testDeleteByQueryPersistsWithoutRecoveryWithRecoveryProofFlush() throws Exception {
+ List<AppSearchSchema> schemas =
+ Collections.singletonList(new AppSearchSchema.Builder("type").build());
+ InternalSetSchemaResponse internalSetSchemaResponse = mAppSearchImpl.setSchema(
+ "package",
+ "database",
+ schemas,
+ /*visibilityConfigs=*/ Collections.emptyList(),
+ /*forceOverride=*/ false,
+ /*version=*/ 0,
+ /* setSchemaStatsBuilder= */ null);
+ assertThat(internalSetSchemaResponse.isSuccess()).isTrue();
+
+ // Add two documents and persist them.
+ GenericDocument document1 =
+ new GenericDocument.Builder<>("namespace1", "id1", "type").build();
+ mAppSearchImpl.putDocument(
+ "package",
+ "database",
+ document1,
+ /*sendChangeNotifications=*/ false,
+ /*logger=*/ null);
+ GenericDocument document2 =
+ new GenericDocument.Builder<>("namespace2", "id2", "type").build();
+ mAppSearchImpl.putDocument(
+ "package",
+ "database",
+ document2,
+ /*sendChangeNotifications=*/ false,
+ /*logger=*/ null);
+ mAppSearchImpl.persistToDisk(PersistType.Code.RECOVERY_PROOF);
+
+ GenericDocument getResult = mAppSearchImpl.getDocument("package", "database", "namespace1",
+ "id1",
+ Collections.emptyMap());
+ assertThat(getResult).isEqualTo(document1);
+ getResult = mAppSearchImpl.getDocument("package", "database", "namespace2",
+ "id2",
+ Collections.emptyMap());
+ assertThat(getResult).isEqualTo(document2);
+
+ // Delete the first document
+ mAppSearchImpl.removeByQuery("package", "database", "",
+ new SearchSpec.Builder().addFilterNamespaces("namespace1").setTermMatch(
+ SearchSpec.TERM_MATCH_EXACT_ONLY).build(), /*statsBuilder=*/ null);
+ mAppSearchImpl.persistToDisk(PersistType.Code.RECOVERY_PROOF);
+ assertThrows(AppSearchException.class, () -> mAppSearchImpl.getDocument("package",
+ "database",
+ "namespace1",
+ "id1",
+ Collections.emptyMap()));
+ getResult = mAppSearchImpl.getDocument("package", "database", "namespace2",
+ "id2",
+ Collections.emptyMap());
+ assertThat(getResult).isEqualTo(document2);
+
+ // Initialize a new instance of AppSearch to test initialization.
+ InitializeStats.Builder initStatsBuilder = new InitializeStats.Builder();
+ AppSearchImpl appSearchImpl2 = AppSearchImpl.create(
+ mAppSearchDir,
+ new AppSearchConfigImpl(
+ new UnlimitedLimitConfig(),
+ new LocalStorageIcingOptionsConfig()
+ ),
+ /*initStatsBuilder=*/initStatsBuilder,
+ /*visibilityChecker=*/ null,
+ /*revocableFileDescriptorStore=*/ null,
+ ALWAYS_OPTIMIZE);
+
+ // Initialization should NOT trigger a recovery.
+ InitializeStats initStats = initStatsBuilder.build();
+ assertThat(initStats.getDocumentStoreRecoveryCause())
+ .isEqualTo(InitializeStats.RECOVERY_CAUSE_NONE);
+ assertThat(initStats.getIndexRestorationCause())
+ .isEqualTo(InitializeStats.RECOVERY_CAUSE_NONE);
+
+ // Only the second document should be retrievable from another instance.
assertThrows(AppSearchException.class, () -> appSearchImpl2.getDocument("package",
"database",
"namespace1",
diff --git a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/LocalStorageTest.java b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/LocalStorageTest.java
index 333cea3..18f8abe 100644
--- a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/LocalStorageTest.java
+++ b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/LocalStorageTest.java
@@ -20,8 +20,11 @@
import static org.junit.Assert.assertThrows;
+import androidx.appsearch.exceptions.AppSearchException;
import androidx.test.core.app.ApplicationProvider;
+import com.google.android.icing.proto.PersistType;
+
import org.junit.Test;
import java.util.concurrent.Executor;
@@ -30,11 +33,15 @@
public class LocalStorageTest {
@Test
public void testSameInstance() throws Exception {
+ LocalStorage.resetInstance();
+
Executor executor = Executors.newCachedThreadPool();
LocalStorage b1 = LocalStorage.getOrCreateInstance(
- ApplicationProvider.getApplicationContext(), executor, /*logger=*/ null);
+ ApplicationProvider.getApplicationContext(), executor, /*logger=*/ null,
+ /* persistToDiskRecoveryProof=*/false);
LocalStorage b2 = LocalStorage.getOrCreateInstance(
- ApplicationProvider.getApplicationContext(), executor, /*logger=*/ null);
+ ApplicationProvider.getApplicationContext(), executor, /*logger=*/ null,
+ /* persistToDiskRecoveryProof=*/false);
assertThat(b1).isSameInstanceAs(b2);
}
@@ -88,4 +95,31 @@
"/testDatabaseNameStartWith").build());
assertThat(e).hasMessageThat().isEqualTo("Database name cannot contain '/'");
}
+
+ @Test
+ public void testLocalStorage_persistToDiskRecoveryProofTrue() throws AppSearchException {
+ LocalStorage.resetInstance();
+
+ LocalStorage.SearchContext searchContext =
+ new LocalStorage.SearchContext.Builder(
+ ApplicationProvider.getApplicationContext(),
+ /*databaseName=*/"dbName").setPersistToDiskRecoveryProof(true).build();
+
+ AppSearchConfig config = LocalStorage.getConfig(searchContext);
+ assertThat(config.getLightweightPersistType()).isEqualTo(PersistType.Code.RECOVERY_PROOF);
+ }
+
+ @Test
+ public void testLocalStorage_persistToDiskRecoveryProofFalse() throws AppSearchException {
+ LocalStorage.resetInstance();
+
+ // persistToDiskRecoveryProof is false by default.
+ LocalStorage.SearchContext searchContext =
+ new LocalStorage.SearchContext.Builder(
+ ApplicationProvider.getApplicationContext(),
+ /*databaseName=*/"dbName").build();
+
+ AppSearchConfig config = LocalStorage.getConfig(searchContext);
+ assertThat(config.getLightweightPersistType()).isEqualTo(PersistType.Code.LITE);
+ }
}
diff --git a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/GenericDocumentToProtoConverterTest.java b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/GenericDocumentToProtoConverterTest.java
index 94d80452..cfb30c8 100644
--- a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/GenericDocumentToProtoConverterTest.java
+++ b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/GenericDocumentToProtoConverterTest.java
@@ -436,14 +436,16 @@
new AppSearchConfigImpl(new UnlimitedLimitConfig(),
new LocalStorageIcingOptionsConfig(),
/* storeParentInfoAsSyntheticProperty= */ false,
- /* shouldRetrieveParentInfo= */ true));
+ /* shouldRetrieveParentInfo= */ true,
+ /* persistToDiskRecoveryProof=*/false));
GenericDocument actualDocWithParentAsSyntheticProperty =
GenericDocumentToProtoConverter.toGenericDocument(documentProto, PREFIX,
new SchemaCache(schemaMap),
new AppSearchConfigImpl(new UnlimitedLimitConfig(),
new LocalStorageIcingOptionsConfig(),
/* storeParentInfoAsSyntheticProperty= */ true,
- /* shouldRetrieveParentInfo= */ true));
+ /* shouldRetrieveParentInfo= */ true,
+ /* persistToDiskRecoveryProof=*/false));
assertThat(actualDocWithParentAsMetaField).isEqualTo(expectedDocWithParentAsMetaField);
assertThat(actualDocWithParentAsMetaField).isNotEqualTo(
@@ -505,14 +507,16 @@
new AppSearchConfigImpl(new UnlimitedLimitConfig(),
new LocalStorageIcingOptionsConfig(),
/* storeParentInfoAsSyntheticProperty= */ false,
- /* shouldRetrieveParentInfo= */ true));
+ /* shouldRetrieveParentInfo= */ true,
+ /* persistToDiskRecoveryProof=*/false));
GenericDocument actualDoc2 =
GenericDocumentToProtoConverter.toGenericDocument(documentProto, PREFIX,
new SchemaCache(schemaMap),
new AppSearchConfigImpl(new UnlimitedLimitConfig(),
new LocalStorageIcingOptionsConfig(),
/* storeParentInfoAsSyntheticProperty= */ true,
- /* shouldRetrieveParentInfo= */ true));
+ /* shouldRetrieveParentInfo= */ true,
+ /* persistToDiskRecoveryProof=*/false));
assertThat(actualDoc1).isEqualTo(expectedDoc);
assertThat(actualDoc2).isEqualTo(expectedDoc);
}
diff --git a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/SearchResultToProtoConverterTest.java b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/SearchResultToProtoConverterTest.java
index 1576821..fb950a0 100644
--- a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/SearchResultToProtoConverterTest.java
+++ b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/SearchResultToProtoConverterTest.java
@@ -59,7 +59,8 @@
new UnlimitedLimitConfig(),
new LocalStorageIcingOptionsConfig(),
/* storeParentInfoAsSyntheticProperty= */ false,
- /* shouldRetrieveParentInfo= */ true);
+ /* shouldRetrieveParentInfo= */ true,
+ /* persistToDiskRecoveryProof=*/false);
// Building the SearchResult received from query.
DocumentProto.Builder documentProtoBuilder = DocumentProto.newBuilder()
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/AppSearchConfig.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/AppSearchConfig.java
index 0932377..3b53f7e 100644
--- a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/AppSearchConfig.java
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/AppSearchConfig.java
@@ -19,6 +19,10 @@
import androidx.annotation.RestrictTo;
import androidx.appsearch.app.GenericDocument;
+import com.google.android.icing.proto.PersistType;
+
+import org.jspecify.annotations.NonNull;
+
/**
* An interface that wraps AppSearch configurations required to create {@link AppSearchImpl}.
*/
@@ -38,4 +42,10 @@
* {@link androidx.appsearch.flags.Flags#FLAG_ENABLE_SEARCH_RESULT_PARENT_TYPES} in on.
*/
boolean shouldRetrieveParentInfo();
+
+ /**
+ * Returns the {@code PersistType.Code} that should be used to persist common mutations such as
+ * PUTs or DELETEs.
+ */
+ PersistType. @NonNull Code getLightweightPersistType();
}
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/AppSearchConfigImpl.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/AppSearchConfigImpl.java
index 21142e0..1479d19 100644
--- a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/AppSearchConfigImpl.java
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/AppSearchConfigImpl.java
@@ -18,6 +18,8 @@
import androidx.annotation.RestrictTo;
+import com.google.android.icing.proto.PersistType;
+
import org.jspecify.annotations.NonNull;
/**
@@ -30,23 +32,27 @@
private final IcingOptionsConfig mIcingOptionsConfig;
private final boolean mStoreParentInfoAsSyntheticProperty;
private final boolean mShouldRetrieveParentInfo;
+ private final boolean mPersistToDiskRecoveryProof;
public AppSearchConfigImpl(@NonNull LimitConfig limitConfig,
@NonNull IcingOptionsConfig icingOptionsConfig) {
this(limitConfig,
icingOptionsConfig,
/* storeParentInfoAsSyntheticProperty= */ false,
- /* shouldRetrieveParentInfo= */ false);
+ /* shouldRetrieveParentInfo= */ false,
+ /* persistToDiskRecoveryProof= */false);
}
public AppSearchConfigImpl(@NonNull LimitConfig limitConfig,
@NonNull IcingOptionsConfig icingOptionsConfig,
boolean storeParentInfoAsSyntheticProperty,
- boolean shouldRetrieveParentInfo) {
+ boolean shouldRetrieveParentInfo,
+ boolean persistToDiskRecoveryProof) {
mLimitConfig = limitConfig;
mIcingOptionsConfig = icingOptionsConfig;
mStoreParentInfoAsSyntheticProperty = storeParentInfoAsSyntheticProperty;
mShouldRetrieveParentInfo = shouldRetrieveParentInfo;
+ mPersistToDiskRecoveryProof = persistToDiskRecoveryProof;
}
@Override
@@ -163,4 +169,10 @@
public long getOrphanBlobTimeToLiveMs() {
return mIcingOptionsConfig.getOrphanBlobTimeToLiveMs();
}
+
+ @Override
+ public PersistType. @NonNull Code getLightweightPersistType() {
+ return mPersistToDiskRecoveryProof ?
+ PersistType.Code.RECOVERY_PROOF : PersistType.Code.LITE;
+ }
}
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/AppSearchImpl.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/AppSearchImpl.java
index 6cb8f15..1dfb53a 100644
--- a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/AppSearchImpl.java
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/AppSearchImpl.java
@@ -503,6 +503,13 @@
}
/**
+ * Returns the instance of AppSearchConfig used by this instance of AppSearchImpl.
+ */
+ public @NonNull AppSearchConfig getConfig() {
+ return mConfig;
+ }
+
+ /**
* Updates the AppSearch schema for this app.
*
* <p>This method belongs to mutate group.
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/LocalStorage.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/LocalStorage.java
index 5938407..d3377ee 100644
--- a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/LocalStorage.java
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/LocalStorage.java
@@ -64,13 +64,16 @@
final String mDatabaseName;
final Executor mExecutor;
final @Nullable AppSearchLogger mLogger;
+ final boolean mPersistToDiskRecoveryProof;
SearchContext(@NonNull Context context, @NonNull String databaseName,
- @NonNull Executor executor, @Nullable AppSearchLogger logger) {
+ @NonNull Executor executor, @Nullable AppSearchLogger logger,
+ boolean persistToDiskRecoveryProof) {
mContext = Preconditions.checkNotNull(context);
mDatabaseName = Preconditions.checkNotNull(databaseName);
mExecutor = Preconditions.checkNotNull(executor);
mLogger = logger;
+ mPersistToDiskRecoveryProof = persistToDiskRecoveryProof;
}
/**
@@ -107,6 +110,7 @@
private final String mDatabaseName;
private Executor mExecutor;
private @Nullable AppSearchLogger mLogger;
+ private boolean mPersistToDiskRecoveryProof;
/**
* Creates a {@link SearchContext.Builder} instance.
@@ -158,12 +162,32 @@
return this;
}
+ /**
+ * Sets whether AppSearch should call persistToDisk LITE or persistToDisk RECOVERY_PROOF
+ * after mutations ({@link AppSearchSession#putAsync} and
+ * {@link AppSearchSession#removeAsync}). LITE guarantees no data loss on initialization
+ * but with a recovery. RECOVERY_PROOF will guarantee no data loss and no recovery.
+ *
+ * <p>Note: This api is only added to facilitate early opt-ins by clients. It will be
+ * deprecated and then deleted (with the new 'true' behavior enabled) once this change
+ * has had sufficient time to soak.
+ *
+ * @exportToFramework:hide
+ */
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ @NonNull
+ public Builder setPersistToDiskRecoveryProof(boolean persistToDiskRecoveryProof) {
+ mPersistToDiskRecoveryProof = persistToDiskRecoveryProof;
+ return this;
+ }
+
/** Builds a {@link SearchContext} instance. */
public @NonNull SearchContext build() {
if (mExecutor == null) {
mExecutor = EXECUTOR;
}
- return new SearchContext(mContext, mDatabaseName, mExecutor, mLogger);
+ return new SearchContext(
+ mContext, mDatabaseName, mExecutor, mLogger, mPersistToDiskRecoveryProof);
}
}
}
@@ -272,7 +296,7 @@
Preconditions.checkNotNull(context);
return FutureUtil.execute(context.mExecutor, () -> {
LocalStorage instance = getOrCreateInstance(context.mContext, context.mExecutor,
- context.mLogger);
+ context.mLogger, context.mPersistToDiskRecoveryProof);
return instance.doCreateSearchSession(context);
});
}
@@ -292,7 +316,7 @@
Preconditions.checkNotNull(context);
return FutureUtil.execute(context.mExecutor, () -> {
LocalStorage instance = getOrCreateInstance(context.mContext, context.mExecutor,
- context.mLogger);
+ context.mLogger, /*persistToDiskRecoveryProof=*/false);
return instance.doCreateGlobalSearchSession(context);
});
}
@@ -305,25 +329,41 @@
*/
@WorkerThread
@VisibleForTesting
- static @NonNull LocalStorage getOrCreateInstance(@NonNull Context context,
- @NonNull Executor executor, @Nullable AppSearchLogger logger)
+ static @NonNull LocalStorage getOrCreateInstance(
+ @NonNull Context context, @NonNull Executor executor,
+ @Nullable AppSearchLogger logger, boolean persistToDiskRecoveryProof)
throws AppSearchException {
Preconditions.checkNotNull(context);
if (sInstance == null) {
synchronized (LocalStorage.class) {
if (sInstance == null) {
- sInstance = new LocalStorage(context, executor, logger);
+ sInstance =
+ new LocalStorage(context, executor, logger, persistToDiskRecoveryProof);
}
}
}
return sInstance;
}
+ @VisibleForTesting
+ static @NonNull AppSearchConfig getConfig(@NonNull SearchContext context)
+ throws AppSearchException {
+ LocalStorage instance = getOrCreateInstance(context.mContext, context.mExecutor,
+ context.mLogger, context.mPersistToDiskRecoveryProof);
+ return instance.mAppSearchImpl.getConfig();
+ }
+
+ @VisibleForTesting
+ static void resetInstance() {
+ sInstance = null;
+ }
+
@WorkerThread
private LocalStorage(
@NonNull Context context,
@NonNull Executor executor,
- @Nullable AppSearchLogger logger)
+ @Nullable AppSearchLogger logger,
+ boolean persistToDiskRecoveryProof)
throws AppSearchException {
Preconditions.checkNotNull(context);
File icingDir = AppSearchEnvironmentFactory.getEnvironmentInstance()
@@ -342,7 +382,8 @@
new UnlimitedLimitConfig(),
new LocalStorageIcingOptionsConfig(),
/* storeParentInfoAsSyntheticProperty= */ false,
- /* shouldRetrieveParentInfo= */ true
+ /* shouldRetrieveParentInfo= */ true,
+ persistToDiskRecoveryProof
);
RevocableFileDescriptorStore revocableFileDescriptorStore = null;
if (Flags.enableBlobStore()) {
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/SearchSessionImpl.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/SearchSessionImpl.java
index 3d820bd..30d8161 100644
--- a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/SearchSessionImpl.java
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/SearchSessionImpl.java
@@ -376,7 +376,7 @@
}
// Now that the batch has been written. Persist the newly written data.
- mAppSearchImpl.persistToDisk(PersistType.Code.LITE);
+ mAppSearchImpl.persistToDisk(mAppSearchImpl.getConfig().getLightweightPersistType());
mIsMutated = true;
// Schedule a task to dispatch change notifications. See requirements for where the
@@ -605,7 +605,7 @@
}
}
// Now that the batch has been written. Persist the newly written data.
- mAppSearchImpl.persistToDisk(PersistType.Code.LITE);
+ mAppSearchImpl.persistToDisk(mAppSearchImpl.getConfig().getLightweightPersistType());
mIsMutated = true;
// Schedule a task to dispatch change notifications. See requirements for where the
// method is called documented in the method description.
@@ -636,7 +636,7 @@
mAppSearchImpl.removeByQuery(mPackageName, mDatabaseName, queryExpression,
searchSpec, removeStatsBuilder);
// Now that the batch has been written. Persist the newly written data.
- mAppSearchImpl.persistToDisk(PersistType.Code.LITE);
+ mAppSearchImpl.persistToDisk(mAppSearchImpl.getConfig().getLightweightPersistType());
mIsMutated = true;
// Schedule a task to dispatch change notifications. See requirements for where the
// method is called documented in the method description.
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStore.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStore.java
index 6d06eb5c..39e07df 100644
--- a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStore.java
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStore.java
@@ -234,7 +234,7 @@
prefixedVisibilityConfig);
}
// Now that the visibility document has been written. Persist the newly written data.
- mAppSearchImpl.persistToDisk(PersistType.Code.LITE);
+ mAppSearchImpl.persistToDisk(mAppSearchImpl.getConfig().getLightweightPersistType());
}
/**