Skip to content

Commit 9d81477

Browse files
authored
Merge pull request #198 from backtrace-labs/fix/refactor-db-retry-timer
Improve Backtrace DB Timer Reliability by Switching to schedule() and Enhancing Null-Safety
2 parents 1602401 + ba7bd22 commit 9d81477

File tree

1 file changed

+50
-67
lines changed

1 file changed

+50
-67
lines changed

backtrace-library/src/main/java/backtraceio/library/BacktraceDatabase.java

Lines changed: 50 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@
4343
*/
4444
public class BacktraceDatabase implements Database {
4545

46-
private static boolean _timerBackgroundWork = false;
4746
private static Timer _timer;
4847
private transient final String LOG_TAG = BacktraceDatabase.class.getSimpleName();
4948
private Api BacktraceApi;
@@ -284,83 +283,73 @@ public BacktraceDatabaseSettings getSettings() {
284283

285284
private void setupTimer() {
286285
_timer = new Timer();
287-
_timer.scheduleAtFixedRate(new TimerTask() {
286+
_timer.schedule(new TimerTask() {
288287
@Override
289288
public void run() {
290289
String dateTimeNow = Calendar.getInstance().getTime().toString();
291-
BacktraceLogger.d(LOG_TAG, "Timer - " + dateTimeNow);
290+
BacktraceLogger.d(LOG_TAG, "Backtrace DB Timer - " + dateTimeNow);
292291
if (backtraceDatabaseContext == null) {
293-
BacktraceLogger.w(LOG_TAG, "Timer - database context is null: " +
292+
BacktraceLogger.w(LOG_TAG, "Backtrace DB Timer - database context is null: " +
294293
dateTimeNow);
295294
return;
296295
}
297296

298297
if (backtraceDatabaseContext.isEmpty()) {
299-
BacktraceLogger.d(LOG_TAG, "Timer - database is empty (no records): " +
298+
BacktraceLogger.d(LOG_TAG, "Backtrace DB Timer - database is empty (no records): " +
300299
dateTimeNow);
301300
return;
302301
}
303302

304-
if (_timerBackgroundWork) {
305-
BacktraceLogger.d(LOG_TAG, "Timer - another timer works now: " + dateTimeNow);
306-
return;
307-
}
308-
309-
BacktraceLogger.d(LOG_TAG, "Timer - continue working: " + dateTimeNow);
310-
_timerBackgroundWork = true;
311-
_timer.cancel();
312-
_timer.purge();
313-
_timer = null;
314-
315-
BacktraceDatabaseRecord record = backtraceDatabaseContext.first();
316-
while (record != null) {
317-
final CountDownLatch threadWaiter = new CountDownLatch(1);
318-
BacktraceData backtraceData = record.getBacktraceData();
319-
if (backtraceData == null || backtraceData.getReport() == null) {
320-
BacktraceLogger.d(LOG_TAG, "Timer - backtrace data or report is null - " +
321-
"deleting record");
322-
delete(record);
323-
} else {
324-
final BacktraceDatabaseRecord currentRecord = record;
325-
BacktraceApi.send(backtraceData, new OnServerResponseEventListener() {
326-
@Override
327-
public void onEvent(BacktraceResult backtraceResult) {
328-
if (backtraceResult.status == BacktraceResultStatus.Ok) {
329-
BacktraceLogger.d(LOG_TAG, "Timer - deleting record");
330-
delete(currentRecord);
331-
} else {
332-
BacktraceLogger.d(LOG_TAG, "Timer - closing record");
333-
currentRecord.close();
334-
// backtraceDatabaseContext.incrementBatchRetry(); TODO: consider another way to remove some records after few retries
303+
try {
304+
BacktraceDatabaseRecord record = backtraceDatabaseContext.first();
305+
while (record != null) {
306+
final CountDownLatch threadWaiter = new CountDownLatch(1);
307+
BacktraceData backtraceData = record.getBacktraceData();
308+
if (backtraceData == null || backtraceData.getReport() == null) {
309+
BacktraceLogger.d(LOG_TAG, "Backtrace DB Timer - backtrace data or report is null - " +
310+
"deleting record");
311+
delete(record);
312+
} else {
313+
final BacktraceDatabaseRecord currentRecord = record;
314+
BacktraceApi.send(backtraceData, new OnServerResponseEventListener() {
315+
@Override
316+
public void onEvent(BacktraceResult backtraceResult) {
317+
if (backtraceResult.status == BacktraceResultStatus.Ok) {
318+
BacktraceLogger.d(LOG_TAG, "Backtrace DB Timer - deleting record");
319+
delete(currentRecord);
320+
} else {
321+
BacktraceLogger.d(LOG_TAG, "Backtrace DB Timer - closing record");
322+
currentRecord.close();
323+
// backtraceDatabaseContext.incrementBatchRetry(); TODO: consider another way to remove some records after few retries
324+
}
325+
threadWaiter.countDown();
335326
}
336-
threadWaiter.countDown();
327+
});
328+
try {
329+
threadWaiter.await();
330+
} catch (Exception ex) {
331+
BacktraceLogger.e(LOG_TAG,
332+
"Error during waiting for result in Backtrace DB Timer", ex
333+
);
334+
}
335+
if (currentRecord.valid() && !currentRecord.locked) {
336+
BacktraceLogger.d(LOG_TAG, "Backtrace DB Timer - record is valid and unlocked");
337+
break;
337338
}
338-
});
339-
try {
340-
threadWaiter.await();
341-
} catch (Exception ex) {
342-
BacktraceLogger.e(LOG_TAG,
343-
"Error during waiting for result in Timer", ex
344-
);
345-
}
346-
if (currentRecord.valid() && !currentRecord.locked) {
347-
BacktraceLogger.d(LOG_TAG, "Timer - record is valid and unlocked");
348-
break;
349339
}
340+
record = backtraceDatabaseContext.first();
350341
}
351-
record = backtraceDatabaseContext.first();
352342
}
353-
BacktraceLogger.d(LOG_TAG, "Setup new timer");
354-
_timerBackgroundWork = false;
355-
setupTimer();
343+
catch (Exception e) {
344+
BacktraceLogger.e(LOG_TAG, "Exception in Backtrace DB timer", e);
345+
}
356346
}
357-
}, databaseSettings.getRetryInterval() * 1000, databaseSettings.getRetryInterval() * 1000);
347+
}, databaseSettings.getRetryInterval() * 1000L, databaseSettings.getRetryInterval() * 1000L);
358348
}
359349

360350
public void flush() {
361351
if (this.BacktraceApi == null) {
362-
throw new IllegalArgumentException("BacktraceApi is required " +
363-
"if you want to use Flush method");
352+
throw new IllegalArgumentException("BacktraceApi is required " + "if you want to use Flush method");
364353
}
365354

366355
BacktraceDatabaseRecord record = backtraceDatabaseContext.first();
@@ -396,13 +385,11 @@ public boolean validConsistency() {
396385
return backtraceDatabaseFileContext.validFileConsistency();
397386
}
398387

399-
public BacktraceDatabaseRecord add(BacktraceReport backtraceReport, Map<String, Object>
400-
attributes) {
388+
public BacktraceDatabaseRecord add(BacktraceReport backtraceReport, Map<String, Object> attributes) {
401389
return add(backtraceReport, attributes, false);
402390
}
403391

404-
public BacktraceDatabaseRecord add(BacktraceReport backtraceReport, Map<String, Object>
405-
attributes, boolean isProguardEnabled) {
392+
public BacktraceDatabaseRecord add(BacktraceReport backtraceReport, Map<String, Object> attributes, boolean isProguardEnabled) {
406393
if (!this._enable || backtraceReport == null) {
407394
return null;
408395
}
@@ -446,8 +433,7 @@ private void loadReports() {
446433

447434
final long endLoadingReportsTime = System.currentTimeMillis();
448435

449-
BacktraceLogger.d(LOG_TAG, "Loading " + backtraceDatabaseContext.count() +
450-
" reports took " + (endLoadingReportsTime - startLoadingReportsTime) + " milliseconds");
436+
BacktraceLogger.d(LOG_TAG, "Loading " + backtraceDatabaseContext.count() + " reports took " + (endLoadingReportsTime - startLoadingReportsTime) + " milliseconds");
451437
}
452438

453439
private void loadReportsToDbContext() {
@@ -482,19 +468,16 @@ private boolean validateDatabaseSize() {
482468
// Check how many records are stored in database
483469
// Remove in case when we want to store one more than expected number
484470
// If record count == 0 then we ignore this condition
485-
if (backtraceDatabaseContext.count() + 1 > databaseSettings.getMaxRecordCount() &&
486-
databaseSettings.getMaxRecordCount() != 0) {
471+
if (backtraceDatabaseContext.count() + 1 > databaseSettings.getMaxRecordCount() && databaseSettings.getMaxRecordCount() != 0) {
487472
if (!backtraceDatabaseContext.removeOldestRecord()) {
488473
BacktraceLogger.e(LOG_TAG, "Can't remove last record. Database size is invalid");
489474
return false;
490475
}
491476
}
492477

493-
if (databaseSettings.getMaxDatabaseSize() != 0 && backtraceDatabaseContext
494-
.getDatabaseSize() > databaseSettings.getMaxDatabaseSize()) {
478+
if (databaseSettings.getMaxDatabaseSize() != 0 && backtraceDatabaseContext.getDatabaseSize() > databaseSettings.getMaxDatabaseSize()) {
495479
int deletePolicyRetry = 5;
496-
while (backtraceDatabaseContext.getDatabaseSize() > databaseSettings
497-
.getMaxDatabaseSize()) {
480+
while (backtraceDatabaseContext.getDatabaseSize() > databaseSettings.getMaxDatabaseSize()) {
498481
backtraceDatabaseContext.removeOldestRecord();
499482
deletePolicyRetry--; // avoid infinity loop
500483
if (deletePolicyRetry == 0) {

0 commit comments

Comments
 (0)