Skip to content

Instantly share code, notes, and snippets.

@eiriklv
Last active October 20, 2016 20:22
Show Gist options
  • Save eiriklv/60bdf67baa57f76e5a65007c84173853 to your computer and use it in GitHub Desktop.
Save eiriklv/60bdf67baa57f76e5a65007c84173853 to your computer and use it in GitHub Desktop.
Corrective saga
// function that creates an async operation that can either fail or succeed based on the argument (bool)
const createAsyncOperation = (opName) => (succeeds) => !succeeds ? Promise.resolve(opName + ' success') : Promise.reject(new Error(opName + ' failure'));
// corrective operations are operations that
// sematically undo the original operation
//
// i.e - you cannot unsend an email or delete the booking at an hotel,
// but you can send an apology email and cancel a reservation
//
// to enable (unlimited) of both operations and corrective operations
// every operation and their counter should(must) be idempotent
const operation1 = createOperation('operation1');
const correctiveOperation1 = createOperation('corrective operation1');
const operation2 = createOperation('operation2');
const correctiveOperation2 = createOperation('corrective operation2');
const operation3 = createOperation('operation3');
const correctiveOperation3 = createOperation('corrective operation3');
const operation4 = createOperation('operation4');
const correctiveOperation4 = createOperation('corrective operation4');
const operation5 = createOperation('operation5');
const correctiveOperation5 = createOperation('corrective operation5');
// function that takes a promise creating function
// and wraps it so that it is retried until it succeeds
function retryUntilSuccess(promiseReturningFunction) {
return promiseReturningFunction()
.catch(error => {
console.log('got error:', error);
console.log('retrying..');
// this should be tapered off to retry at
// increasing intervals to avoid hammering
return retryUntilSuccess(promiseReturningFunction);
})
}
// a subSaga that corrects itself if it fails
function* subSaga(action) {
const correctiveOperationsNeeded = [];
try {
yield operation1(true);
correctiveOperationsNeeded.push(correctiveOperation1);
yield operation2(true);
correctiveOperationsNeeded.push(correctiveOperation2);
yield operation3(true);
correctiveOperationsNeeded.push(correctiveOperation3);
yield operation4(true);
correctiveOperationsNeeded.push(correctiveOperation4);
yield operation5(true);
correctiveOperationsNeeded.push(correctiveOperation5);
} catch (error) {
// run all corrective (counter)operations if something fails
// NOTE: these are retried until every one of them succeeds
yield Promise.all(correctiveOperationsNeeded.map(operation => retryUntilSuccess(operation.bind(null, true)));
} finally {
// retry the whole saga
return yield* subSaga(action);
}
}
// a main saga that delegates something to a sub-saga
function* mainSaga() {
while (true) {
const action = yield take(START_MULTIPART_TRANSACTION);
yield* subSaga(action);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment