Last active
October 20, 2016 20:22
-
-
Save eiriklv/60bdf67baa57f76e5a65007c84173853 to your computer and use it in GitHub Desktop.
Corrective saga
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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