Reputation: 2576
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.
$asyncresult will invoke the function, and return a HTTP status 202, but with no way to later check whether the function actually did anything.
$promise is a Guzzle promise which are used extensively by the AWS SDK, to which a function can be bound in PHP to be executed when the lambda is finished. That seems very useful, as long as PHP doesn't return a response to the browser, because at that point you lose the promise. You can call $result = $promise->wait()
to let it resolve. The major advantage of these promises seems to be to run multiple lambdas concurrently, and combine the results, all within a single PHP request.
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?
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
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