Skip to content

Commit

Permalink
fix(next storage): Force SQLite checkpoint on Pie and up (#582)
Browse files Browse the repository at this point in the history
  • Loading branch information
Krzysztof authored Apr 23, 2021
1 parent 8ff385a commit c2aa400
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,19 @@
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;
import android.database.sqlite.SQLiteDatabase;
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;
Expand Down Expand Up @@ -142,4 +141,38 @@ 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) {
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 = !nextStorageFile.exists() && currentStorageFile.exists();
if (!isCheckpointRequired) {
Log.i("AsyncStorage_Next", "SQLite checkpoint not required.");
return;
}

try {
ReactDatabaseSupplier supplier = ReactDatabaseSupplier.getInstance(ctx);
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, the Next storage might not migrate the data properly: " + e.getMessage());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public List<NativeModule> 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"
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -100,6 +101,7 @@ private object MIGRATION_TO_NEXT : Migration(1, 2) {
FROM $oldTableName;
""".trimIndent()
)
Log.e("AsyncStorage_Next", "Migration to Next storage completed.")
}
}

Expand Down

0 comments on commit c2aa400

Please sign in to comment.