Skip to content

Instantly share code, notes, and snippets.

@jthomas
Last active May 13, 2019 11:29
Show Gist options
  • Save jthomas/85c55e790d7e97fd0ca6ae94991dd80f to your computer and use it in GitHub Desktop.
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
"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 }
}
"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