Last active
May 13, 2019 11:29
-
-
Save jthomas/85c55e790d7e97fd0ca6ae94991dd80f to your computer and use it in GitHub Desktop.
Example showing how to handle intermittant action failures for large number of invocations
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
"use strict"; | |
const ERROR_RATE = 0.25 | |
function should_fail () { | |
return Math.random() < ERROR_RATE | |
} | |
function main(params) { | |
if (!params.a || !params.b) throw new Error('Missing input parameters (a or b).') | |
if (should_fail()) throw new Error('failed!') | |
const sum = params.a + params.b | |
return { sum } | |
} |
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
"use strict"; | |
// Set up Apache OpenWhisk Client SDK | |
const openwhisk = require('openwhisk'); | |
const options = { | |
apihost: <INSERT_HOSTNAME>, | |
api_key: <INSERT_API_KEY> | |
} | |
const ow = openwhisk(options) | |
const rand_int = () => Math.floor(Math.random() * 100) | |
// Promise-ify setTimeout | |
const delay = async ms => new Promise(resolve => setTimeout(resolve, ms)) | |
// Returns function which checks whether max elapsed time has passed | |
// since instance creation. | |
const elapsed_time = max => { | |
const start = new Date().getTime() | |
return () => { | |
const now = new Date().getTime() | |
return (now - start) > max | |
} | |
} | |
// Perform non-blocking action invocation and poll activation result using returned activation id. | |
// `activations.get()` will return result if action has finished and result is available. | |
// HTTP 404 returned when activation has not finished, wait and re-check after delay. | |
// All other errors code are fatal and should be thrown to caller. | |
// | |
// Parameters include delay_ms which controls polling delay and max_time | |
// which indicates maximum allowable waiting time for action to finish. | |
const invoke_and_poll = async (action, params, delay_ms, max_time) => { | |
const time_has_elapsed = elapsed_time(max_time) | |
const activation = await ow.actions.invoke({name: action, params}) | |
console.log(`new activation id: ${activation.activationId}`) | |
let result = null | |
while (!result) { | |
try { | |
result = await ow.activations.get({ name: activation.activationId }) | |
console.log(`activation result (${activation.activationId}) now available!`) | |
} catch (err) { | |
if (err.statusCode !== 404) { | |
throw err | |
} | |
console.log(`activation result (${activation.activationId}) not available yet`) | |
} | |
await delay(delay_ms) | |
if (time_has_elapsed()) throw new Error(`Maximum polling duration has elapsed (${result.activationId})`) | |
} | |
return result | |
} | |
// Action invocation with retries when activation result indicates a failure | |
// result.response.success boolean value shows whether action result suceeded or failed | |
// retries parameter configures the maximum number of allowable retries | |
const invoke_with_retries = async (invoke, retries) => { | |
let result = null | |
let max_invocations = retries + 1 | |
const actvs = [] | |
do { | |
if (max_invocations == 0) throw new Error(`Exhaused all available retries. ${actvs}`) | |
result = await invoke() | |
actvs.push(result.activationId) | |
console.log('action invocation succeeded:', result.response.success) | |
max_invocations-- | |
} while (!result.response.success) | |
console.log('action result:', result.response.result) | |
return result | |
} | |
;(async () => { | |
// 100 invocations, 1 second between polls, 15 second maximum polling time, 15 maximum retries | |
const polling_delay_ms = 1000 | |
const polling_timeout_ms = 1000 * 15 | |
const maximum_retries = 5 | |
const invocations = 200 | |
// Generate input data for N invocations. | |
const input = new Array(invocations).fill(null).map(() => { | |
return { a: rand_int(), b: rand_int() } | |
}) | |
// Fire polling invocation with retries for each input value | |
const output = input.map(async params => { | |
const ow_invoke = () => invoke_and_poll('sum', params, polling_delay_ms, polling_timeout_ms) | |
const result = await invoke_with_retries(ow_invoke, maximum_retries) | |
console.log(`${params.a} + ${params.b} = ${result.response.result.sum}`) | |
return result | |
}) | |
// Wait for all results to be returned! | |
const all_results = await Promise.all(output) | |
console.log(all_results.map(result => result.response.result)) | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment