├── .gitignore
├── LICENSE
├── README.md
├── bin
└── slsperf.js
├── index.js
├── package.json
└── providers
├── alphabet
├── index.js
└── serverless.yml
├── amazon
├── handler.js
└── serverless.yml
├── ibm
├── handler.js
└── serverless.yml
├── index.js
├── microsoft
├── .gitignore
├── handler.js
└── serverless.yml
└── prototype
├── index.js
└── serverless.yml
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | *.log
3 | npm-debug.log
4 |
5 | # Runtime data
6 | pids
7 | *.pid
8 | *.seed
9 | dist
10 |
11 | # Directory for instrumented libs generated by jscoverage/JSCover
12 | lib-cov
13 |
14 | # Coverage directory used by tools like istanbul
15 | coverage
16 |
17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
18 | .grunt
19 |
20 | # node-waf configuration
21 | .lock-wscript
22 |
23 | # Compiled binary addons (http://nodejs.org/api/addons.html)
24 | build/Release
25 |
26 | # Dependency directory
27 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
28 | node_modules
29 |
30 | # IDE stuff
31 | **/.idea
32 |
33 | # OS stuff
34 | .DS_Store
35 | .tmp
36 |
37 | # Serverless stuff
38 | .serverless
39 | admin.env
40 | .env
41 | tmp
42 | .coveralls.yml
43 | tmpdirs-serverless
44 |
45 | # ESLint cache
46 | .eslintcache
47 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2017 Garrett McGrath
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 | SOFTWARE.
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Serverless Performance
2 |
3 | This package allows you to measure performance characteristics of serverless platforms such as AWS Lambda, Azure Functions, Google Cloud Functions, and IBM OpenWhisk.
4 |
5 | The Serverless Framework is used as the deployment engine for this package.
6 |
7 | Note: this project is in its early stages, proceed cautiously. If you encounter issues, please report them and they will be fixed promptly.
8 |
9 | ## Getting Started
10 |
11 | ### Install Package
12 |
13 | This package can be installed via npm:
14 |
15 | ```bash
16 | npm install serverless-performance
17 | ```
18 |
19 | ### Configure Platforms
20 |
21 | You must configure credentials before using any of the supported platforms:
22 |
23 | * **AWS Lambda** can be configured by following this guide on the Serverless Framework website.
24 | * **IBM OpenWhisk** can be configured by following this guide on the Serverless Framework website.
25 | * **Azure Functions** can be configured by following this guide on the Serverless Framework website.
26 | * **Google Cloud Functions** can be configured by creating a `keyfile.json` file as described here.
27 | * **Prototype Platform** also compatible with a new prototype platform using a `prototypeServiceBaseUri` environment variable.
28 |
29 | ### Command Line Interface
30 |
31 | A command line tool for this package is available at `bin/slsperf.js`:
32 |
33 | ```
34 | Usage: slsperf [options]
35 |
36 | Options:
37 |
38 | -h, --help output usage information
39 | -p, --provider Serverless platform to target (amazon, ibm, microsoft, google, prototype)
40 | --project Name of the project to deploy Google Cloud Functions to
41 | --credentials Path of the file holding Google Cloud credentials
42 | -d, --duration Number of milliseconds the function should execute before returning
43 | -b, --backoff Runs a backoff test on the specified provider
44 | -c, --concurrency Runs a concurrency test on the specified provider
45 | -k, --keep-alive Maintains an invocation call to the specified provider
46 | -i, --iterations Number of times to run the test
47 |
48 | Examples:
49 |
50 | node slsperf.js -p amazon -d 0 -c -i 1 .
51 | ```
52 |
53 | ## Example Results
54 |
55 | The following are example results of running the performance tool on various serverless platforms. Details of the experimental setup can be found here.
56 |
57 | ### Concurrency Test
58 |
59 |
60 |
61 |
62 |
63 | The concurrency test is designed to measure the ability of serverless platforms to performantly scale and execute a function. The tool maintains invocation calls to the test function by reissuing each request immediately after receiving the response from the previous call. The test begins by maintaining a single invocation call in this way, and every 10 seconds adds an additional concurrent call, up to a maximum of 15 concurrent requests to the test function.
64 |
65 | ### Backoff Test
66 |
67 |
68 |
69 |
70 |
71 | The backoff test is designed to study the cold start times and expiration behaviors of function instances in the various platforms. The backoff test sends single invocation call to the test function at increasing intervals, ranging from one to thirty minutes.
72 |
--------------------------------------------------------------------------------
/bin/slsperf.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | // TODO: this script sucks; usage on empty, actual validation, ect.
4 |
5 | const fs = require('fs');
6 | const path = require('path');
7 |
8 | const program = require('commander');
9 |
10 | const slsperf = require(path.join(__dirname, '..'));
11 |
12 | let config = {
13 | provider: {},
14 | function: {},
15 | test: {}
16 | };
17 |
18 | let latencyTest = {
19 | type: 'latency',
20 | delayCallback: i => 60000 * (Math.floor((i - 1) / 5) + 1),
21 | maxDelay: 1800000
22 | };
23 |
24 | let throughputTest = {
25 | type: 'throughput',
26 | width: 15,
27 | duration: 10000,
28 | };
29 |
30 | let keepAliveTest = {
31 | type: 'latency',
32 | delayCallback: i => i ? 0 : 100,
33 | maxDelay: 101
34 | };
35 |
36 | program
37 | .usage('[options] ')
38 | .option(
39 | '-p, --provider ',
40 | 'Serverless platform to target (amazon, ibm, microsoft, google, prototype)',
41 | name => config.provider.name = name == 'google' ? 'alphabet' : name)
42 | .option(
43 | '--project ',
44 | 'Name of the project to deploy Google Cloud Functions to',
45 | name => config.provider.project = name)
46 | .option(
47 | '--credentials ',
48 | 'Path of the file holding Google Cloud credentials',
49 | path => config.provider.credentials = path)
50 | .option(
51 | '-d, --duration ',
52 | 'Number of milliseconds the function should execute before returning',
53 | ms => config.function.duration = parseInt(ms))
54 | .option(
55 | '-b, --backoff',
56 | 'Runs a backoff test on the specified provider',
57 | () => config.test = latencyTest)
58 | .option(
59 | '-c, --concurrency',
60 | 'Runs a concurrency test on the specified provider',
61 | () => config.test = throughputTest)
62 | .option(
63 | '-k, --keep-alive',
64 | 'Runs a keep-alive test on the specified provider',
65 | () => config.test = keepAliveTest)
66 | .option(
67 | '-i, --iterations ',
68 | 'Number of times to run the test',
69 | n => config.test.iterations = parseInt(n))
70 | .parse(process.argv);
71 |
72 | config.resultsDir = program.args[0];
73 |
74 | if (!fs.existsSync(config.resultsDir)) {
75 | fs.mkdirSync(config.resultsDir);
76 | }
77 |
78 | fs.writeFileSync(path.join(config.resultsDir, `${config.provider.name}_${config.test.type}_config.json`), JSON.stringify(config));
79 |
80 | let iteration = 0;
81 | fs.readdirSync(config.resultsDir).forEach(file => {
82 | if (file.match(/[a-z]+_[a-z]+_[0-9]+\.json/) != null) {
83 | let left = file.split('.')[0].split('_');
84 | if (left[0] == config.provider.name && left[1] == config.test.type) {
85 | let iterationFound = parseInt(left[2]);
86 | if (iterationFound >= iteration) {
87 | console.log(`Iteration ${iterationFound} already complete`);
88 | iteration = iterationFound + 1;
89 | }
90 | }
91 | }
92 | });
93 |
94 | if (iteration < config.test.iterations) {
95 | console.log(`Starting iteration ${iteration}`);
96 | slsperf.run(config, false, function processOutput(output) {
97 | let outputFile = path.join(config.resultsDir, `${config.provider.name}_${config.test.type}_${iteration}.json`);
98 | fs.writeFileSync(outputFile, JSON.stringify(output, null, 4));
99 | console.log(`Finished iteration ${iteration}`);
100 | if (++iteration < config.test.iterations) {
101 | console.log(`Starting iteration ${iteration}`);
102 | slsperf.run(config, true, processOutput);
103 | }
104 | });
105 | }
106 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const fs = require('fs');
4 |
5 | const request = require('request');
6 |
7 | const providers = require('./providers');
8 |
9 | function run(config, verifyRemoval, callback) {
10 | providers.prepareFunctions(config.provider, () => {
11 | providers.removeFunctions(config.provider, removeCode => {
12 | if (verifyRemoval && removeCode != 0) {
13 | throw new Error('Function removal failed with code ' + removeCode);
14 | }
15 |
16 | providers.deployFunctions(config.provider, (deployCode, uri) => {
17 | if (deployCode != 0) {
18 | throw new Error('Function deployment failed with code ' + deployCode);
19 | }
20 |
21 | switch (config.test.type) {
22 | case 'latency':
23 | executeLatencyTest(
24 | [],
25 | 0,
26 | config.test.delayCallback,
27 | config.test.maxDelay,
28 | config.function.duration,
29 | uri,
30 | callback);
31 | break;
32 | case 'throughput':
33 | executeThroughputTest(
34 | [],
35 | 1,
36 | config.test.width,
37 | config.test.duration,
38 | config.function.duration,
39 | uri,
40 | callback);
41 | break;
42 | }
43 | });
44 | });
45 | });
46 | }
47 |
48 | function executeLatencyTest(results, delay, delayCallback, maxDelay, functionDuration, uri, callback) {
49 | executeFunction(uri, functionDuration, result => {
50 | results.delay = delay;
51 | results.push(result);
52 |
53 | if (delay >= maxDelay) {
54 | callback(results);
55 | return;
56 | }
57 |
58 | let nextDelay = delay + delayCallback(results.length);
59 | console.log(`Delaying for ${nextDelay}ms`);
60 |
61 | setTimeout(
62 | executeLatencyTest,
63 | nextDelay,
64 | results, nextDelay, delayCallback, maxDelay, functionDuration, uri, callback);
65 | });
66 | }
67 |
68 | function executeThroughputTest(results, currentWidth, maxWidth, testDuration, functionDuration, uri, callback) {
69 | if (currentWidth > maxWidth) {
70 | callback(results);
71 | return;
72 | }
73 | console.log(`Execution throughput test with width: ${currentWidth}`);
74 | results[currentWidth] = results[currentWidth] || [];
75 |
76 | let startTime = null;
77 | let stageCompleted = false;
78 |
79 | for (let i = 0; i < currentWidth; i++) {
80 | executeFunction(uri, functionDuration, function processResult(result) {
81 | if (startTime == null) {
82 | startTime = Date.now();
83 | }
84 |
85 | results[currentWidth].push(result);
86 |
87 | if (Date.now() - startTime > testDuration) {
88 | if (!stageCompleted) {
89 | stageCompleted = true;
90 |
91 | executeThroughputTest(
92 | results,
93 | currentWidth + 1,
94 | maxWidth,
95 | testDuration,
96 | functionDuration,
97 | uri,
98 | callback);
99 | }
100 | } else {
101 | executeFunction(uri, functionDuration, processResult);
102 | }
103 | });
104 | }
105 | }
106 |
107 | function executeFunction(uri, duration, callback) {
108 | request.post({
109 | url: uri,
110 | body: JSON.stringify({ duration: duration }),
111 | headers: { 'Content-Type': 'application/json' },
112 | time: true
113 | }, (err, res, body) => {
114 | if (err) throw err;
115 | if (res.statusCode != 200) {
116 | throw new Error('Unexpected response. Status code: ' + res.statusCode + '. Body: ' + body);
117 | }
118 |
119 | let parsedBody = JSON.parse(body);
120 |
121 | let actualDuration = parsedBody.duration != undefined ? parsedBody.duration : parsedBody.output.duration;
122 | let instanceId = parsedBody.id || parsedBody.output.id;
123 |
124 | let overhead = res.elapsedTime - actualDuration;
125 |
126 | console.log(`Execution latency of ${overhead}ms`);
127 |
128 | callback({
129 | executionOverhead: overhead,
130 | requestStart: res.request.startTime,
131 | requestDuration: res.elapsedTime,
132 | functionDuration: actualDuration,
133 | instanceId: instanceId,
134 | body: parsedBody
135 | });
136 | });
137 | }
138 |
139 | module.exports = {
140 | run: run
141 | };
142 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "serverless-performance",
3 | "version": "0.0.46",
4 | "description": "This package allows you to measure performance characteristics of serverless platforms such as AWS Lambda, Azure Functions, IBM OpenWhisk, and Google Cloud Functions.",
5 | "main": "index.js",
6 | "dependencies": {
7 | "request": "^2.79.0",
8 | "serverless": "git+https://github.com/mgarrettm/serverless",
9 | "serverless-azure-functions": "git+https://github.com/serverless/serverless-azure-functions",
10 | "serverless-google-cloudfunctions": "git+https://github.com/serverless/serverless-google-cloudfunctions.git",
11 | "serverless-openwhisk": "^0.4.4",
12 | "serverless-prototype-plugin": "0.0.20",
13 | "url-regex": "^4.0.0",
14 | "yamljs": "^0.2.8"
15 | },
16 | "devDependencies": {},
17 | "scripts": {
18 | "test": "echo \"Error: no test specified\" && exit 1"
19 | },
20 | "bin": {
21 | "serverless-performance": "bin/slsperf.js",
22 | "slsperf": "bin/slsperf.js"
23 | },
24 | "repository": {
25 | "type": "git",
26 | "url": "git+https://github.com/mgarrettm/serverless-performance.git"
27 | },
28 | "keywords": [
29 | "serverless",
30 | "performance"
31 | ],
32 | "author": "Garrett McGrath",
33 | "license": "MIT",
34 | "bugs": {
35 | "url": "https://github.com/mgarrettm/serverless-performance/issues"
36 | },
37 | "homepage": "https://github.com/mgarrettm/serverless-performance#readme"
38 | }
39 |
--------------------------------------------------------------------------------
/providers/alphabet/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var id;
4 |
5 | exports.test = (request, response) => {
6 | var start = Date.now();
7 |
8 | if (typeof id === 'undefined') {
9 | id = uuid();
10 | }
11 |
12 | while (Date.now() < start + request.body.duration) {}
13 |
14 | response.status(200).send({
15 | duration: Date.now() - start,
16 | id: id
17 | });
18 | };
19 |
20 | function uuid(a) {
21 | return a?(a^Math.random()*16>>a/4).toString(16):([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,uuid);
22 | }
--------------------------------------------------------------------------------
/providers/alphabet/serverless.yml:
--------------------------------------------------------------------------------
1 | service: test
2 | provider:
3 | name: google
4 | runtime: nodejs4.3
5 | project: your-project-name
6 | credentials: '~/.gcloud/keyfile.json'
7 | plugins:
8 | - serverless-google-cloudfunctions
9 | package:
10 | exclude: ['node_modules/**', .gitignore, package.json, '.git/**']
11 | functions:
12 | test0: {handler: test, availableMemoryMb: 512, events: [{http: /}]}
13 |
--------------------------------------------------------------------------------
/providers/amazon/handler.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var id;
4 |
5 | module.exports.test = (event, context, callback) => {
6 | var start = Date.now();
7 |
8 | if (typeof id === 'undefined') {
9 | id = uuid();
10 | }
11 |
12 | let duration = JSON.parse(event.body).duration;
13 | while (Date.now() < start + duration) {}
14 |
15 | callback(null, {
16 | statusCode: 200,
17 | body: JSON.stringify({
18 | duration: Date.now() - start,
19 | id: id
20 | })
21 | });
22 | };
23 |
24 | function uuid(a) {
25 | return a?(a^Math.random()*16>>a/4).toString(16):([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,uuid);
26 | }
--------------------------------------------------------------------------------
/providers/amazon/serverless.yml:
--------------------------------------------------------------------------------
1 | service: amazon
2 | provider:
3 | name: aws
4 | runtime: nodejs4.3
5 | stage: dev
6 | region: us-east-1
7 | memorySize: 512
8 | functions:
9 | test-fppbjs: {handler: handler.test, events: [{http: {path: /test, method: post, private: false}}]}
10 |
--------------------------------------------------------------------------------
/providers/ibm/handler.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var id;
4 |
5 | function test(params) {
6 | var start = Date.now();
7 |
8 | if (typeof id === 'undefined') {
9 | id = uuid();
10 | }
11 |
12 | while (Date.now() < start + params.duration) {}
13 |
14 | return {
15 | duration: Date.now() - start,
16 | id: id
17 | };
18 | }
19 |
20 | function uuid(a) {
21 | return a?(a^Math.random()*16>>a/4).toString(16):([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,uuid);
22 | }
23 |
24 | exports.test = test;
25 |
--------------------------------------------------------------------------------
/providers/ibm/serverless.yml:
--------------------------------------------------------------------------------
1 | service: ibm
2 | provider:
3 | name: openwhisk
4 | functions:
5 | test: {handler: handler.test, memory: 512, events: [{http: 'POST test'}]}
6 | plugins:
7 | - serverless-openwhisk
8 |
--------------------------------------------------------------------------------
/providers/index.js:
--------------------------------------------------------------------------------
1 | // TODO: have you ever heard of promises?
2 | // TODO: better way to call serverless? not exec?
3 |
4 | 'use strict';
5 |
6 | const exec = require('child_process').exec;
7 | const fs = require('fs');
8 | const path = require('path');
9 |
10 | const urlRegex = require('url-regex');
11 | const YAML = require('yamljs');
12 |
13 | const serverlessPath = path.join(require.resolve('serverless'), '../../bin/serverless');
14 |
15 | let env = Object.create(process.env);
16 | env.SLS_IGNORE_WARNING = '*';
17 | env.SLS_DEBUG = '*';
18 |
19 | function prepareFunctions(provider, callback) {
20 | let ymlPath = path.join(__dirname, provider.name, 'serverless.yml');
21 |
22 | YAML.load(ymlPath, yml => {
23 | yml = modifyYAML(provider, yml);
24 |
25 | let ymlString = YAML.stringify(yml, null, 4);
26 |
27 | fs.writeFile(ymlPath, ymlString, err => {
28 | if (err) throw err;
29 | callback();
30 | });
31 | });
32 | }
33 |
34 | function deployFunctions(provider, callback) {
35 | let deploymentPath = path.join(__dirname, provider.name);
36 |
37 | let child = exec('cd ' + deploymentPath + '&& node ' + serverlessPath + ' deploy', { env: env });
38 |
39 | let stdout = '';
40 | child.stdout.on('data', data => {
41 | stdout += data;
42 | process.stdout.write(data);
43 | });
44 |
45 | child.stderr.on('data', data => process.stderr.write(data));
46 |
47 | child.on('close', code => {
48 | let uri = [];
49 | if (code == 0) {
50 | uri = extractUri(provider, stdout.match(urlRegex()));
51 | }
52 |
53 | callback(code, uri);
54 | });
55 | }
56 |
57 | function removeFunctions(provider, callback) {
58 | let deploymentPath = path.join(__dirname, provider.name);
59 |
60 | let child = exec('cd ' + deploymentPath + '&& node ' + serverlessPath + ' remove', { env: env });
61 |
62 | child.stdout.on('data', data => process.stdout.write(data));
63 | child.stderr.on('data', data => process.stderr.write(data));
64 |
65 | child.on('close', code => {
66 | callback(code);
67 | })
68 | }
69 |
70 | function modifyYAML(provider, yml) {
71 | switch(provider.name) {
72 | case 'alphabet':
73 | provider.project = provider.project || generateRandomLetters(12);
74 | yml.provider.project = provider.project;
75 | provider.credentials = provider.credentials || '~/.gcloud/keyfile.json';
76 | yml.provider.credentials = provider.credentials;
77 | break;
78 | case 'microsoft':
79 | provider.service = provider.service || generateRandomLetters(12);
80 | yml.service = provider.service;
81 | break;
82 | }
83 |
84 | yml.functions = generateFunctions(provider);
85 |
86 | return yml;
87 | }
88 |
89 | function generateFunctions(provider) {
90 | let functions = {};
91 |
92 | let functionName = 'test';
93 | if (provider.name == 'amazon') {
94 | functionName += '-' + generateRandomLetters(6);
95 | }
96 |
97 | switch(provider.name) {
98 | case 'alphabet':
99 | functions[functionName] = {
100 | handler: 'test',
101 | availableMemoryMb: 512,
102 | events: [{
103 | http: '/'
104 | }]
105 | };
106 | break;
107 | case 'amazon':
108 | functions[functionName] = {
109 | handler: 'handler.test',
110 | events: [{
111 | http: {
112 | path: '/test',
113 | method: 'post',
114 | private: false
115 | }
116 | }]
117 | };
118 | break;
119 | case 'ibm':
120 | functions[functionName] = {
121 | handler: 'handler.test',
122 | memory: 512,
123 | events: [{
124 | http: 'POST test'
125 | }]
126 | };
127 | break;
128 | case 'microsoft':
129 | functions[functionName] = {
130 | handler: 'handler.test',
131 | events: [{
132 | http: true,
133 | 'x-azure-settings': {
134 | authLevel: 'anonymous'
135 | }
136 | }]
137 | };
138 | break;
139 | case 'prototype':
140 | functions[functionName] = {
141 | handler: 'index.js',
142 | memorySize: 512
143 | };
144 | break;
145 | }
146 |
147 | return functions;
148 | }
149 |
150 | function extractUri(provider, uris) {
151 | switch (provider.name) {
152 | case 'microsoft':
153 | return 'http://' + provider.service + '.azurewebsites.net/api/test';
154 | default:
155 | return uris[0];
156 | }
157 | }
158 |
159 | function generateRandomLetters(length)
160 | {
161 | let possibilities = "abcdefghijklmnopqrstuvwxyz";
162 | let text = "";
163 | for(let i = 0; i < length; i++) {
164 | text += possibilities.charAt(Math.floor(Math.random() * possibilities.length));
165 | }
166 | return text;
167 | }
168 |
169 | module.exports = {
170 | prepareFunctions: prepareFunctions,
171 | deployFunctions: deployFunctions,
172 | removeFunctions: removeFunctions
173 | };
--------------------------------------------------------------------------------
/providers/microsoft/.gitignore:
--------------------------------------------------------------------------------
1 | # Hide functions directory
2 | functions
--------------------------------------------------------------------------------
/providers/microsoft/handler.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var id;
4 |
5 | module.exports.test = function (context, req) {
6 | var start = Date.now();
7 |
8 | if (typeof id === 'undefined') {
9 | id = uuid();
10 | }
11 |
12 | while (Date.now() < start + req.body.duration) {}
13 |
14 | context.done(null, { body: {
15 | duration: Date.now() - start,
16 | id: id
17 | }});
18 | };
19 |
20 | function uuid(a) {
21 | return a?(a^Math.random()*16>>a/4).toString(16):([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,uuid);
22 | }
23 |
--------------------------------------------------------------------------------
/providers/microsoft/serverless.yml:
--------------------------------------------------------------------------------
1 | service: your-service-name
2 | provider:
3 | name: azure
4 | location: 'East US'
5 | plugins:
6 | - serverless-azure-functions
7 | package:
8 | exclude: ['node_modules/**', .gitignore, package.json, '.git/**']
9 | functions:
10 | test0: {handler: handler.test, events: [{http: true, x-azure-settings: {authLevel: anonymous}}]}
11 |
--------------------------------------------------------------------------------
/providers/prototype/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var id;
4 |
5 | module.exports = function (input) {
6 | var start = Date.now();
7 |
8 | if (typeof id === 'undefined') {
9 | id = uuid();
10 | }
11 |
12 | while (Date.now() < start + input.duration) {}
13 |
14 | return {
15 | duration: Date.now() - start,
16 | id: id
17 | };
18 | };
19 |
20 | function uuid(a) {
21 | return a?(a^Math.random()*16>>a/4).toString(16):([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,uuid);
22 | }
23 |
--------------------------------------------------------------------------------
/providers/prototype/serverless.yml:
--------------------------------------------------------------------------------
1 | service: prototype
2 | provider:
3 | name: prototype
4 | location: 'East US'
5 | plugins:
6 | - serverless-prototype-plugin
7 | package:
8 | exclude: ['node_modules/**', .gitignore, package.json, '.git/**']
9 | functions:
10 | test0: {handler: index.js, memorySize: 512}
11 |
--------------------------------------------------------------------------------