syncbase.js: runInBatch retries and tweaks for consistency
Change-Id: I530863702ae876882296d49c9cd34e43f79bca64
diff --git a/src/nosql/batch.js b/src/nosql/batch.js
index cd5fa36..157a21d 100644
--- a/src/nosql/batch.js
+++ b/src/nosql/batch.js
@@ -8,7 +8,7 @@
* @summary
* runInBatch runs a function with a newly created batch. If the function
* errors, the batch is aborted. If the function succeeds, the batch is
- * committed. If an error occurs during commit, then the batch is aborted.
+ * committed.
*
* @param {module:vanadium.context.Context} ctx Vanadium context.
* @param {module:syncbase.database.Database} db Database.
@@ -17,41 +17,39 @@
* batch.
* @param {module:vanadium~voidCb} cb Callback that will be called after the
* batch has been committed or aborted.
- *
- * TODO(nlacasse): Add retry loop.
*/
function runInBatch(ctx, db, opts, fn, cb) {
- db.beginBatch(ctx, opts, function(err, batchDb) {
- if (err) {
- return cb(err);
- }
-
- function onError(err) {
- batchDb.abort(ctx, function() {
- cb(err);
- });
- }
-
- function onSuccess() {
- // TODO(nlacasse): Commit() can fail for a number of reasons, e.g. RPC
- // failure or ErrConcurrentTransaction. Depending on the cause of
- // failure, it may be desirable to retry the Commit() and/or to call
- // Abort(). For now, we always abort on a failed commit.
- batchDb.commit(ctx, function(commitErr) {
- if (commitErr) {
- return onError(commitErr);
- }
- return cb(null);
- });
- }
-
- fn(batchDb, function(err) {
+ function attempt(cb) {
+ db.beginBatch(ctx, opts, function(err, batchDb) {
if (err) {
- return onError(err);
+ return cb(err);
}
- onSuccess();
+ fn(batchDb, function(err) {
+ if (err) {
+ return batchDb.abort(ctx, function() {
+ return cb(err); // return fn error, not abort error
+ });
+ }
+ // TODO(sadovsky): commit() can fail for a number of reasons, e.g. RPC
+ // failure or ErrConcurrentTransaction. Depending on the cause of
+ // failure, it may be desirable to retry the commit() and/or to call
+ // abort().
+ batchDb.commit(ctx, cb);
+ });
});
- });
+ }
+
+ function retryLoop(i) {
+ attempt(function(err) {
+ if (err && i < 2) {
+ retryLoop(i + 1);
+ } else {
+ cb(err);
+ }
+ });
+ }
+
+ retryLoop(0);
}
/**
diff --git a/test/integration/test-run-in-batch.js b/test/integration/test-run-in-batch.js
index 84011f3..cc29d5a 100644
--- a/test/integration/test-run-in-batch.js
+++ b/test/integration/test-run-in-batch.js
@@ -77,7 +77,7 @@
});
});
-test('runInBatch aborts if commit fails', function(t) {
+test('runInBatch does not abort if commit fails', function(t) {
var ctx = {};
var db = new MockDb(true);
@@ -90,7 +90,7 @@
t.ok(db.batchDb, 'batch db is created');
t.ok(db.batchDb.commitCalled, 'batchDb.commit() was called');
- t.ok(db.batchDb.abortCalled, 'batchDb.abort() was called');
+ t.notok(db.batchDb.abortCalled, 'batchDb.abort() was not called');
t.end();
});