Как запустить код, который обращается к состоянию в памяти действия Apache OpenWhisk после его завершения?

Я тестировал использование IBM Cloud Functions (управляемый Apache OpenWhisk) для запуска кода в фоновом режиме после завершения действия, но мой обратный вызов, предоставленный при вызове setTimeout, не запускался в нужное время позже, и он никогда не запускался вообще, если я не вызвал функция во второй раз. Это было запущено в тот момент (поздно).

Подробности:

У меня было два варианта использования:

  • Накопление данных нескольких запросов в памяти с последующим помещением большого объекта в корзину облачного хранилища объектов после того, как накопилось много данных или прошло определенное время без запросов.
  • Управление подключением к базе данных для каждого контейнера для API, чтобы я мог закрыть подключение в неиспользуемом контейнере, который IBM Cloud Functions еще не уничтожил.

Я предполагал, что это сработает, потому что я использовал другие платформы, такие как Google Cloud Run, и заметил, что код работает в фоновом режиме (с использованием setTimeout и т. Д.), Просматривая журналы этого кода в Stackdriver после завершения запроса. И есть даже целая библиотека, созданная сторонником разработчика AWS, которая управляет подключениями MySQL в фоновом режиме на AWS Lambda (https://www.npmjs.com/package/serverless-mysql).

Я тестировал следующую функцию:

// from https://stackoverflow.com/questions/105034/how-to-create-guid-uuid
function uuidv4() {
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
        var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
        return v.toString(16);
    });
}

function main() {
    const runId = uuidv4().slice(31, 36);

    console.log(`function started (runId = ${runId})`);

    setTimeout(() => {
        console.log(`after 5s delay (runId = ${runId})`);
    }, 5000);

    return {
        msg: `ok (runId = ${runId})`,
    };
}

И я развернул его командой ibmcloud fn action update logging-in-background src/index.js --kind nodejs:10.

Я создал экземпляр LogDNA и установил его как экземпляр моей платформы, чтобы мои журналы функций переходили к нему. Вот что я вижу в журналах после трехкратного вызова функции с помощью команды ibmcloud fn action invoke logging-in-background --blocking, каждые 10 секунд (CRN удален):

May 18 17:26:23 functions REDACTED 2020-05-18T21:26:23.956013Z    stdout: function started (runId = 9be7c)
May 18 17:26:23 functions REDACTED Activation record '3589870e8ce44cc089870e8ce4acc018' for entity 'logging-in-background'
May 18 17:26:34 functions REDACTED 2020-05-18T21:26:34.111745Z    stdout: after 5s delay (runId = 9be7c)
May 18 17:26:34 functions REDACTED 2020-05-18T21:26:34.115043Z    stdout: function started (runId = faba6)
May 18 17:26:34 functions REDACTED Activation record 'ac47c067177648f187c0671776b8f1c2' for entity 'logging-in-background'
May 18 17:26:44 functions REDACTED 2020-05-18T21:26:44.248470Z    stdout: after 5s delay (runId = faba6)
May 18 17:26:44 functions REDACTED 2020-05-18T21:26:44.253822Z    stdout: function started (runId = 0af34)
May 18 17:26:44 functions REDACTED Activation record 'bbad3eabb3d64ab1ad3eabb3d61ab1a7' for entity 'logging-in-background'

Вы можете видеть, как, когда я впервые вызвал функцию, она записала только сообщение «функция запущена». Через 5 секунд не было зарегистрировано сообщение «после 5 секунд задержки». Но затем, в начале второго вызова, через 10 секунд после первого вызова, он, наконец, регистрирует сообщение «после 5-секундной задержки», связанное с запуском 9be7c. Обратный вызов для setTimeout, похоже, никогда не будет запущен, как минимум, до следующего вызова действия.

Это то, как Apache OpenWhisk должен работать по задумке, или есть что-то, что я делаю неправильно, чтобы запускать код в фоновом режиме после завершения действия?


person Matt Welke    schedule 18.05.2020    source источник
comment
Это то, как Apache OpenWhisk должен работать по замыслу ... - То, что вы наблюдали и описываете, - это то, как система предназначена для работы.   -  person user6062970    schedule 19.05.2020


Ответы (2)


Ваша функция возвращается до завершения setTimeout.

Взгляните на документы здесь:

https://github.com/apache/openwhisk/blob/master/docs/actions-nodejs.md#creating-asynchronous-actions

Правильный способ сделать это:

