Skip to content

Commit 37a250a

Browse files
Fixes 4344 - Duplicate Uploads (commons-app#4442)
* Fixes 4344 - Update the retention policy of the Work Manager to ExistingWorkPolicy.APPEND_OR_REPLACE- which would append the new work to the end of existing one. This helps remove the while loop in UploadWorker which was meant to handle the cases where a new worker would be created for retries. The while loop seemed to have race conditions uploading duplicate entries. * Update states to IN_PROGRESS before uploads are processed
1 parent 458aa25 commit 37a250a

File tree

3 files changed

+59
-56
lines changed

3 files changed

+59
-56
lines changed

app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,7 @@ public void toggleLimitedConnectionMode() {
318318
} else {
319319
WorkManager.getInstance(getApplicationContext()).enqueueUniqueWork(
320320
UploadWorker.class.getSimpleName(),
321-
ExistingWorkPolicy.KEEP, OneTimeWorkRequest.from(UploadWorker.class));
321+
ExistingWorkPolicy.APPEND_OR_REPLACE, OneTimeWorkRequest.from(UploadWorker.class));
322322

323323
viewUtilWrapper
324324
.showShortToast(getBaseContext(), getString(R.string.limited_connection_disabled));

app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,7 @@ public void updateTopCardTitle() {
289289
public void makeUploadRequest() {
290290
WorkManager.getInstance(getApplicationContext()).enqueueUniqueWork(
291291
UploadWorker.class.getSimpleName(),
292-
ExistingWorkPolicy.KEEP, OneTimeWorkRequest.from(UploadWorker.class));
292+
ExistingWorkPolicy.APPEND_OR_REPLACE, OneTimeWorkRequest.from(UploadWorker.class));
293293
}
294294

295295
@Override

app/src/main/java/fr/free/nrw/commons/upload/worker/UploadWorker.kt

+57-54
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import androidx.core.app.NotificationCompat
77
import androidx.core.app.NotificationManagerCompat
88
import androidx.work.CoroutineWorker
99
import androidx.work.WorkerParameters
10+
import com.google.gson.Gson
1011
import com.mapbox.mapboxsdk.plugins.localization.BuildConfig
1112
import dagger.android.ContributesAndroidInjector
1213
import fr.free.nrw.commons.CommonsApplication
@@ -146,61 +147,60 @@ class UploadWorker(var appContext: Context, workerParams: WorkerParameters) :
146147
CommonsApplication.NOTIFICATION_CHANNEL_ID_ALL
147148
)!!
148149
withContext(Dispatchers.IO) {
149-
//Doing this so that retry requests do not create new work requests and while a work is
150-
// already running, all the requests should go through this, so kind of a queue
151-
while (contributionDao.getContribution(statesToProcess)
152-
.blockingGet().isNotEmpty()
153-
) {
154-
val queuedContributions = contributionDao.getContribution(statesToProcess)
155-
.blockingGet()
156-
//Showing initial notification for the number of uploads being processed
157-
158-
processingUploads.setContentTitle(appContext.getString(R.string.starting_uploads))
159-
processingUploads.setContentText(
160-
appContext.resources.getQuantityString(
161-
R.plurals.starting_multiple_uploads,
162-
queuedContributions.size,
163-
queuedContributions.size
164-
)
150+
val queuedContributions = contributionDao.getContribution(statesToProcess)
151+
.blockingGet()
152+
//Showing initial notification for the number of uploads being processed
153+
154+
Timber.e("Queued Contributions: "+ queuedContributions.size)
155+
156+
processingUploads.setContentTitle(appContext.getString(R.string.starting_uploads))
157+
processingUploads.setContentText(
158+
appContext.resources.getQuantityString(
159+
R.plurals.starting_multiple_uploads,
160+
queuedContributions.size,
161+
queuedContributions.size
165162
)
166-
notificationManager?.notify(
167-
PROCESSING_UPLOADS_NOTIFICATION_TAG,
168-
PROCESSING_UPLOADS_NOTIFICATION_ID,
169-
processingUploads.build()
170-
)
171-
172-
queuedContributions.asFlow().map { contribution ->
173-
/**
174-
* If the limited connection mode is on, lets iterate through the queued
175-
* contributions
176-
* and set the state as STATE_QUEUED_LIMITED_CONNECTION_MODE ,
177-
* otherwise proceed with the upload
178-
*/
179-
if(isLimitedConnectionModeEnabled()){
180-
if (contribution.state == Contribution.STATE_QUEUED) {
181-
contribution.state = Contribution.STATE_QUEUED_LIMITED_CONNECTION_MODE
182-
contributionDao.save(contribution)
183-
}
184-
} else {
185-
contribution.transferred = 0
186-
contribution.state = Contribution.STATE_IN_PROGRESS
187-
contributionDao.save(contribution)
188-
uploadContribution(contribution = contribution)
189-
}
190-
}.collect()
163+
)
164+
notificationManager?.notify(
165+
PROCESSING_UPLOADS_NOTIFICATION_TAG,
166+
PROCESSING_UPLOADS_NOTIFICATION_ID,
167+
processingUploads.build()
168+
)
191169

192-
//Dismiss the global notification
193-
notificationManager?.cancel(
194-
PROCESSING_UPLOADS_NOTIFICATION_TAG,
195-
PROCESSING_UPLOADS_NOTIFICATION_ID
196-
)
170+
/**
171+
* To avoid race condition when multiple of these workers are working, assign this state
172+
so that the next one does not process these contribution again
173+
*/
174+
queuedContributions.forEach {
175+
it.state=Contribution.STATE_IN_PROGRESS
176+
contributionDao.saveSynchronous(it)
177+
}
197178

198-
//No need to keep looking if the limited connection mode is on,
199-
//If the user toggles it, the work manager will be started again
200-
if(isLimitedConnectionModeEnabled()){
201-
break;
179+
queuedContributions.asFlow().map { contribution ->
180+
/**
181+
* If the limited connection mode is on, lets iterate through the queued
182+
* contributions
183+
* and set the state as STATE_QUEUED_LIMITED_CONNECTION_MODE ,
184+
* otherwise proceed with the upload
185+
*/
186+
if (isLimitedConnectionModeEnabled()) {
187+
if (contribution.state == Contribution.STATE_QUEUED) {
188+
contribution.state = Contribution.STATE_QUEUED_LIMITED_CONNECTION_MODE
189+
contributionDao.saveSynchronous(contribution)
190+
}
191+
} else {
192+
contribution.transferred = 0
193+
contribution.state = Contribution.STATE_IN_PROGRESS
194+
contributionDao.saveSynchronous(contribution)
195+
uploadContribution(contribution = contribution)
202196
}
203-
}
197+
}.collect()
198+
199+
//Dismiss the global notification
200+
notificationManager?.cancel(
201+
PROCESSING_UPLOADS_NOTIFICATION_TAG,
202+
PROCESSING_UPLOADS_NOTIFICATION_ID
203+
)
204204
}
205205
//TODO make this smart, think of handling retries in the future
206206
return Result.success()
@@ -307,6 +307,7 @@ class UploadWorker(var appContext: Context, workerParams: WorkerParameters) :
307307
Timber.e(exception)
308308
Timber.e("Upload from stash failed for contribution : $filename")
309309
showFailedNotification(contribution)
310+
contribution.state=Contribution.STATE_FAILED
310311
if (STASH_ERROR_CODES.contains(exception.message)) {
311312
clearChunks(contribution)
312313
}
@@ -315,26 +316,28 @@ class UploadWorker(var appContext: Context, workerParams: WorkerParameters) :
315316
StashUploadState.PAUSED -> {
316317
showPausedNotification(contribution)
317318
contribution.state = Contribution.STATE_PAUSED
318-
contributionDao.save(contribution).blockingGet()
319+
contributionDao.saveSynchronous(contribution)
319320
}
320321
else -> {
321322
Timber.e("""upload file to stash failed with status: ${stashUploadResult.state}""")
322323
showFailedNotification(contribution)
323324
contribution.state = Contribution.STATE_FAILED
324325
contribution.chunkInfo = null
325-
contributionDao.save(contribution).blockingAwait()
326+
contributionDao.saveSynchronous(contribution)
326327
}
327328
}
328329
}catch (exception: Exception){
329330
Timber.e(exception)
330331
Timber.e("Stash upload failed for contribution: $filename")
331332
showFailedNotification(contribution)
333+
contribution.state=Contribution.STATE_FAILED
334+
clearChunks(contribution)
332335
}
333336
}
334337

335338
private fun clearChunks(contribution: Contribution) {
336339
contribution.chunkInfo=null
337-
contributionDao.save(contribution).blockingAwait()
340+
contributionDao.saveSynchronous(contribution)
338341
}
339342

340343
/**

0 commit comments

Comments
 (0)