Created
April 1, 2021 14:41
-
-
Save ivorpad/c263929228ffb12d367900d47f677122 to your computer and use it in GitHub Desktop.
Generated by XState Viz: https://xstate.js.org/viz
This file contains 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
let count = 0; | |
const functionThatCanFail = async () => { | |
const urls = [ | |
`https://api.github.com/users/mojombo`, | |
`https://api.github.com/users/ivorpad`, | |
`https://api.github.com/users/failtofetch`, | |
`https://api.github.com/users/davidkpiano` | |
]; | |
const user = await fetch(urls[count]); | |
count++; | |
if (count > 3) { | |
count = 0; | |
} | |
return user; | |
}; | |
// Health check will make a request with a small payload to see if the system is operational | |
// If the server responds with 200 then continue with the requests. | |
const healthCheck = () => { | |
return new Promise((resolve, reject) => { | |
setTimeout(() => { | |
resolve({ message: "system recovered" }); | |
}, 2000); | |
}); | |
}; | |
// Conditional to check if we should trip the circuit | |
const shouldTripCircuit = (ctx, _evt) => { | |
console.log((ctx.failure / ctx.fired) * 100 > ctx.errorThresholdPercentage); | |
return (ctx.failure / ctx.fired) * 100 > ctx.errorThresholdPercentage; | |
}; | |
const fetchMachine = Machine({ | |
id: "circuit-breaker", | |
initial: "closed", | |
context: { | |
message: null, | |
user: null, | |
failure: 0, | |
success: 0, | |
fired: 0, | |
timeout: 2000, | |
errorThresholdPercentage: 50, // Conditionally trip circuit if error threshold is more than 50% | |
openTimeout: 5000 | |
}, | |
states: { | |
closed: { | |
on: { | |
FETCH: [ | |
{ target: "open", cond: shouldTripCircuit }, | |
{ target: "loading" } | |
] | |
}, | |
after: { | |
3000: { | |
actions: "resetMessage", | |
cond: (ctx) => !!ctx.message | |
} | |
}, | |
exit: "increaseFired" | |
}, | |
loading: { | |
invoke: { | |
src: "serviceThatCanFail", | |
onDone: { | |
target: "success", | |
actions: "assignUser" | |
}, | |
onError: { | |
target: "open" | |
} | |
} | |
}, | |
open: { | |
entry: ["increaseFailure", "resetUser", "notifyFailure"], | |
after: { | |
HALF_OPEN_INTERVAL: "half-open" | |
}, | |
on: { | |
FETCH: "fail-safe" | |
} | |
}, | |
"half-open": { | |
entry: ["resetSuccess", "resetMessage"], | |
on: { | |
FETCH: "health-check" | |
} | |
}, | |
"health-check": { | |
entry: assign({ | |
message: "Checking if systems are operational" | |
}), | |
invoke: { | |
src: healthCheck, | |
onDone: { | |
target: "closed", | |
actions: ["resetFailure", "notifySuccess"] // Maybe increase the success counter here? | |
}, | |
onError: "open" | |
} | |
}, | |
"fail-safe": { | |
entry: ["notifyFailure", "increaseFired"], | |
always: "open" | |
}, | |
success: { | |
entry: "increaseSuccess", | |
always: "closed" | |
} | |
} | |
}, | |
{ | |
actions: { | |
increaseSuccess: assign({ | |
success: ctx => ctx.success + 1 | |
}), | |
increaseFailure: assign({ | |
failure: ctx => ctx.failure + 1 | |
}), | |
increaseFired: assign({ | |
fired: ctx => ctx.fired + 1 | |
}), | |
resetFailure: assign({ | |
failure: 0, | |
errorMessage: null | |
}), | |
resetSuccess: assign({ | |
success: 0 | |
}), | |
resetUser: assign({ | |
user: null | |
}), | |
resetMessage: assign({ | |
message: null | |
}), | |
notifyFailure: assign({ | |
message: ctx => | |
`System failed. Please try again later. Failure: ${ctx.failure}`, | |
user: null | |
}), | |
notifySuccess: assign({ | |
message: "All systems operational" | |
}), | |
assignUser: assign({ user: (ctx, event) => event.data }) | |
}, | |
services: { | |
serviceThatCanFail: async () => { | |
const resp = await functionThatCanFail(); | |
if (!resp.ok) { | |
throw new Error("User not found"); | |
} else { | |
return resp.json(); | |
} | |
} | |
}, | |
delays: { | |
HALF_OPEN_INTERVAL: (ctx, event) => { | |
return ctx.openTimeout; | |
} | |
} | |
}); | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment