faastjs
Version:
Serverless batch computing made simple.
641 lines • 96.2 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.logUrl = exports.googlePacker = exports.cleanup = exports.getRequestSubscription = exports.getResponseSubscription = exports.getResponseQueueTopic = exports.initialize = exports.initializeGoogleServices = exports.GoogleImpl = exports.defaults = exports.defaultGcWorker = exports.GoogleMetrics = void 0;
const abort_controller_1 = require("abort-controller");
const gaxios_1 = require("gaxios");
const googleapis_1 = require("googleapis");
const https = require("https");
const util = require("util");
const cache_1 = require("../cache");
const cost_1 = require("../cost");
const error_1 = require("../error");
const log_1 = require("../log");
const packer_1 = require("../packer");
const provider_1 = require("../provider");
const serialize_1 = require("../serialize");
const shared_1 = require("../shared");
const throttle_1 = require("../throttle");
const google_queue_1 = require("./google-queue");
const google_shared_1 = require("./google-shared");
const googleTrampolineHttps = require("./google-trampoline-https");
const googleTrampolineQueue = require("./google-trampoline-queue");
const gaxios = new gaxios_1.Gaxios({
retryConfig: {
retry: 3,
noResponseRetries: 3,
shouldRetry: (0, google_shared_1.shouldRetryRequest)(log_1.log.info)
}
});
const GoogleCloudFunctionsMemorySizes = [128, 256, 512, 1024, 2048];
class GoogleMetrics {
constructor() {
this.outboundBytes = 0;
this.pubSubBytes = 0;
}
}
exports.GoogleMetrics = GoogleMetrics;
function defaultGcWorker(resources, services) {
return deleteResources(services, resources, log_1.log.gc);
}
exports.defaultGcWorker = defaultGcWorker;
exports.defaults = {
...provider_1.commonDefaults,
region: "us-central1",
googleCloudFunctionOptions: {},
_gcWorker: defaultGcWorker
};
exports.GoogleImpl = {
name: "google",
initialize,
defaults: exports.defaults,
cleanup,
costSnapshot,
logUrl,
invoke,
poll,
responseQueueId
};
async function initializeGoogleServices() {
const auth = await googleapis_1.google.auth.getClient({
scopes: ["https://www.googleapis.com/auth/cloud-platform"]
});
googleapis_1.google.options({
auth,
retryConfig: {
retry: 8,
retryDelay: 250,
noResponseRetries: 3,
shouldRetry: (0, google_shared_1.shouldRetryRequest)(log_1.log.info)
}
});
return {
cloudFunctions: googleapis_1.google.cloudfunctions("v1"),
pubsub: googleapis_1.google.pubsub("v1"),
cloudBilling: googleapis_1.google.cloudbilling("v1"),
google: googleapis_1.google
};
}
exports.initializeGoogleServices = initializeGoogleServices;
async function defaultPollDelay(retries) {
if (retries > 5) {
await (0, shared_1.sleep)(5 * 1000);
}
await (0, shared_1.sleep)((retries + 1) * 500);
}
async function pollOperation({ request, checkDone, delay = defaultPollDelay, maxRetries = 50 }) {
let retries = 0;
await delay(retries);
while (true) {
log_1.log.info(`Polling...`);
const result = await request();
if (checkDone(result)) {
log_1.log.info(`Done.`);
return result;
}
if (retries++ >= maxRetries) {
throw new error_1.FaastError(`Timed out after ${retries} attempts.`);
}
await delay(retries);
}
}
async function quietly(promise) {
try {
const result = await promise;
return result.data;
}
catch (err) {
return;
}
}
const throttleGoogleWrite = (0, throttle_1.throttle)({
concurrency: 4,
rate: 3,
retry: (err, n) => {
const { message } = err;
return (n < 6 &&
(message.match(/Build failed/) !== null ||
message.match(/Quota/) !== null ||
message.match(/load attempt timed out/) !== null ||
message.match(/ECONNRESET/) !== null ||
message.match(/failed on loading user code/) != null));
}
}, (op) => op());
async function waitFor(api, response) {
return throttleGoogleWrite(async () => {
let operation;
try {
operation = await response();
}
catch (err) {
throw new error_1.FaastError(err, "could not get operation");
}
const operationName = operation.data.name;
try {
return pollOperation({
request: () => quietly(api.operations.get({ name: operationName })),
checkDone: result => {
/* istanbul ignore if */
if (!result) {
return false;
}
/* istanbul ignore if */
if (result.error) {
const underlying = new error_1.FaastError(result.error.message ?? undefined);
underlying.stack = "";
throw new error_1.FaastError(underlying, "Error polling operation");
}
return result.done || false;
}
});
}
catch (err) {
throw new error_1.FaastError(err, "poll operation failed");
}
});
}
async function deleteFunction(api, path) {
try {
return await waitFor(api, () => api.projects.locations.functions.delete({
name: path
}));
}
catch (err) {
if (err.message.match(/does not exist/)) {
return;
}
throw err;
}
}
async function initialize(fmodule, nonce, options) {
log_1.log.info(`Create google cloud function`);
const services = await initializeGoogleServices();
const project = await googleapis_1.google.auth.getProjectId();
const { cloudFunctions, pubsub } = services;
const { region } = options;
const location = `projects/${project}/locations/${region}`;
const functionName = "faast-" + nonce;
const { timeout } = options;
const { wrapperVerbose } = options.debugOptions;
async function createCodeBundle() {
const childProcessMemoryLimitMb = options.childProcessMemoryMb;
const wrapperOptions = {
childProcessTimeoutMs: Math.max(1000, (timeout - 5) * 1000),
wrapperVerbose,
childProcessMemoryLimitMb
};
const { archive } = await googlePacker(fmodule, options, wrapperOptions, functionName);
const uploadUrlResponse = await throttleGoogleWrite(() => cloudFunctions.projects.locations.functions.generateUploadUrl({
parent: location
}));
const uploadResult = await uploadZip(uploadUrlResponse.data.uploadUrl, archive);
log_1.log.info(`Upload zip file response: ${uploadResult?.statusText}`);
return uploadUrlResponse.data.uploadUrl;
}
const trampoline = `projects/${project}/locations/${region}/functions/${functionName}`;
const resources = {
trampoline,
region
};
const state = {
resources,
services,
project,
functionName,
metrics: new GoogleMetrics(),
options
};
const { gc, retentionInDays, _gcWorker: gcWorker } = options;
if (gc === "auto" || gc === "force") {
log_1.log.gc(`Starting garbage collector`);
state.gcPromise = collectGarbage(gcWorker, services, project, retentionInDays).catch(err => {
log_1.log.gc(`Garbage collection error: ${err}`);
});
}
const pricingPromise = getGoogleCloudFunctionsPricing(services.cloudBilling, region);
const { mode } = options;
const responseQueuePromise = (async () => {
const topic = await pubsub.projects.topics.create({
name: getResponseQueueTopic(project, functionName)
});
resources.responseQueueTopic = topic.data.name ?? undefined;
resources.responseSubscription = getResponseSubscription(project, functionName);
log_1.log.info(`Creating response queue subscription`);
await pubsub.projects.subscriptions.create({
name: resources.responseSubscription,
requestBody: {
topic: resources.responseQueueTopic
}
});
})();
let requestQueuePromise;
if (mode === "queue") {
log_1.log.info(`Initializing queue`);
resources.requestQueueTopic = getRequestQueueTopic(project, functionName);
requestQueuePromise = pubsub.projects.topics.create({
name: resources.requestQueueTopic
});
resources.requestSubscription = getRequestSubscription(project, functionName, region);
}
const sourceUploadUrl = await createCodeBundle();
const { memorySize, googleCloudFunctionOptions, env } = options;
if (!GoogleCloudFunctionsMemorySizes.find(size => size === memorySize)) {
log_1.log.warn(`Invalid memorySize ${memorySize} for Google Cloud Functions`);
}
const requestBody = {
name: trampoline,
entryPoint: "trampoline",
timeout: `${timeout}s`,
availableMemoryMb: memorySize,
sourceUploadUrl,
environmentVariables: env,
runtime: "nodejs14",
...googleCloudFunctionOptions
};
if (mode === "queue") {
await requestQueuePromise;
requestBody.eventTrigger = {
eventType: "providers/cloud.pubsub/eventTypes/topic.publish",
resource: resources.requestQueueTopic
};
}
else {
requestBody.httpsTrigger = {};
}
log_1.log.info(`Create function at ${location}`);
log_1.log.info(`Request body: %O`, requestBody);
await (0, throttle_1.retryOp)(3, async () => {
try {
log_1.log.info(`create function ${requestBody.name} [${options.description}]`);
await waitFor(cloudFunctions, () => cloudFunctions.projects.locations.functions.create({
location,
requestBody
}));
await cloudFunctions.projects.locations.functions.setIamPolicy({
resource: trampoline,
requestBody: {
policy: {
bindings: [
{
members: ["allUsers"],
role: "roles/cloudfunctions.invoker"
}
]
}
}
});
}
catch (err) {
/* istanbul ignore next */
await deleteFunction(cloudFunctions, trampoline).catch(() => { });
throw new error_1.FaastError({ cause: err, name: error_1.FaastErrorNames.ECREATE }, "failed to create google cloud function");
}
});
if (mode === "https" || mode === "auto") {
try {
const func = await cloudFunctions.projects.locations.functions.get({
name: trampoline
});
if (!func.data.httpsTrigger) {
throw new error_1.FaastError("Could not get http trigger url");
}
const { url } = func.data.httpsTrigger;
if (!url) {
throw new error_1.FaastError("Could not get http trigger url");
}
log_1.log.info(`Function URL: ${url}`);
state.url = url;
}
catch (err) {
throw new error_1.FaastError(err, `Could not get function ${trampoline} or its url, despite it being created`);
}
}
await pricingPromise;
await responseQueuePromise;
return state;
}
exports.initialize = initialize;
function getRequestQueueTopic(project, functionName) {
return `projects/${project}/topics/${functionName}-Requests`;
}
function getResponseQueueTopic(project, functionName) {
return `projects/${project}/topics/${functionName}-Responses`;
}
exports.getResponseQueueTopic = getResponseQueueTopic;
function getResponseSubscription(project, functionName) {
return `projects/${project}/subscriptions/${functionName}-Responses`;
}
exports.getResponseSubscription = getResponseSubscription;
function getRequestSubscription(project, functionName, region) {
return `projects/${project}/subscriptions/gcf-${functionName}-${region}-${functionName}-Requests`;
}
exports.getRequestSubscription = getRequestSubscription;
const agent = new https.Agent({ keepAlive: true, timeout: 0, maxSockets: 1000 });
async function callFunctionHttps(url, call, metrics, cancel) {
const source = new abort_controller_1.AbortController();
try {
const axiosConfig = {
method: "POST",
url,
headers: { "Content-Type": "application/json" },
body: (0, serialize_1.serialize)(call),
signal: source.signal,
responseType: "json",
retry: false,
agent
};
const rawResponse = await Promise.race([
gaxios.request(axiosConfig),
cancel
]);
if (!rawResponse) {
log_1.log.info(`cancelling gcp invoke`);
source.abort();
return;
}
try {
metrics.outboundBytes += (0, shared_1.computeHttpResponseBytes)(rawResponse.headers);
}
catch (err) {
throw new error_1.FaastError(err, `Could not parse ${util.inspect(rawResponse.data)}`);
}
}
catch (err) {
const { response } = err;
if (response) {
if (response.status === 503) {
throw new error_1.FaastError({ cause: err, name: error_1.FaastErrorNames.EMEMORY }, "google cloud function: possibly out of memory");
}
throw new error_1.FaastError(err, `when invoking google cloud function: %s\nDetails: %s`, response.statusText, response.data);
}
throw new error_1.FaastError(err, `when invoking google cloud function`);
}
}
async function invoke(state, call, cancel) {
const { options, resources, services, url, metrics } = state;
switch (options.mode) {
case "auto":
case "https":
return callFunctionHttps(url, call, metrics, cancel);
case "queue":
const { requestQueueTopic } = resources;
const { pubsub } = services;
const serialized = (0, serialize_1.serialize)(call);
return (0, google_queue_1.publishPubSub)(pubsub, requestQueueTopic, serialized);
}
}
function poll(state, cancel) {
return (0, google_queue_1.receiveMessages)(state.services.pubsub, state.resources.responseSubscription, state.metrics, cancel);
}
function responseQueueId(state) {
return state.resources.responseQueueTopic;
}
async function deleteResources(services, resources, output = log_1.log.info) {
const { trampoline, requestQueueTopic, requestSubscription, responseSubscription, responseQueueTopic, region, ...rest } = resources;
const _exhaustiveCheck = {};
const { cloudFunctions, pubsub } = services;
// We deliberately rethrow transient errors here, so only if all prior
// deletes succeed do we proceed. If there's a transient error then future
// garbage collection will clean up. The order is important; the function
// itself must be deleted last.
const check = async (request) => {
try {
await request;
}
catch (err) {
/* istanbul ignore next */
if (err.message.match(/Resource not found/)) {
return;
}
throw err;
}
};
if (responseSubscription) {
await check(pubsub.projects.subscriptions.delete({ subscription: responseSubscription }));
output(`Deleted response subscription: ${responseSubscription}`);
}
if (responseQueueTopic) {
await check(pubsub.projects.topics.delete({ topic: responseQueueTopic }));
output(`Deleted response queue topic: ${responseQueueTopic}`);
}
if (requestSubscription) {
await check(pubsub.projects.subscriptions.delete({ subscription: requestSubscription }));
output(`Deleted response subscription: ${requestSubscription}`);
}
if (requestQueueTopic) {
await check(pubsub.projects.topics.delete({ topic: requestQueueTopic }));
output(`Deleted request queue topic: ${requestQueueTopic}`);
}
if (trampoline) {
await check(deleteFunction(cloudFunctions, trampoline));
output(`Deleted function ${trampoline}`);
}
}
async function cleanup(state, options) {
log_1.log.info(`google cleanup starting.`);
if (state.gcPromise) {
log_1.log.info(`Waiting for garbage collection...`);
await state.gcPromise;
log_1.log.info(`Garbage collection done.`);
}
if (options.deleteResources) {
try {
await deleteResources(state.services, state.resources);
}
catch (err) {
throw new error_1.FaastError(err, "delete resources failed");
}
}
log_1.log.info(`google cleanup done.`);
}
exports.cleanup = cleanup;
let garbageCollectorRunning = false;
async function collectGarbage(gcWorker, services, proj, retentionInDays) {
if (gcWorker === defaultGcWorker) {
if (garbageCollectorRunning) {
return;
}
garbageCollectorRunning = true;
}
try {
const { cloudFunctions } = services;
let pageToken;
let promises = [];
const scheduleDeleteResources = (0, throttle_1.throttle)({
concurrency: 5,
rate: 5,
burst: 2
}, async (gServices, fn) => {
const { region, name, project } = parseFunctionName(fn.name);
const resources = {
region,
trampoline: fn.name,
requestQueueTopic: getRequestQueueTopic(project, name),
requestSubscription: getRequestSubscription(project, name, region),
responseQueueTopic: getResponseQueueTopic(project, name),
responseSubscription: getResponseSubscription(project, name)
};
await gcWorker(resources, gServices);
});
const fnPattern = new RegExp(`/functions/faast-${shared_1.uuidv4Pattern}$`);
do {
const funcListResponse = await cloudFunctions.projects.locations.functions.list({
parent: `projects/${proj}/locations/-`,
pageToken
});
pageToken = funcListResponse.data.nextPageToken ?? undefined;
const garbageFunctions = (funcListResponse.data.functions || [])
.filter(fn => (0, shared_1.hasExpired)(fn.updateTime, retentionInDays))
.filter(fn => fn.name.match(fnPattern));
promises = garbageFunctions.map(fn => scheduleDeleteResources(services, fn));
} while (pageToken);
await Promise.all(promises);
}
finally {
if (gcWorker === defaultGcWorker) {
garbageCollectorRunning = false;
}
}
}
function parseFunctionName(path) {
const match = path.match(/^projects\/(.*)\/locations\/(.*)\/functions\/(.*)$/);
return match && { project: match[1], region: match[2], name: match[3] };
}
async function uploadZip(url, zipStream) {
const config = {
method: "PUT",
url,
body: zipStream,
headers: {
"content-type": "application/zip",
"x-goog-content-length-range": "0,104857600"
}
};
return gaxios.request(config);
}
async function googlePacker(functionModule, options, wrapperOptions, FunctionName) {
const { mode } = options;
const trampolineModule = mode === "queue" ? googleTrampolineQueue : googleTrampolineHttps;
return (0, packer_1.packer)(trampolineModule, functionModule, options, wrapperOptions, FunctionName);
}
exports.googlePacker = googlePacker;
let getGooglePrice;
function ensureGooglePriceCache(cloudBilling) {
if (getGooglePrice) {
return;
}
getGooglePrice = (0, throttle_1.throttle)({
concurrency: 1,
rate: 3,
memoize: true,
cache: cache_1.caches.googlePrices
}, async (region, serviceName, description, conversionFactor) => {
try {
const skusResponse = await cloudBilling.services.skus.list({
parent: serviceName
});
const { skus = [] } = skusResponse.data;
const matchingSkus = skus.filter(sku => sku.description === description);
log_1.log.provider(`matching SKUs: ${util.inspect(matchingSkus, { depth: null })}`);
const regionOrGlobalSku = matchingSkus.find(sku => sku.serviceRegions.find(r => r === region)) ??
matchingSkus.find(sku => sku.serviceRegions.find(r => r === "global"));
const pexp = regionOrGlobalSku.pricingInfo[0].pricingExpression;
const prices = pexp.tieredRates.map(rate => Number(rate.unitPrice.units ?? "0") +
rate.unitPrice.nanos / 1e9);
const price = Math.max(...prices) *
(conversionFactor / pexp.baseUnitConversionFactor);
log_1.log.provider(`Found price for ${serviceName}, ${description}, ${region}: ${price}`);
return price;
}
catch (err) {
throw new error_1.FaastError(err, `failed to get google pricing for "${description}"`);
}
});
}
let googleServices;
const listGoogleServices = (0, throttle_1.throttle)({ concurrency: 1 }, async (cloudBilling) => {
if (googleServices) {
return googleServices;
}
const response = await cloudBilling.services.list();
googleServices = response.data.services;
return googleServices;
});
async function getGoogleCloudFunctionsPricing(cloudBilling, region) {
const services = await listGoogleServices(cloudBilling);
ensureGooglePriceCache(cloudBilling);
const getPricing = (serviceName, description, conversionFactor = 1) => {
const service = services.find(s => s.displayName === serviceName);
return getGooglePrice(region, service.name, description, conversionFactor);
};
return {
perInvocation: await getPricing("Cloud Functions", "Invocations"),
perGhzSecond: await getPricing("Cloud Functions", "CPU Time"),
perGbSecond: await getPricing("Cloud Functions", "Memory Time", 2 ** 30),
perGbOutboundData: await getPricing("Cloud Functions", `Network Egress from ${region}`, 2 ** 30),
perGbPubSub: await getPricing("Cloud Pub/Sub", "Message Delivery Basic", 2 ** 30)
};
}
// https://cloud.google.com/functions/pricing
const gcfProvisonableMemoryTable = {
128: 0.2,
256: 0.4,
512: 0.8,
1024: 1.4,
2048: 2.4
};
async function costSnapshot(state, stats) {
const costs = new cost_1.CostSnapshot("google", state.options, stats);
const { memorySize = exports.defaults.memorySize } = state.options;
const provisionableSizes = (0, shared_1.keysOf)(gcfProvisonableMemoryTable)
.map(n => Number(n))
.sort((a, b) => a - b);
const provisionedMb = provisionableSizes.find(size => memorySize <= size);
if (!provisionedMb) {
log_1.log.warn(`Could not determine provisioned memory or CPU for requested memory size ${memorySize}`);
}
const provisionedGhz = gcfProvisonableMemoryTable[provisionedMb];
const billedTimeStats = stats.estimatedBilledTime;
const seconds = (billedTimeStats.mean / 1000) * billedTimeStats.samples;
const { region } = state.resources;
const prices = await getGoogleCloudFunctionsPricing(state.services.cloudBilling, region);
const provisionedGb = provisionedMb / 1024;
const functionCallDuration = new cost_1.CostMetric({
name: "functionCallDuration",
pricing: prices.perGbSecond * provisionedGb + prices.perGhzSecond * provisionedGhz,
unit: "second",
measured: seconds,
comment: `https://cloud.google.com/functions/pricing#compute_time (${provisionedMb} MB, ${provisionedGhz} GHz)`
});
costs.push(functionCallDuration);
const functionCallRequests = new cost_1.CostMetric({
name: "functionCallRequests",
pricing: prices.perInvocation,
measured: stats.invocations,
unit: "request",
comment: "https://cloud.google.com/functions/pricing#invocations"
});
costs.push(functionCallRequests);
const outboundDataTransfer = new cost_1.CostMetric({
name: "outboundDataTransfer",
pricing: prices.perGbOutboundData,
measured: state.metrics.outboundBytes / 2 ** 30,
unit: "GB",
comment: "https://cloud.google.com/functions/pricing#networking"
});
costs.push(outboundDataTransfer);
const pubsub = new cost_1.CostMetric({
name: "pubsub",
pricing: prices.perGbPubSub,
measured: state.metrics.pubSubBytes / 2 ** 30,
unit: "GB",
comment: "https://cloud.google.com/pubsub/pricing"
});
costs.push(pubsub);
return costs;
}
function logUrl(state) {
const { project, functionName } = state;
return `https://console.cloud.google.com/logs/viewer?project=${project}&resource=cloud_function%2Ffunction_name%2F${functionName}`;
}
exports.logUrl = logUrl;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ29vZ2xlLWZhYXN0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2dvb2dsZS9nb29nbGUtZmFhc3QudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsdURBQW1EO0FBQ25ELG1DQUE4RTtBQUM5RSwyQ0FNb0I7QUFDcEIsK0JBQStCO0FBQy9CLDZCQUE2QjtBQUM3QixvQ0FBa0M7QUFDbEMsa0NBQW1EO0FBQ25ELG9DQUF1RDtBQUN2RCxnQ0FBNkI7QUFDN0Isc0NBQWlEO0FBQ2pELDBDQVFxQjtBQUNyQiw0Q0FBeUM7QUFDekMsc0NBTW1CO0FBQ25CLDBDQUFnRDtBQUVoRCxpREFBZ0U7QUFDaEUsbURBQXFEO0FBQ3JELG1FQUFtRTtBQUNuRSxtRUFBbUU7QUFNbkUsTUFBTSxNQUFNLEdBQUcsSUFBSSxlQUFNLENBQUM7SUFDdEIsV0FBVyxFQUFFO1FBQ1QsS0FBSyxFQUFFLENBQUM7UUFDUixpQkFBaUIsRUFBRSxDQUFDO1FBQ3BCLFdBQVcsRUFBRSxJQUFBLGtDQUFrQixFQUFDLFNBQUcsQ0FBQyxJQUFJLENBQUM7S0FDNUM7Q0FDSixDQUFDLENBQUM7QUE4QkgsTUFBTSwrQkFBK0IsR0FBRyxDQUFDLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxFQUFFLElBQUksRUFBRSxJQUFJLENBQUMsQ0FBQztBQXdEcEUsTUFBYSxhQUFhO0lBQTFCO1FBQ0ksa0JBQWEsR0FBRyxDQUFDLENBQUM7UUFDbEIsZ0JBQVcsR0FBRyxDQUFDLENBQUM7SUFDcEIsQ0FBQztDQUFBO0FBSEQsc0NBR0M7QUF1QkQsU0FBZ0IsZUFBZSxDQUFDLFNBQTBCLEVBQUUsUUFBd0I7SUFDaEYsT0FBTyxlQUFlLENBQUMsUUFBUSxFQUFFLFNBQVMsRUFBRSxTQUFHLENBQUMsRUFBRSxDQUFDLENBQUM7QUFDeEQsQ0FBQztBQUZELDBDQUVDO0FBRVksUUFBQSxRQUFRLEdBQTRCO0lBQzdDLEdBQUcseUJBQWM7SUFDakIsTUFBTSxFQUFFLGFB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