amplify-android icon indicating copy to clipboard operation
amplify-android copied to clipboard

SQLiteDatabaseLockedException caused by TransferDBHelper.update$aws_storage_s3_release

Open yaroslav-v opened this issue 1 year ago • 2 comments

Before opening, please confirm:

Language and Async Model

Java

Amplify Categories

Storage

Gradle script dependencies

// Put output below this line
implementation "com.amplifyframework:core:2.14.10"
implementation "com.amplifyframework:aws-storage-s3:2.14.10"
implementation "com.amplifyframework:aws-auth-cognito:2.14.10"

Environment information

# Put output below this line
------------------------------------------------------------
Gradle 8.2
------------------------------------------------------------

Build time:   2023-06-30 18:02:30 UTC
Revision:     5f4a070a62a31a17438ac998c2b849f4f6892877

Kotlin:       1.8.20
Groovy:       3.0.17
Ant:          Apache Ant(TM) version 1.10.13 compiled on January 4 2023
JVM:          17.0.10 (MacPorts 17.0.10+11)
OS:           Mac OS X 14.3 x86_64

Please include any relevant guides or documentation you're referencing

No response

Describe the bug

Hi there!

We have a few reports (about 20 at the moment) on Firebase with this crash for a few latest releases of the library v2.14.x including the last one v2.14.10. The issue appears on Android 10-13, affected devices are mostly Motorola (around 80%) with some Samsung, Xiaomi etc.

The issue is quite rare and, unfortunately, there are no exact steps to reproduce it. I don't see any specific pattern in accompanying logs. Sometimes it happened after the app update, but in the minority of the cases.

Reproduction steps (if applicable)

No response

Code Snippet

// Initialization in Application class

Amplify.Logging.disable();

Amplify.addPlugin(new AWSCognitoAuthPlugin());
Amplify.addPlugin(new AWSS3StoragePlugin());

AmplifyConfiguration amplifyConfig = AmplifyConfiguration.builder(context)
		.devMenuEnabled(false)
		.build();
Amplify.configure(amplifyConfig, context);


// Uploading, somewhere in the app

DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.US);
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));

Map<String, String> metadata = new ArrayMap<>(4);
metadata.put("date", dateFormat.format(date));
metadata.put("session_uuid", uid);
metadata.put("token", authToken);
metadata.put("service", serviceName);

StorageUploadFileOptions options =
		StorageUploadFileOptions.builder()
				.accessLevel(StorageAccessLevel.PUBLIC)
				.contentType(mimeType)
				.metadata(metadata)
				.build();

final File file = new File(path);
if (file.canRead()) {
	Amplify.Storage.uploadFile(
			file.getName(),
			file,
			options,
			result -> {
				// write success logs
			},
			failure -> {
				// write failure logs
			}
	);
}

Log output

// Put your logs below this line
Fatal Exception: android.database.sqlite.SQLiteDatabaseLockedException
database is locked (code 5 SQLITE_BUSY)
android.database.sqlite.SQLiteConnection.nativeExecuteForChangedRowCount (SQLiteConnection.java)
android.database.sqlite.SQLiteConnection.executeForChangedRowCount (SQLiteConnection.java:900)
android.database.sqlite.SQLiteSession.executeForChangedRowCount (SQLiteSession.java:756)
android.database.sqlite.SQLiteStatement.executeUpdateDelete (SQLiteStatement.java:66)
android.database.sqlite.SQLiteDatabase.updateWithOnConflict (SQLiteDatabase.java:1817)
android.database.sqlite.SQLiteDatabase.update (SQLiteDatabase.java:1763)
com.amplifyframework.storage.s3.transfer.TransferDBHelper.update$aws_storage_s3_release (TransferDBHelper.kt:192)
com.amplifyframework.storage.s3.transfer.TransferDB.updateState (TransferDB.kt:215)
com.amplifyframework.storage.s3.transfer.TransferStatusUpdater.updateTransferState (TransferStatusUpdater.kt:106)
com.amplifyframework.storage.s3.transfer.TransferWorkerObserver.updateTransferState (TransferWorkerObserver.kt:166)
com.amplifyframework.storage.s3.transfer.TransferWorkerObserver.handleTransferStatusUpdate (TransferWorkerObserver.kt:116)
com.amplifyframework.storage.s3.transfer.TransferWorkerObserver.access$handleTransferStatusUpdate (TransferWorkerObserver.kt:35)
com.amplifyframework.storage.s3.transfer.TransferWorkerObserver$onChanged$1.invokeSuspend (TransferWorkerObserver.kt:95)
kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith (ContinuationImpl.kt:33)
kotlinx.coroutines.DispatchedTask.run (DispatchedTask.kt:108)
java.util.concurrent.ThreadPoolExecutor.runWorker (ThreadPoolExecutor.java:1167)
java.util.concurrent.ThreadPoolExecutor$Worker.run (ThreadPoolExecutor.java:641)
java.lang.Thread.run (Thread.java:923)

amplifyconfiguration.json

No response

GraphQL Schema

// Put your schema below this line


Additional information and screenshots

No response

yaroslav-v avatar Feb 19 '24 10:02 yaroslav-v

Hello thanks for reporting this issue. Someone from our team will take look at this.

ankpshah avatar Feb 27 '24 22:02 ankpshah

🤔 This error suggests that there may be concurrent SQliteOpenHelper instances access the database. We will need to look into how that might be possible.

mattcreaser avatar Mar 01 '24 19:03 mattcreaser

Hi guys!

I just want to give you some additional context. For the last 30 days this issue was registered for 9 users in Firebase.

The issue appears on Android 7-13, affected devices are Samsung (S10, A13, Galaxy A72), Sony (Xperia 8), Motorola (Moto G Pure), Xiaomi (Redmi 5), Oppo, LG.

The stack trace is the same for all cases, except for some cases the cause is "database is locked (code 5 SQLITE_BUSY)" and for others it's "database is locked (code 5 SQLITE_BUSY[5])".

AWS Amplify v2.14.11

yaroslav-v avatar Apr 08 '24 09:04 yaroslav-v

Thanks for the update @yaroslav-v.

I've spent a little while looking at this and I'm not quite sure how this error is occurring. Although the TransferDB.getInstance function is not thread-safe, we still should not be creating multiple instances as it's only created once per TransferManager, which is once per plugin.

@yaroslav-v just for confirmation: you're not creating multiple instances of AWSS3StoragePlugin, right? One way this can happen is if your application manifest has any android:process tags to make different components run in their own processes.

Barring that, I think my plan for this issue will to be to clean up the creation of the TransferDB to make it thread safe and see if that makes any difference. I'm not expecting that to resolve the issue, but it can't hurt.

mattcreaser avatar Apr 19 '24 19:04 mattcreaser

@mattcreaser Hi! Thx for trying.

I've double checked and there are no android:process in the manifest and Amplify.addPlugin(new AWSS3StoragePlugin()); is called only from the application extension class.

yaroslav-v avatar Apr 22 '24 08:04 yaroslav-v

Oops after looking at it again this morning I realized I missed the obvious bug in the implementation of the singleton pattern for TransferDB. I'll make a fix today.

mattcreaser avatar Apr 23 '24 14:04 mattcreaser

This fix has been released in Amplify 2.15.2 🎉

Closing this issue, please feel free to open a new one if required.

mattcreaser avatar Apr 25 '24 18:04 mattcreaser

This issue is now closed. Comments on closed issues are hard for our team to see. If you need more assistance, please open a new issue that references this one.

github-actions[bot] avatar Apr 25 '24 18:04 github-actions[bot]