mirror of
https://github.com/nextcloud/android.git
synced 2024-11-29 10:49:04 +03:00
Merge pull request #11124 from nextcloud/fix/upload-worker-infinite-loop
Fix infinite loop in upload worker
This commit is contained in:
commit
66c617828a
2 changed files with 123 additions and 91 deletions
|
@ -78,21 +78,24 @@ class FilesUploadWorker(
|
||||||
return Result.failure() // user account is needed
|
return Result.failure() // user account is needed
|
||||||
}
|
}
|
||||||
|
|
||||||
// get all pending uploads
|
/*
|
||||||
var currentAndPendingUploadsForAccount =
|
* As pages are retrieved by sorting uploads by ID, if new uploads are added while uploading the current ones,
|
||||||
uploadsStorageManager.getCurrentAndPendingUploadsForAccount(MAX_UPLOADS_QUERY, accountName)
|
* they will be present in the pages that follow.
|
||||||
while (currentAndPendingUploadsForAccount.isNotEmpty()) {
|
*/
|
||||||
Log_OC.d(TAG, "Handling ${currentAndPendingUploadsForAccount.size} uploads for account $accountName")
|
var currentPage = uploadsStorageManager.getCurrentAndPendingUploadsForAccountPageAscById(-1, accountName)
|
||||||
handlePendingUploads(currentAndPendingUploadsForAccount, accountName)
|
while (currentPage.isNotEmpty()) {
|
||||||
currentAndPendingUploadsForAccount =
|
Log_OC.d(TAG, "Handling ${currentPage.size} uploads for account $accountName")
|
||||||
uploadsStorageManager.getCurrentAndPendingUploadsForAccount(MAX_UPLOADS_QUERY, accountName)
|
val lastId = currentPage.last().uploadId
|
||||||
|
handlePendingUploads(currentPage, accountName)
|
||||||
|
currentPage =
|
||||||
|
uploadsStorageManager.getCurrentAndPendingUploadsForAccountPageAscById(lastId, accountName)
|
||||||
}
|
}
|
||||||
|
|
||||||
Log_OC.d(TAG, "No more pending uploads for account $accountName, stopping work")
|
Log_OC.d(TAG, "No more pending uploads for account $accountName, stopping work")
|
||||||
return Result.success()
|
return Result.success()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handlePendingUploads(uploads: Array<OCUpload>, accountName: String) {
|
private fun handlePendingUploads(uploads: List<OCUpload>, accountName: String) {
|
||||||
val user = userAccountManager.getUser(accountName)
|
val user = userAccountManager.getUser(accountName)
|
||||||
|
|
||||||
for (upload in uploads) {
|
for (upload in uploads) {
|
||||||
|
@ -240,7 +243,6 @@ class FilesUploadWorker(
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val TAG: String = FilesUploadWorker::class.java.simpleName
|
val TAG: String = FilesUploadWorker::class.java.simpleName
|
||||||
private const val MAX_UPLOADS_QUERY = 100
|
|
||||||
private const val FOREGROUND_SERVICE_ID: Int = 412
|
private const val FOREGROUND_SERVICE_ID: Int = 412
|
||||||
private const val MAX_PROGRESS: Int = 100
|
private const val MAX_PROGRESS: Int = 100
|
||||||
const val ACCOUNT = "data_account"
|
const val ACCOUNT = "data_account"
|
||||||
|
|
|
@ -56,8 +56,7 @@ import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.VisibleForTesting;
|
import androidx.annotation.VisibleForTesting;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Database helper for storing list of files to be uploaded, including status
|
* Database helper for storing list of files to be uploaded, including status information for each file.
|
||||||
* information for each file.
|
|
||||||
*/
|
*/
|
||||||
public class UploadsStorageManager extends Observable {
|
public class UploadsStorageManager extends Observable {
|
||||||
private static final String TAG = UploadsStorageManager.class.getSimpleName();
|
private static final String TAG = UploadsStorageManager.class.getSimpleName();
|
||||||
|
@ -65,6 +64,8 @@ public class UploadsStorageManager extends Observable {
|
||||||
private static final String AND = " AND ";
|
private static final String AND = " AND ";
|
||||||
private static final int SINGLE_RESULT = 1;
|
private static final int SINGLE_RESULT = 1;
|
||||||
|
|
||||||
|
private static final long QUERY_PAGE_SIZE = 100;
|
||||||
|
|
||||||
private final ContentResolver contentResolver;
|
private final ContentResolver contentResolver;
|
||||||
private final CurrentAccountProvider currentAccountProvider;
|
private final CurrentAccountProvider currentAccountProvider;
|
||||||
|
|
||||||
|
@ -364,61 +365,19 @@ public class UploadsStorageManager extends Observable {
|
||||||
}
|
}
|
||||||
|
|
||||||
private OCUpload[] getUploads(@Nullable String selection, @Nullable String... selectionArgs) {
|
private OCUpload[] getUploads(@Nullable String selection, @Nullable String... selectionArgs) {
|
||||||
return getUploads(0, selection, selectionArgs);
|
final List<OCUpload> uploads = new ArrayList<>();
|
||||||
}
|
|
||||||
|
|
||||||
private OCUpload[] getUploads(final int limit, @Nullable String selection, @Nullable String... selectionArgs) {
|
|
||||||
ArrayList<OCUpload> uploads = new ArrayList<>();
|
|
||||||
final long pageSize = 100;
|
|
||||||
long page = 0;
|
long page = 0;
|
||||||
long rowsRead;
|
long rowsRead;
|
||||||
long rowsTotal = 0;
|
long rowsTotal = 0;
|
||||||
long lastRowID = -1;
|
long lastRowID = -1;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
String pageSelection = selection;
|
final List<OCUpload> uploadsPage = getUploadPage(lastRowID, selection, selectionArgs);
|
||||||
String[] pageSelectionArgs = selectionArgs;
|
rowsRead = uploadsPage.size();
|
||||||
if (page > 0 && lastRowID >= 0) {
|
rowsTotal += rowsRead;
|
||||||
if (selection != null) {
|
if (!uploadsPage.isEmpty()) {
|
||||||
pageSelection = "(" + selection + ") AND _id < ?";
|
lastRowID = uploadsPage.get(uploadsPage.size() - 1).getUploadId();
|
||||||
} else {
|
|
||||||
pageSelection = "_id < ?";
|
|
||||||
}
|
}
|
||||||
if (selectionArgs != null) {
|
|
||||||
pageSelectionArgs = Arrays.copyOf(selectionArgs, selectionArgs.length + 1);
|
|
||||||
} else {
|
|
||||||
pageSelectionArgs = new String[1];
|
|
||||||
}
|
|
||||||
pageSelectionArgs[pageSelectionArgs.length - 1] = String.valueOf(lastRowID);
|
|
||||||
Log_OC.d(TAG, String.format(Locale.ENGLISH, "QUERY: %s ROWID: %d", pageSelection, lastRowID));
|
|
||||||
} else {
|
|
||||||
Log_OC.d(TAG, String.format(Locale.ENGLISH, "QUERY: %s ROWID: %d", selection, lastRowID));
|
|
||||||
}
|
|
||||||
rowsRead = 0;
|
|
||||||
|
|
||||||
Cursor c = getDB().query(
|
|
||||||
ProviderTableMeta.CONTENT_URI_UPLOADS,
|
|
||||||
null,
|
|
||||||
pageSelection,
|
|
||||||
pageSelectionArgs,
|
|
||||||
String.format(Locale.ENGLISH, "_id DESC LIMIT %d", pageSize)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (c != null) {
|
|
||||||
if (c.moveToFirst()) {
|
|
||||||
do {
|
|
||||||
rowsRead++;
|
|
||||||
rowsTotal++;
|
|
||||||
lastRowID = c.getLong(c.getColumnIndexOrThrow(ProviderTableMeta._ID));
|
|
||||||
OCUpload upload = createOCUploadFromCursor(c);
|
|
||||||
if (upload == null) {
|
|
||||||
Log_OC.e(TAG, "OCUpload could not be created from cursor");
|
|
||||||
} else {
|
|
||||||
uploads.add(upload);
|
|
||||||
}
|
|
||||||
} while (c.moveToNext() && !c.isAfterLast());
|
|
||||||
}
|
|
||||||
c.close();
|
|
||||||
Log_OC.v(TAG, String.format(Locale.ENGLISH,
|
Log_OC.v(TAG, String.format(Locale.ENGLISH,
|
||||||
"getUploads() got %d rows from page %d, %d rows total so far, last ID %d",
|
"getUploads() got %d rows from page %d, %d rows total so far, last ID %d",
|
||||||
rowsRead,
|
rowsRead,
|
||||||
|
@ -426,15 +385,10 @@ public class UploadsStorageManager extends Observable {
|
||||||
rowsTotal,
|
rowsTotal,
|
||||||
lastRowID
|
lastRowID
|
||||||
));
|
));
|
||||||
page += 1;
|
uploads.addAll(uploadsPage);
|
||||||
} else {
|
page++;
|
||||||
break;
|
} while (rowsRead > 0);
|
||||||
}
|
|
||||||
} while (rowsRead > 0 && (limit <= 0 || rowsRead < limit));
|
|
||||||
|
|
||||||
if (limit > 0 && uploads.size() > limit) {
|
|
||||||
uploads = new ArrayList<>(uploads.subList(0, limit));
|
|
||||||
}
|
|
||||||
|
|
||||||
Log_OC.v(TAG, String.format(Locale.ENGLISH,
|
Log_OC.v(TAG, String.format(Locale.ENGLISH,
|
||||||
"getUploads() returning %d (%d) rows after reading %d pages",
|
"getUploads() returning %d (%d) rows after reading %d pages",
|
||||||
|
@ -447,6 +401,67 @@ public class UploadsStorageManager extends Observable {
|
||||||
return uploads.toArray(new OCUpload[0]);
|
return uploads.toArray(new OCUpload[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private List<OCUpload> getUploadPage(final long afterId, @Nullable String selection, @Nullable String... selectionArgs) {
|
||||||
|
return getUploadPage(afterId, true, selection, selectionArgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private List<OCUpload> getUploadPage(final long afterId, final boolean descending, @Nullable String selection, @Nullable String... selectionArgs) {
|
||||||
|
List<OCUpload> uploads = new ArrayList<>();
|
||||||
|
String pageSelection = selection;
|
||||||
|
String[] pageSelectionArgs = selectionArgs;
|
||||||
|
|
||||||
|
String idComparator;
|
||||||
|
String sortDirection;
|
||||||
|
if (descending) {
|
||||||
|
sortDirection = "DESC";
|
||||||
|
idComparator = "<";
|
||||||
|
} else {
|
||||||
|
sortDirection = "ASC";
|
||||||
|
idComparator = ">";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (afterId >= 0) {
|
||||||
|
if (selection != null) {
|
||||||
|
pageSelection = "(" + selection + ") AND _id " + idComparator + " ?";
|
||||||
|
} else {
|
||||||
|
pageSelection = "_id " + idComparator + " ?";
|
||||||
|
}
|
||||||
|
if (selectionArgs != null) {
|
||||||
|
pageSelectionArgs = Arrays.copyOf(selectionArgs, selectionArgs.length + 1);
|
||||||
|
} else {
|
||||||
|
pageSelectionArgs = new String[1];
|
||||||
|
}
|
||||||
|
pageSelectionArgs[pageSelectionArgs.length - 1] = String.valueOf(afterId);
|
||||||
|
Log_OC.d(TAG, String.format(Locale.ENGLISH, "QUERY: %s ROWID: %d", pageSelection, afterId));
|
||||||
|
} else {
|
||||||
|
Log_OC.d(TAG, String.format(Locale.ENGLISH, "QUERY: %s ROWID: %d", selection, afterId));
|
||||||
|
}
|
||||||
|
Cursor c = getDB().query(
|
||||||
|
ProviderTableMeta.CONTENT_URI_UPLOADS,
|
||||||
|
null,
|
||||||
|
pageSelection,
|
||||||
|
pageSelectionArgs,
|
||||||
|
String.format(Locale.ENGLISH, "_id " + sortDirection + " LIMIT %d", QUERY_PAGE_SIZE)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (c != null) {
|
||||||
|
if (c.moveToFirst()) {
|
||||||
|
do {
|
||||||
|
OCUpload upload = createOCUploadFromCursor(c);
|
||||||
|
if (upload == null) {
|
||||||
|
Log_OC.e(TAG, "OCUpload could not be created from cursor");
|
||||||
|
} else {
|
||||||
|
uploads.add(upload);
|
||||||
|
}
|
||||||
|
} while (c.moveToNext() && !c.isAfterLast());
|
||||||
|
}
|
||||||
|
c.close();
|
||||||
|
}
|
||||||
|
return uploads;
|
||||||
|
}
|
||||||
|
|
||||||
private OCUpload createOCUploadFromCursor(Cursor c) {
|
private OCUpload createOCUploadFromCursor(Cursor c) {
|
||||||
OCUpload upload = null;
|
OCUpload upload = null;
|
||||||
if (c != null) {
|
if (c != null) {
|
||||||
|
@ -484,11 +499,7 @@ public class UploadsStorageManager extends Observable {
|
||||||
}
|
}
|
||||||
|
|
||||||
public OCUpload[] getCurrentAndPendingUploadsForAccount(final @NonNull String accountName) {
|
public OCUpload[] getCurrentAndPendingUploadsForAccount(final @NonNull String accountName) {
|
||||||
return getCurrentAndPendingUploadsForAccount(0, accountName);
|
return getUploads(ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_IN_PROGRESS.value +
|
||||||
}
|
|
||||||
|
|
||||||
public OCUpload[] getCurrentAndPendingUploadsForAccount(final int limit, final @NonNull String accountName) {
|
|
||||||
return getUploads(limit, ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_IN_PROGRESS.value +
|
|
||||||
" OR " + ProviderTableMeta.UPLOADS_LAST_RESULT +
|
" OR " + ProviderTableMeta.UPLOADS_LAST_RESULT +
|
||||||
"==" + UploadResult.DELAYED_FOR_WIFI.getValue() +
|
"==" + UploadResult.DELAYED_FOR_WIFI.getValue() +
|
||||||
" OR " + ProviderTableMeta.UPLOADS_LAST_RESULT +
|
" OR " + ProviderTableMeta.UPLOADS_LAST_RESULT +
|
||||||
|
@ -501,6 +512,25 @@ public class UploadsStorageManager extends Observable {
|
||||||
accountName);
|
accountName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a page of uploads after <code>afterId</code>, where uploads are sorted by ascending upload id.
|
||||||
|
* <p>
|
||||||
|
* If <code>afterId</code> is -1, returns the first page
|
||||||
|
*/
|
||||||
|
public List<OCUpload> getCurrentAndPendingUploadsForAccountPageAscById(final long afterId, final @NonNull String accountName) {
|
||||||
|
final String selection = ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_IN_PROGRESS.value +
|
||||||
|
" OR " + ProviderTableMeta.UPLOADS_LAST_RESULT +
|
||||||
|
"==" + UploadResult.DELAYED_FOR_WIFI.getValue() +
|
||||||
|
" OR " + ProviderTableMeta.UPLOADS_LAST_RESULT +
|
||||||
|
"==" + UploadResult.LOCK_FAILED.getValue() +
|
||||||
|
" OR " + ProviderTableMeta.UPLOADS_LAST_RESULT +
|
||||||
|
"==" + UploadResult.DELAYED_FOR_CHARGING.getValue() +
|
||||||
|
" OR " + ProviderTableMeta.UPLOADS_LAST_RESULT +
|
||||||
|
"==" + UploadResult.DELAYED_IN_POWER_SAVE_MODE.getValue() +
|
||||||
|
" AND " + ProviderTableMeta.UPLOADS_ACCOUNT_NAME + "== ?";
|
||||||
|
return getUploadPage(afterId, false, selection, accountName);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all failed uploads.
|
* Get all failed uploads.
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Reference in a new issue