UNPKG

faastjs

Version:

Serverless batch computing made simple.

641 lines 96.2 kB
"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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