From a53961f2df830debab899734603ee509a017c7e5 Mon Sep 17 00:00:00 2001 From: Krzysztof Borowy Date: Fri, 9 Apr 2021 09:53:16 +0200 Subject: [PATCH 1/3] force checkpoint on pie and up --- .../asyncstorage/AsyncLocalStorageUtil.java | 36 +++++++++++++++++++ .../asyncstorage/AsyncStoragePackage.java | 5 ++- 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/android/src/main/java/com/reactnativecommunity/asyncstorage/AsyncLocalStorageUtil.java b/android/src/main/java/com/reactnativecommunity/asyncstorage/AsyncLocalStorageUtil.java index fe29a269..53b8404b 100644 --- a/android/src/main/java/com/reactnativecommunity/asyncstorage/AsyncLocalStorageUtil.java +++ b/android/src/main/java/com/reactnativecommunity/asyncstorage/AsyncLocalStorageUtil.java @@ -9,13 +9,17 @@ import javax.annotation.Nullable; +import java.io.File; import java.util.Arrays; import java.util.Iterator; import android.content.ContentValues; +import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; +import android.os.Build; import android.text.TextUtils; +import android.util.Log; import com.facebook.react.bridge.ReadableArray; @@ -142,4 +146,36 @@ private static void deepMergeInto(JSONObject oldJSON, JSONObject newJSON) } } } + /** + * From Pie and up, Android started to use Write-ahead logging (WAL), instead of journal rollback + * for atomic commits and rollbacks. + * Basically, WAL does not write directly to the database file, rather to the supporting WAL file. + * Because of that, migration to the next storage might not be successful, because the content of + * RKStorage might be still in WAL file instead. Committing all data from WAL to db file is called + * a "checkpoint" and is done automatically (by default) when the WAL file reaches a threshold + * size of 1000 pages. + * More here: https://sqlite.org/wal.html + *

+ * This helper will force checkpoint on RKStorage, if Next storage file does not exists yet. + */ + public static void verifyAndForceSqliteCheckpoint(Context ctx) { + File nextStorageFile = ctx.getDatabasePath("AsyncStorage"); + File currentStorageFile = ctx.getDatabasePath(ReactDatabaseSupplier.DATABASE_NAME); + boolean isCheckpointRequired = Build.VERSION.SDK_INT >= Build.VERSION_CODES.P + && !nextStorageFile.exists() + && currentStorageFile.exists(); + if (!isCheckpointRequired) { + Log.i("AsyncStorage_Next", "SQLite checkpoint not required."); + return; + } + + try { + ReactDatabaseSupplier supplier = ReactDatabaseSupplier.getInstance(ctx); + supplier.get().execSQL("PRAGMA wal_checkpoint"); + supplier.closeDatabase(); + Log.i("AsyncStorage_Next", "Forcing SQLite checkpoint successful."); + } catch (Exception e) { + Log.w("AsyncStorage_Next", "Could not force checkpoint on RKStorage: " + e.getMessage()); + } + } } diff --git a/android/src/main/java/com/reactnativecommunity/asyncstorage/AsyncStoragePackage.java b/android/src/main/java/com/reactnativecommunity/asyncstorage/AsyncStoragePackage.java index c110122c..b315724f 100644 --- a/android/src/main/java/com/reactnativecommunity/asyncstorage/AsyncStoragePackage.java +++ b/android/src/main/java/com/reactnativecommunity/asyncstorage/AsyncStoragePackage.java @@ -1,6 +1,6 @@ /** * Copyright (c) Facebook, Inc. and its affiliates. - * + *

* This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ @@ -8,12 +8,14 @@ package com.reactnativecommunity.asyncstorage; import android.util.Log; + import com.facebook.react.ReactPackage; import com.facebook.react.bridge.JavaScriptModule; import com.facebook.react.bridge.NativeModule; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactContext; import com.facebook.react.uimanager.ViewManager; + import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -29,6 +31,7 @@ public List createNativeModules(ReactApplicationContext reactConte Class storageClass = Class.forName("com.reactnativecommunity.asyncstorage.next.StorageModule"); NativeModule inst = (NativeModule) storageClass.getDeclaredConstructor(new Class[]{ReactContext.class}).newInstance(reactContext); moduleList.add(inst); + AsyncLocalStorageUtil.verifyAndForceSqliteCheckpoint(reactContext); } catch (Exception e) { String message = "Something went wrong when initializing module:" + "\n" From 890799d880a7723c3fce897b0586ee8eaca4eab1 Mon Sep 17 00:00:00 2001 From: Krzysztof Borowy Date: Wed, 21 Apr 2021 11:05:31 +0200 Subject: [PATCH 2/3] improvements --- .../asyncstorage/AsyncLocalStorageUtil.java | 14 ++++++++------ .../asyncstorage/next/StorageSupplier.kt | 2 ++ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/android/src/main/java/com/reactnativecommunity/asyncstorage/AsyncLocalStorageUtil.java b/android/src/main/java/com/reactnativecommunity/asyncstorage/AsyncLocalStorageUtil.java index 53b8404b..ab7c26e2 100644 --- a/android/src/main/java/com/reactnativecommunity/asyncstorage/AsyncLocalStorageUtil.java +++ b/android/src/main/java/com/reactnativecommunity/asyncstorage/AsyncLocalStorageUtil.java @@ -155,15 +155,17 @@ private static void deepMergeInto(JSONObject oldJSON, JSONObject newJSON) * a "checkpoint" and is done automatically (by default) when the WAL file reaches a threshold * size of 1000 pages. * More here: https://sqlite.org/wal.html - *

+ * * This helper will force checkpoint on RKStorage, if Next storage file does not exists yet. */ public static void verifyAndForceSqliteCheckpoint(Context ctx) { + if(Build.VERSION.SDK_INT < Build.VERSION_CODES.P) { + Log.i("AsyncStorage_Next", "SQLite checkpoint not required on this API version."); + } + File nextStorageFile = ctx.getDatabasePath("AsyncStorage"); File currentStorageFile = ctx.getDatabasePath(ReactDatabaseSupplier.DATABASE_NAME); - boolean isCheckpointRequired = Build.VERSION.SDK_INT >= Build.VERSION_CODES.P - && !nextStorageFile.exists() - && currentStorageFile.exists(); + boolean isCheckpointRequired = !nextStorageFile.exists() && currentStorageFile.exists(); if (!isCheckpointRequired) { Log.i("AsyncStorage_Next", "SQLite checkpoint not required."); return; @@ -171,11 +173,11 @@ public static void verifyAndForceSqliteCheckpoint(Context ctx) { try { ReactDatabaseSupplier supplier = ReactDatabaseSupplier.getInstance(ctx); - supplier.get().execSQL("PRAGMA wal_checkpoint"); + supplier.get().rawQuery("PRAGMA wal_checkpoint", null).close(); supplier.closeDatabase(); Log.i("AsyncStorage_Next", "Forcing SQLite checkpoint successful."); } catch (Exception e) { - Log.w("AsyncStorage_Next", "Could not force checkpoint on RKStorage: " + e.getMessage()); + Log.w("AsyncStorage_Next", "Could not force checkpoint on RKStorage, the Next storage might not migrate the data properly: " + e.getMessage()); } } } diff --git a/android/src/main/java/com/reactnativecommunity/asyncstorage/next/StorageSupplier.kt b/android/src/main/java/com/reactnativecommunity/asyncstorage/next/StorageSupplier.kt index e4bdc866..e8a0c419 100644 --- a/android/src/main/java/com/reactnativecommunity/asyncstorage/next/StorageSupplier.kt +++ b/android/src/main/java/com/reactnativecommunity/asyncstorage/next/StorageSupplier.kt @@ -1,6 +1,7 @@ package com.reactnativecommunity.asyncstorage.next import android.content.Context +import android.util.Log import androidx.room.ColumnInfo import androidx.room.Dao import androidx.room.Database @@ -100,6 +101,7 @@ private object MIGRATION_TO_NEXT : Migration(1, 2) { FROM $oldTableName; """.trimIndent() ) + Log.e("AsyncStorage_Next", "Migration to Next storage completed.") } } From f5d14942aa899adbd413ca63e6b56c2b56d7cf2f Mon Sep 17 00:00:00 2001 From: Krzysztof Borowy Date: Fri, 23 Apr 2021 09:38:48 +0200 Subject: [PATCH 3/3] nits --- .../asyncstorage/AsyncLocalStorageUtil.java | 7 +------ .../asyncstorage/AsyncStoragePackage.java | 4 +--- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/android/src/main/java/com/reactnativecommunity/asyncstorage/AsyncLocalStorageUtil.java b/android/src/main/java/com/reactnativecommunity/asyncstorage/AsyncLocalStorageUtil.java index ab7c26e2..689901f3 100644 --- a/android/src/main/java/com/reactnativecommunity/asyncstorage/AsyncLocalStorageUtil.java +++ b/android/src/main/java/com/reactnativecommunity/asyncstorage/AsyncLocalStorageUtil.java @@ -8,11 +8,9 @@ package com.reactnativecommunity.asyncstorage; import javax.annotation.Nullable; - import java.io.File; import java.util.Arrays; import java.util.Iterator; - import android.content.ContentValues; import android.content.Context; import android.database.Cursor; @@ -20,12 +18,9 @@ import android.os.Build; import android.text.TextUtils; import android.util.Log; - import com.facebook.react.bridge.ReadableArray; - import org.json.JSONException; import org.json.JSONObject; - import static com.reactnativecommunity.asyncstorage.ReactDatabaseSupplier.KEY_COLUMN; import static com.reactnativecommunity.asyncstorage.ReactDatabaseSupplier.TABLE_CATALYST; import static com.reactnativecommunity.asyncstorage.ReactDatabaseSupplier.VALUE_COLUMN; @@ -159,7 +154,7 @@ private static void deepMergeInto(JSONObject oldJSON, JSONObject newJSON) * This helper will force checkpoint on RKStorage, if Next storage file does not exists yet. */ public static void verifyAndForceSqliteCheckpoint(Context ctx) { - if(Build.VERSION.SDK_INT < Build.VERSION_CODES.P) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) { Log.i("AsyncStorage_Next", "SQLite checkpoint not required on this API version."); } diff --git a/android/src/main/java/com/reactnativecommunity/asyncstorage/AsyncStoragePackage.java b/android/src/main/java/com/reactnativecommunity/asyncstorage/AsyncStoragePackage.java index b315724f..28a78408 100644 --- a/android/src/main/java/com/reactnativecommunity/asyncstorage/AsyncStoragePackage.java +++ b/android/src/main/java/com/reactnativecommunity/asyncstorage/AsyncStoragePackage.java @@ -1,6 +1,6 @@ /** * Copyright (c) Facebook, Inc. and its affiliates. - *

+ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ @@ -8,14 +8,12 @@ package com.reactnativecommunity.asyncstorage; import android.util.Log; - import com.facebook.react.ReactPackage; import com.facebook.react.bridge.JavaScriptModule; import com.facebook.react.bridge.NativeModule; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactContext; import com.facebook.react.uimanager.ViewManager; - import java.util.ArrayList; import java.util.Collections; import java.util.List;