Reputation: 1056
I'm running a relatively simple AWS Function to add a subscription to Stripe.
It runs fine unless I hit it shortly after I just hit it. Just trying to run it in PostMan one after the other fails and returns:
{"errorMessage": "Process exited before completing request"}
The requests are delivered via API Gateway.
Function is configured with 30s timeout and is take ~1300ms to run on the base 128M RAM (issue reproducible @ 256M).
I thought this was exactly what Lambda was designed to avoid... I'm second guessing my decision to use Lambda for a (synchronous) mission critical component.
EDIT: As requested, here's the function code:
var stripe = require('stripe');
exports.handler = function (event, context, callback) {
var self = this;
stripe = stripe(getKey(event.stage, 'STRIPE_SECRET_KEY'));
self.createSubscription = createSubscription;
self.validPayload = validPayload;
console.log('event: ', event);
if (self.validPayload(event, context)) {
self.createSubscription(event, stripe, callback, context);
}
/**
* checks that the necessary payload has been received
* if YES: returns true and allows process to continue
* if NO: throws context.fail with useful error message(s)
* operating under custom error code naming convention of
* http code + 3 digit ULM error code
* @param event - from Lambda
* @param context - from Lambda
* @returns {boolean} - whether the payload contains the required data
*/
function validPayload (event, context) {
var errorResponse = {
status: 400,
errors: []
};
if (!event.billing_email) {
errorResponse.errors.push({
code: 400001,
message: "No billing email provided."
})
}
if (!event.plan) {
errorResponse.errors.push({
code: 400002,
message: "No plan was selected."
})
}
if (!event.token) {
errorResponse.errors.push({
code: 400003,
message: "A valid credit card was not provided."
})
}
if (!!errorResponse.errors.length) {
context.fail(JSON.stringify(errorResponse));
return false;
} else {
return true;
}
}
/**
* Creates a new customer & subscription using stripe package method
* if success, executes callback with response data
* if fail, throws context.fail with useful error message(s)
* @param event - from Lambda
* @param stripe - probably not necessary...
* @param callback - from Lambda
* @param context - probably not necessary...
*/
function createSubscription (event, stripe, callback, context) {
stripe.customers.create({
source: event.token,
plan: event.plan,
email: event.billing_email
}, function (err, customer) {
if (err) {
var errorResponse = {
status: 400,
errors: []
};
errorResponse.errors.push({
code: 400004,
message: err.message
});
console.error('Customer/Plan Creation Failed');
callback(JSON.stringify(errorResponse));
} else {
callback(null, {
status: 200,
customer: customer
});
}
});
}
function getKey (stage, keyId) {
var keys = {
STRIPE_SECRET_KEY: {
staging: 'sk_test_123456',
prod: 'sk_live_123456'
}
};
if (stage === 'prod') {
return keys[keyId][stage];
} else {
return keys[keyId]['staging'];
}
}
};
EDIT 2: Dug into CloudWatch and found this error log: TypeError: stripe is not a function at exports.handler (/var/task/exports.js:5:14)
Upvotes: 0
Views: 650
Reputation: 5973
@rowanu is correct, your problem is on this line stripe = stripe(getKey(event.stage, 'STRIPE_SECRET_KEY'));
. Since the Lambda stays hot to handle subsequent requests any variables declared outside of the handler
function will be seen by each new request that comes in. This should be a simple fix, don't redefine the stripe
variable. Something like this would do the trick:
var stripe = require('stripe');
var stripeInstance; // undefined on startup
exports.handler = function (event, context, callback) {
// now define stripeInstance if its not already defined
if(!stripeInstance) {
stripeInstance = stripe(getKey(event.stage, 'STRIPE_SECRET_KEY'));
}
// now all functions will be able to use the same instance of stripe.
// This assumes the event.stage is always the same, if you need a new instance for every request then remove the if statement
// rename all references of stripe to stripeInstance
...
Upvotes: 4
Reputation: 4152
"Process exited before completing request" indicates that your function exited without calling the callback. It is not related to timeouts or throttling.
Usually this indicates that an exception is thrown from a code path that doesn't have adequate error handling.
You will simply need to handle or fix "stripe is not a function at exports.handler (/var/task/exports.js:5:14)" and call the appropriate callback.
Upvotes: 1