okdewit
okdewit

Reputation: 2576

AWS SDK (PHP): Invoking lambda function asynchronously, retrieving status

We have an AWS lambda function, used for some awful proprietary file format processing and customizing before download, which is triggered from PHP through the AWS SDK. The lambda grabs the file from an s3 bucket, inserts some metadata into the file, compresses it, and places it on s3 in a temporary bucket so it can be downloaded during customer checkout.

The lambda part works flawlessly through invoke/invokeAsync:

use Aws\Sdk;
...
$lambdaclient = $this->sdk->createClient('lambda',$region);

$syncresult = $lambdaclient->invoke([
    'FunctionName' => $functionName,
    'InvocationType' => 'RequestResponse',
    'Payload' => json_encode($payload),
]);

$asyncresult = $lambdaclient->invoke([
    'FunctionName' => $functionName,
    'InvocationType' => 'Event',
    'Payload' => json_encode($payload),
]);

$promise = $lambdaclient->invokeAsync([
    'FunctionName' => $functionName,
    'Payload' => json_encode($payload),
]);

Problem is that the lambda can take up to a minute to complete, which is quite long for PHP to return a response to the browser.

What I'd like is to invoke the function asynchronously, and check up on the function status in a later PHP request (storing some reference to the invocation in the session data perhaps?). That way, I could poll for the status using an Ajax request later, and make sure that when I create a signed s3 URL to the final file that everything completed without errors.

Would such a thing be possible using the AWS SDK? Or are there better solutions?

Edit:

I have decided to use invokeAsync triggered from a client-side ajax call which is aborted (not waiting for an answer). The PHP script is given a fairly generous amount of time to finish (using settimelimit), because the calls to lambda are running concurrently and the browser isn't waiting anymore anyway.

I store the promises in an Array, and use:

$values = array_map(function($result){
    return json_decode($result->toArray()['Payload']->getContents());
}, Promise\unwrap($promises));

to get the response payloads (they're wrapped in streaminterfaces by AWS SDK), and write them away (DynamoDB in my case, by SQL/session/cache would probably all work).

When the end user needs the result, I just plainly check for the stored data and whether the converted files exist on s3.

I might improve the ajax handling by playing around with flushing the output buffer from PHP, finishing the call properly with some returned ID, while allowing PHP to wait for the promises from AWS.

Upvotes: 4

Views: 4912

Answers (1)

Pitchinnate
Pitchinnate

Reputation: 7556

Yes you could do this I'm not sure if you could do it purely with Lambda though. The way I would approach it is this:

1) When your current PHP code gets hit add send a message with AWS SNS with details about the function and parameters. Immediately return a message to the browser with information about the file to watch for in s3. Then have your browser poll the endpoint I describe in step 3.

2) Setup your lambda function to listen for a SNS message and run automatically (http://docs.aws.amazon.com/sns/latest/dg/sns-lambda.html)

3) Have another PHP endpoint that you can hit with AJAX that checks to see if the file created by lambda is done being created yet. If not return a message saying it isn't ready yet, once the file is present then return a message saying it is ready.

If you don't want to use a purely AWS approach then you could do something like this:

1) When your current PHP code gets hit add a record to a database table with information about the function and parameters that need ran and a status of something like queued. Send back the id for the record in the db to browser right away. Then have your browser poll the endpoint I describe in step 3.

2) Setup a separate php script that runs continuously or is triggered via cron or something, that checks every few seconds for entries to the table created above with a status of queued. Update the record status to something like processing so it doesn't accidentally get ran again. Now have it trigger the Lambda function and use the promise. When it is complete, update the record status to complete.

3) Have another PHP endpoint that you can hit with AJAX that you pass in the id received in step one and simply return the status for the record. Once the browser gets back a status of complete it knows the file is ready to be downloaded.

Upvotes: 1

Related Questions