faastjs
Version:
Serverless batch computing made simple.
499 lines (498 loc) • 21.4 kB
TypeScript
import { FaastModule } from "../index";
import { AwsOptions } from "./aws/aws-faast";
import { FunctionStats, CommonOptions } from "./provider";
import { PropertiesExcept, AnyFunction } from "./types";
/**
* A line item in the cost estimate, including the resource usage metric
* measured and its pricing.
* @public
*/
export declare class CostMetric {
/** The name of the cost metric, e.g. `functionCallDuration` */
readonly name: string;
/** The price in USD per unit measured. */
readonly pricing: number;
/** The name of the units that pricing is measured in for this metric. */
readonly unit: string;
/** The measured value of the cost metric, in units. */
readonly measured: number;
/**
* The plural form of the unit name. By default the plural form will be the
* name of the unit with "s" appended at the end, unless the last letter is
* capitalized, in which case there is no plural form (e.g. "GB").
*/
readonly unitPlural?: string;
/**
* An optional comment, usually providing a link to the provider's pricing
* page and other data.
*/
readonly comment?: string;
/**
* True if this cost metric is only for informational purposes (e.g. AWS's
* `logIngestion`) and does not contribute cost.
*/
readonly informationalOnly?: boolean;
/** @internal */
constructor(arg: PropertiesExcept<CostMetric, AnyFunction>);
/**
* The cost contribution of this cost metric. Equal to
* {@link CostMetric.pricing} * {@link CostMetric.measured}.
*/
cost(): number;
/**
* Return a string with the cost estimate for this metric, omitting
* comments.
*/
describeCostOnly(): string;
/** Describe this cost metric, including comments. */
toString(): string;
}
/**
* A summary of the costs incurred by a faast.js module at a point in time.
* Output of {@link FaastModule.costSnapshot}.
* @remarks
* Cost information provided by faast.js is an estimate. It is derived from
* internal faast.js measurements and not by consulting data provided by your
* cloud provider.
*
* **Faast.js does not guarantee the accuracy of cost estimates.**
*
* **Use at your own risk.**
*
* Example using AWS:
* ```typescript
* const faastModule = await faast("aws", m);
* try {
* // Invoke faastModule.functions.*
* } finally {
* await faastModule.cleanup();
* console.log(`Cost estimate:`);
* console.log(`${await faastModule.costSnapshot()}`);
* }
* ```
*
* AWS example output:
* ```text
* Cost estimate:
* functionCallDuration $0.00002813/second 0.6 second $0.00001688 68.4% [1]
* sqs $0.00000040/request 9 requests $0.00000360 14.6% [2]
* sns $0.00000050/request 5 requests $0.00000250 10.1% [3]
* functionCallRequests $0.00000020/request 5 requests $0.00000100 4.1% [4]
* outboundDataTransfer $0.09000000/GB 0.00000769 GB $0.00000069 2.8% [5]
* logIngestion $0.50000000/GB 0 GB $0 0.0% [6]
* ---------------------------------------------------------------------------------------
* $0.00002467 (USD)
*
* * Estimated using highest pricing tier for each service. Limitations apply.
* ** Does not account for free tier.
* [1]: https://aws.amazon.com/lambda/pricing (rate = 0.00001667/(GB*second) * 1.6875 GB = 0.00002813/second)
* [2]: https://aws.amazon.com/sqs/pricing
* [3]: https://aws.amazon.com/sns/pricing
* [4]: https://aws.amazon.com/lambda/pricing
* [5]: https://aws.amazon.com/ec2/pricing/on-demand/#Data_Transfer
* [6]: https://aws.amazon.com/cloudwatch/pricing/ - Log ingestion costs not currently included.
* ```
*
* A cost snapshot contains several {@link CostMetric} values. Each `CostMetric`
* summarizes one component of the overall cost of executing the functions so
* far. Some cost metrics are common to all faast providers, and other metrics
* are provider-specific. The common metrics are:
*
* - `functionCallDuration`: the estimated billed CPU time (rounded to the next
* 100ms) consumed by completed cloud function calls. This is the metric that
* usually dominates cost.
*
* - `functionCallRequests`: the number of invocation requests made. Most
* providers charge for each invocation.
*
* Provider-specific metrics vary. For example, AWS has the following additional
* metrics:
*
* - `sqs`: AWS Simple Queueing Service. This metric captures the number of
* queue requests made to insert and retrieve queued results (each 64kb chunk
* is counted as an additional request). SQS is used even if
* {@link CommonOptions.mode} is not set to `"queue"`, because it is necessary
* for monitoring cloud function invocations.
*
* - `sns`: AWS Simple Notification Service. SNS is used to invoke Lambda
* functions when {@link CommonOptions.mode} is `"queue"`.
*
* - `outboundDataTransfer`: an estimate of the network data transferred out
* from the cloud provider for this faast.js module. This estimate only counts
* data returned from cloud function invocations and infrastructure that
* faast.js sets up. It does not count any outbound data sent by your cloud
* functions that are not known to faast.js. Note that if you run faast.js on
* EC2 in the same region (see {@link AwsOptions.region}), then the data
* transfer costs will be zero (however, the cost snapshot will not include
* EC2 costs). Also note that if your cloud function transfers data from/to S3
* buckets in the same region, there is no cost as long as that data is not
* returned from the function.
*
* - `logIngestion`: this cost metric is always zero for AWS. It is present to
* remind the user that AWS charges for log data ingested by CloudWatch Logs
* that are not measured by faast.js. Log entries may arrive significantly
* after function execution completes, and there is no way for faast.js to
* know exactly how long to wait, therefore it does not attempt to measure
* this cost. In practice, if your cloud functions do not perform extensive
* logging on all invocations, log ingestion costs from faast.js are likely to
* be low or fall within the free tier.
*
* The Local provider has no extra metrics.
*
* Prices are retrieved dynamically from AWS and cached locally.
* Cached prices expire after 24h. For each cost metric, faast.js uses the
* highest price tier to compute estimated pricing.
*
* Cost estimates do not take free tiers into account.
* @public
*/
export declare class CostSnapshot {
/** The {@link Provider}, e.g. "aws" */
readonly provider: string;
/**
* The options used to initialize the faast.js module where this cost
* snapshot was generated.
*/
readonly options: CommonOptions | AwsOptions;
/** The function statistics that were used to compute prices. */
readonly stats: FunctionStats;
/**
* The cost metric components for this cost snapshot. See
* {@link CostMetric}.
*/
readonly costMetrics: CostMetric[];
/** @internal */
constructor(
/** The {@link Provider}, e.g. "aws" */
provider: string,
/**
* The options used to initialize the faast.js module where this cost
* snapshot was generated.
*/
options: CommonOptions | AwsOptions, stats: FunctionStats, costMetrics?: CostMetric[]);
/** Sum of cost metrics. */
total(): number;
/** A summary of all cost metrics and prices in this cost snapshot. */
toString(): string;
/**
* Comma separated value output for a cost snapshot.
* @remarks
* The format is "metric,unit,pricing,measured,cost,percentage,comment".
*
* Example output:
* ```text
* metric,unit,pricing,measured,cost,percentage,comment
* functionCallDuration,second,0.00002813,0.60000000,0.00001688,64.1% ,"https://aws.amazon.com/lambda/pricing (rate = 0.00001667/(GB*second) * 1.6875 GB = 0.00002813/second)"
* functionCallRequests,request,0.00000020,5,0.00000100,3.8% ,"https://aws.amazon.com/lambda/pricing"
* outboundDataTransfer,GB,0.09000000,0.00000844,0.00000076,2.9% ,"https://aws.amazon.com/ec2/pricing/on-demand/#Data_Transfer"
* sqs,request,0.00000040,13,0.00000520,19.7% ,"https://aws.amazon.com/sqs/pricing"
* sns,request,0.00000050,5,0.00000250,9.5% ,"https://aws.amazon.com/sns/pricing"
* logIngestion,GB,0.50000000,0,0,0.0% ,"https://aws.amazon.com/cloudwatch/pricing/ - Log ingestion costs not currently included."
* ```
*/
csv(): string;
/** @internal */
push(metric: CostMetric): void;
/**
* Find a specific cost metric by name.
* @returns a {@link CostMetric} if found, otherwise `undefined`.
*/
find(name: string): CostMetric | undefined;
}
/**
* Analyze the cost of a workload across many provider configurations.
* @public
*/
export declare namespace CostAnalyzer {
/**
* An input to {@link CostAnalyzer.analyze}, specifying one
* configuration of faast.js to run against a workload. See
* {@link AwsOptions}.
* @public
*/
type Configuration = {
provider: "aws";
options: AwsOptions;
};
/**
* Default AWS cost analyzer configurations include all memory sizes for AWS
* Lambda.
* @remarks
* The default AWS cost analyzer configurations include every memory size
* from 128MB to 3008MB in 64MB increments. Each configuration has the
* following settings:
*
* ```typescript
* {
* provider: "aws",
* options: {
* mode: "https",
* memorySize,
* timeout: 300,
* gc: "off",
* childProcess: true
* }
* }
* ```
*
* Use `Array.map` to change or `Array.filter` to remove some of these
* configurations. For example:
*
* ```typescript
* const configsWithAtLeast1GB = awsConfigurations.filter(c => c.memorySize > 1024)
* const shorterTimeout = awsConfigurations.map(c => ({...c, timeout: 60 }));
* ```
* @public
*/
const awsConfigurations: Configuration[];
/**
* User-defined custom metrics for a workload. These are automatically
* summarized in the output; see {@link CostAnalyzer.Workload}.
* @public
*/
type WorkloadAttribute<A extends string> = {
[attr in A]: number;
};
/**
* A user-defined cost analyzer workload for {@link CostAnalyzer.analyze}.
* @public
* Example:
*/
interface Workload<T extends object, A extends string> {
/**
* The imported module that contains the cloud functions to test.
*/
funcs: T;
/**
* A function that executes cloud functions on
* `faastModule.functions.*`. The work function should return `void` if
* there are no custom workload attributes. Otherwise, it should return
* a {@link CostAnalyzer.WorkloadAttribute} object which maps
* user-defined attribute names to numerical values for the workload.
* For example, this might measure bandwidth or some other metric not
* tracked by faast.js, but are relevant for evaluating the
* cost-performance tradeoff of the configurations analyzed by the cost
* analyzer.
*/
work: (faastModule: FaastModule<T>) => Promise<WorkloadAttribute<A> | void>;
/**
* An array of configurations to run the work function against (see
* {@link CostAnalyzer.Configuration}). For example, each entry in the
* array may specify a provider, memory size, and other options.
* Default: {@link CostAnalyzer.awsConfigurations}.
*/
configurations?: Configuration[];
/**
* Combine {@link CostAnalyzer.WorkloadAttribute} instances returned
* from multiple workload executions (caused by value of
* {@link CostAnalyzer.Workload.repetitions}). The default is a function
* that takes the average of each attribute.
*/
summarize?: (summaries: WorkloadAttribute<A>[]) => WorkloadAttribute<A>;
/**
* Format an attribute value for console output. This is displayed by
* the cost analyzer when all of the repetitions for a configuration
* have completed. The default returns
* `${attribute}:${value.toFixed(1)}`.
*/
format?: (attr: A, value: number) => string;
/**
* Format an attribute value for CSV. The default returns
* `value.toFixed(1)`.
*/
formatCSV?: (attr: A, value: number) => string;
/**
* If true, do not output live results to the console. Can be useful for
* running the cost analyzer as part of automated tests. Default: false.
*/
silent?: boolean;
/**
* The number of repetitions to run the workload for each cost analyzer
* configuration. Higher repetitions help reduce the jitter in the
* results. Repetitions execute in the same FaastModule instance.
* Default: 10.
*/
repetitions?: number;
/**
* The amount of concurrency to allow. Concurrency can arise from
* multiple repetitions of the same configuration, or concurrenct
* executions of different configurations. This concurrency limit
* throttles the total number of concurrent workload executions across
* both of these sources of concurrency. Default: 64.
*/
concurrency?: number;
}
/**
* A cost estimate result for a specific cost analyzer configuration.
* @public
*/
interface Estimate<A extends string> {
/**
* The cost snapshot for the cost analysis of the specific (workload,
* configuration) combination. See {@link CostSnapshot}.
*/
costSnapshot: CostSnapshot;
/**
* The worload configuration that was analyzed. See
* {@link CostAnalyzer.Configuration}.
*/
config: Configuration;
/**
* Additional workload metrics returned from the work function. See
* {@link CostAnalyzer.WorkloadAttribute}.
*/
extraMetrics: WorkloadAttribute<A>;
}
/**
* Estimate the cost of a workload using multiple configurations and
* providers.
* @param userWorkload - a {@link CostAnalyzer.Workload} object specifying
* the workload to run and additional parameters.
* @returns A promise for a {@link CostAnalyzer.Result}
* @public
* @remarks
* It can be deceptively difficult to set optimal parameters for AWS Lambda
* and similar services. On the surface there appears to be only one
* parameter: memory size. Choosing more memory also gives more CPU
* performance, but it's unclear how much. It's also unclear where single
* core performance stops getting better. The workload cost analyzer solves
* these problems by making it easy to run cost experiments.
* ```text
* (AWS)
* ┌───────┐
* ┌────▶│ 128MB │
* │ └───────┘
* │ ┌───────┐
* ┌─────────────────┐ ├────▶│ 256MB │
* ┌──────────────┐ │ │ │ └───────┘
* │ workload │───▶│ │ │ ...
* └──────────────┘ │ │ │ ┌───────┐
* │ cost analyzer │─────┼────▶│3008MB │
* ┌──────────────┐ │ │ └───────┘
* │configurations│───▶│ │
* └──────────────┘ │ │
* └─────────────────┘
*
* ```
* `costAnalyzer` is the entry point. It automatically runs this workload
* against multiple configurations in parallel. Then it uses faast.js' cost
* snapshot mechanism to automatically determine the price of running the
* workload with each configuration.
*
* Example:
*
* ```typescript
* // functions.ts
* export function randomNumbers(n: number) {
* let sum = 0;
* for (let i = 0; i < n; i++) {
* sum += Math.random();
* }
* return sum;
* }
*
* // cost-analyzer-example.ts
* import { writeFileSync } from "fs";
* import { CostAnalyzer, FaastModule } from "faastjs";
* import * as funcs from "./functions";
*
* async function work(faastModule: FaastModule<typeof funcs>) {
* await faastModule.functions.randomNumbers(100000000);
* }
*
* async function main() {
* const results = await CostAnalyzer.analyze({ funcs, work });
* writeFileSync("cost.csv", results.csv());
* }
*
* main();
* ```
*
* Example output (this is printed to `console.log` unless the
* {@link CostAnalyzer.Workload.silent} is `true`):
* ```text
* ✔ aws 128MB queue 15.385s 0.274σ $0.00003921
* ✔ aws 192MB queue 10.024s 0.230σ $0.00003576
* ✔ aws 256MB queue 8.077s 0.204σ $0.00003779
* ▲ ▲ ▲ ▲ ▲ ▲
* │ │ │ │ │ │
* provider │ mode │ stdev average
* │ │ execution estimated
* memory │ time cost
* size │
* average cloud
* execution time
* ```
*
* The output lists the provider, memory size, ({@link CommonOptions.mode}),
* average time of a single execution of the workload, the standard
* deviation (in seconds) of the execution time, and average estimated cost
* for a single run of the workload.
*
* The "execution time" referenced here is not wall clock time, but rather
* execution time in the cloud function. The execution time does not include
* any time the workload spends waiting locally. If the workload invokes
* multiple cloud functions, their execution times will be summed even if
* they happen concurrently. This ensures the execution time and cost are
* aligned.
*/
function analyze<T extends object, A extends string>(userWorkload: Workload<T, A>): Promise<Result<T, A>>;
/**
* Cost analyzer results for each workload and configuration.
* @remarks
* The `estimates` property has the cost estimates for each configuration.
* See {@link CostAnalyzer.Estimate}.
* @public
*/
class Result<T extends object, A extends string> {
/** The workload analyzed. */
readonly workload: Required<Workload<T, A>>;
/**
* Cost estimates for each configuration of the workload. See
* {@link CostAnalyzer.Estimate}.
*/
readonly estimates: Estimate<A>[];
/** @internal */
constructor(
/** The workload analyzed. */
workload: Required<Workload<T, A>>,
/**
* Cost estimates for each configuration of the workload. See
* {@link CostAnalyzer.Estimate}.
*/
estimates: Estimate<A>[]);
/**
* Comma-separated output of cost analyzer. One line per cost analyzer
* configuration.
* @remarks
* The columns are:
*
* - `memory`: The memory size allocated.
*
* - `cloud`: The cloud provider.
*
* - `mode`: See {@link CommonOptions.mode}.
*
* - `options`: A string summarizing other faast.js options applied to the
* `workload`. See {@link CommonOptions}.
*
* - `completed`: Number of repetitions that successfully completed.
*
* - `errors`: Number of invocations that failed.
*
* - `retries`: Number of retries that were attempted.
*
* - `cost`: The average cost of executing the workload once.
*
* - `executionTime`: the aggregate time spent executing on the provider for
* all cloud function invocations in the workload. This is averaged across
* repetitions.
*
* - `executionTimeStdev`: The standard deviation of `executionTime`.
*
* - `billedTime`: the same as `exectionTime`, except rounded up to the next
* 100ms for each invocation. Usually very close to `executionTime`.
*/
csv(): string;
}
}