Skip to content

Instantly share code, notes, and snippets.

@jthomas
Last active May 13, 2019 11:29
Show Gist options
  • Select an option

  • Save jthomas/85c55e790d7e97fd0ca6ae94991dd80f to your computer and use it in GitHub Desktop.

Select an option

Save jthomas/85c55e790d7e97fd0ca6ae94991dd80f to your computer and use it in GitHub Desktop.

Revisions

  1. jthomas revised this gist May 13, 2019. 1 changed file with 100 additions and 0 deletions.
    100 changes: 100 additions & 0 deletions script.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,100 @@
    "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))
    })();
  2. jthomas created this gist May 13, 2019.
    15 changes: 15 additions & 0 deletions action.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,15 @@
    "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 }
    }