function main(args) {
    const runId = uuidv4().slice(31, 36);

    console.log(`function started (runId = ${runId})`);
    return new Promise(function(resolve, reject) {
       setTimeout(function() {
         console.log(`after 5s delay (runId = ${runId})`);
         resolve({ msg: `ok (runId = ${runId}) });
       }, 5000);
    })
}

При использовании лямбда-выражения AWS, если вы оставляете задачи в цикле событий, функция не возвращает / не завершает запрос, пока четный цикл не станет пустым. Если вы используете специальный флаг контекста, чтобы функция могла быстро возвращаться, вам не гарантируется, что фоновая обработка действительно завершится.

Чтобы инициализировать пулы соединений и повторно использовать их при вызовах, вы можете сохранить соединение в глобальной переменной и убедиться, что оно только инициализировано.

person user6062970    schedule 18.05.2020
comment
Я уже читал эту часть документации. Но я не пытаюсь создать действие, которое не вернется, пока не завершится его асинхронная часть. Я пытаюсь создать действие, которое можно завершить, оставив при этом работу над циклом событий, которая будет завершена позже. Один из моих вариантов использования - накопление данных для помещения большого объекта в корзину COS после завершения действия - также включает устранение неполадок. Все дело в том, чтобы завершить действие, сохранить некоторые данные в памяти, принять больше данных в будущих вызовах действий, а затем в конечном итоге отправить данные в COS. Может ли OpenWhisk сделать это? - person Matt Welke; 19.05.2020
comment
Существуют также общие методы управления постоянными соединениями с базой данных с платформ FaaS, так что контейнер зомби не поддерживает соединение открытым, при этом несколько контейнеров зомби в конечном итоге насыщают лимит соединений базы данных. Это еще один вариант использования, о котором я упомянул в своем вопросе. Для меня важно, чтобы OpenWhisk тоже мог это делать, иначе я не смогу безопасно сохранить ссылку на соединение в действии и повторно использовать ее для нескольких вызовов. Мне каждый раз приходилось закрывать и снова открывать соединение. - person Matt Welke; 19.05.2020
comment
Способ сделать это с помощью OpenWhisk - вызвать асинхронное действие, которое может завершить обработку. Либо с помощью пакета npm openwhisk для другого вызова, либо напрямую через REST API. Для более сложных рабочих процессов вам может потребоваться композитор OpenWhisk (например, AWS Step Functions). С AWS lambda, когда вы оставляете задачи в цикле событий, функция не возвращает / не завершает запрос, пока четный цикл не станет пустым. Если вы используете специальный флаг контекста, чтобы функция могла быстро возвращаться, вам не гарантируется, что фоновая обработка действительно завершится. - person user6062970; 19.05.2020
comment
Все дело в том, чтобы завершить действие, сохранить некоторые данные в памяти, принять больше данных в будущих вызовах действий, а затем в конечном итоге отправить данные в COS. Может ли OpenWhisk сделать это? --- Вы можете оставить внутрифункциональное состояние. Например, если вы объявляете глобальную переменную, она доступна для всех вызовов функции, которая повторно использует этот контейнер. Вы можете использовать этот подход (наличие глобальной ссылки) также для создания подключения к базе данных или пула подключений. Вам не нужно создавать новые соединения при каждом вызове. - person user6062970; 19.05.2020
comment
Спасибо за предложение изучить другие шаблоны, например, вызвать другое действие из моего действия, но я не думаю, что это сработает и для моего варианта использования. Я бы хотел, чтобы код, который запускается позже, по-прежнему имел доступ к состоянию первого действия. Я думаю, что для моего варианта использования COS единственным вариантом было бы сохранить это состояние во внешней базе данных и провести второй запланированный опрос этой базы данных и поместить объект в корзину, когда будет собрано достаточно. - person Matt Welke; 19.05.2020
comment
Что касается проблемы с зомби-соединением SQL (контейнер не используется, но еще не убит), я не могу придумать решения, если OpenWhisk не будет запускать код, помещенный в цикл событий после завершения действия. Не было бы никакого способа закрыть соединение, кроме как дождаться, пока OpenWhisk решит убить контейнер. Это заставляет меня задаться вопросом, как эта бессерверная библиотека mysql делает это. Каким-то образом он может закрыть соединения после того, как функция AWS Lambda вернется, когда она обнаружит, что для этого настало подходящее время. Я могу попробовать посмотреть, как работает эта библиотека. - person Matt Welke; 19.05.2020
comment
Что ж, теперь я чувствую себя довольно глупо. Теперь, когда я смотрю на то, что делает библиотека serverless-mysql, становится ясно, что она не добавляет обратные вызовы в цикл обработки событий Node.js. Он делает все на стороне сервера, где сервером в данном случае является сервер MySQL. В конце каждого вызова лямбда он запускает запрос MySQL, который уничтожает соединения с другими контейнерами лямбда, если они не использовались какое-то время. Теперь, когда вы понимаете, что я пытался сделать, если вы отредактируете свой ответ, чтобы объяснить проблему цикла событий, я могу отметить его как принятый ответ. - person Matt Welke; 19.05.2020

Другой пользователь помог мне понять, что происходит, объяснив, что OpenWhisk не запускает код в цикле событий после завершения действия. Следовательно, невозможно сделать то, что я хотел сделать, когда я хотел, чтобы каждое вызываемое действие извлекало данные, хранящиеся в локальной переменной, через некоторое время после завершения действия.

person Matt Welke    schedule 06.06.2020