Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -189,4 +189,13 @@ cache
*.iml

# MacOS Finder
**/.DS_Store
**/.DS_Store

# Local benchmarks
out_benchmark.json
out_storage.json
local_deployment.json
experiments.json

# Editors
.vscode
29 changes: 29 additions & 0 deletions benchmarks/100.webapps/110.dynamic-html/bun/function.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
const Mustache = require('mustache'),
fs = require('fs'),
path = require('path');

function random(b, e) {
return Math.round(Math.random() * (e - b) + b);
}

exports.handler = async function(event) {
var random_numbers = new Array(event.random_len);
for(var i = 0; i < event.random_len; ++i) {
random_numbers[i] = random(0, 100);
}
var input = {
cur_time: new Date().toLocaleString(),
username: event.username,
random_numbers: random_numbers
};

var file = path.resolve(path.dirname(process.execPath), 'templates', 'template.html');
return new Promise((resolve, reject) => {
fs.readFile(file, "utf-8",
function(err, data) {
if(err) reject(err);
resolve(Mustache.render(data, input));
}
);
});
};
10 changes: 10 additions & 0 deletions benchmarks/100.webapps/110.dynamic-html/bun/init.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/bin/bash

DIR=$1
VERBOSE=$2
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
path="${SCRIPT_DIR}/templates/"
if [ "$VERBOSE" = true ]; then
echo "Update ${DIR} with static templates ${path}"
fi
cp -r ${SCRIPT_DIR}/templates ${DIR}
10 changes: 10 additions & 0 deletions benchmarks/100.webapps/110.dynamic-html/bun/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"name": "",
"version": "1.0.0",
"description": "",
"author": "",
"license": "",
"dependencies": {
"mustache": "^3.2.1"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<!DOCTYPE html>
<html>
<head>
<title>Randomly generated data.</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="http://netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" rel="stylesheet" media="screen">
<style type="text/css">
.container {
max-width: 500px;
padding-top: 100px;
}
</style>
</head>
<body>
<div class="container">
<p>Welcome {{username}}!</p>
<p>Data generated at: {{cur_time}}!</p>
<p>Requested random numbers:</p>
<ul>
{{#random_numbers}}
<li>{{.}}</li>
{{/random_numbers}}
</ul>
</div>
</body>
</html>
6 changes: 5 additions & 1 deletion benchmarks/100.webapps/110.dynamic-html/config.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
{
"timeout": 10,
"memory": 128,
"languages": ["python", "nodejs"],
"languages": [
"python",
"nodejs",
"bun"
],
"modules": []
}
2 changes: 1 addition & 1 deletion benchmarks/100.webapps/110.dynamic-html/nodejs/function.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ exports.handler = async function(event) {
random_numbers: random_numbers
};

var file = path.resolve(__dirname, 'templates', 'template.html');
var file = path.resolve(path.dirname(process.execPath), 'templates', 'template.html');
return new Promise((resolve, reject) => {
fs.readFile(file, "utf-8",
function(err, data) {
Expand Down
47 changes: 47 additions & 0 deletions benchmarks/wrappers/aws/bun/handler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@

const path = require('path'), fs = require('fs');

function process_output(data, http_trigger) {
if(http_trigger)
return JSON.stringify(data);
else
return data;
}

exports.handler = async function(event, context) {
var begin = Date.now()/1000;
var start = process.hrtime();
var http_trigger = "body" in event;
var input_data = http_trigger ? JSON.parse(event.body) : event
var func = require('./function') // Different to nodejs, since the benchmark-function will be on the same level at the time of compilation
var ret = func.handler(input_data);
return ret.then(
(result) => {
var elapsed = process.hrtime(start);
var end = Date.now()/1000;
var micro = elapsed[1] / 1e3 + elapsed[0] * 1e6;

var is_cold = false;
var fname = path.join('/tmp','cold_run');
if(!fs.existsSync(fname)) {
is_cold = true;
fs.closeSync(fs.openSync(fname, 'w'));
}
return {
statusCode: 200,
body: process_output({
begin: begin,
end: end,
compute_time: micro,
results_time: 0,
result: {output: result},
is_cold: is_cold,
request_id: context.awsRequestId
}, http_trigger)
};
},
(error) => {
throw(error);
}
);
}
40 changes: 40 additions & 0 deletions benchmarks/wrappers/aws/bun/runtime.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* Custom runtime while loop for AWS lambda.
* Listens for function events, executes handler, and returns results.
*
* ENV variables based on https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html#configuration-envvars-runtime
* API endpoints based on https://docs.aws.amazon.com/lambda/latest/dg/runtimes-api.html
*/

import { handler } from "./handler.js";

const RUNTIME_API = process.env.AWS_LAMBDA_RUNTIME_API;
const API_BASE = `http://${RUNTIME_API}/2018-06-01/runtime`;

while (true) {
const nextResponse = await fetch(`${API_BASE}/invocation/next`);
const event = await nextResponse.json();
const requestId = nextResponse.headers.get("Lambda-Runtime-Aws-Request-Id");

// NOTE: If more context is needed inside the handler, they can be added here
const context = { awsRequestId: requestId };

try {
const response = await handler(event, context);

await fetch(`${API_BASE}/invocation/${requestId}/response`, {
method: "POST",
body: JSON.stringify(response),
});
} catch (error) {
console.error(error);
await fetch(`${API_BASE}/invocation/${requestId}/error`, {
method: "POST",
body: JSON.stringify({
errorMessage: error.message,
errorType: "Runtime.UserCodeError",
stackTrace: error.stack ? error.stack.split("\n") : [],
}),
});
}
}
50 changes: 50 additions & 0 deletions benchmarks/wrappers/aws/bun/storage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@

const aws = require('aws-sdk'),
fs = require('fs'),
path = require('path'),
uuid = require('uuid'),
util = require('util'),
stream = require('stream');

class aws_storage {

constructor() {
this.S3 = new aws.S3();
}

unique_name(file) {
let name = path.parse(file);
let uuid_name = uuid.v4().split('-')[0];
return path.join(name.dir, util.format('%s.%s%s', name.name, uuid_name, name.ext));
}

upload(bucket, file, filepath) {
var upload_stream = fs.createReadStream(filepath);
let uniqueName = this.unique_name(file);
let params = {Bucket: bucket, Key: uniqueName, Body: upload_stream};
var upload = this.S3.upload(params);
return [uniqueName, upload.promise()];
};

download(bucket, file, filepath) {
var file = fs.createWriteStream(filepath);
this.S3.getObject( {Bucket: bucket, Key: file} ).createReadStream().pipe(file);
};

uploadStream(bucket, file) {
var write_stream = new stream.PassThrough();
let uniqueName = this.unique_name(file);
// putObject won't work correctly for streamed data (length has to be known before)
// https://stackoverflow.com/questions/38442512/difference-between-upload-and-putobject-for-uploading-a-file-to-s3
var upload = this.S3.upload( {Bucket: bucket, Key: uniqueName, Body: write_stream} );
return [write_stream, upload.promise(), uniqueName];
};

// We return a promise to match the API for other providers
downloadStream(bucket, file) {
// AWS.Request -> read stream
let downloaded = this.S3.getObject( {Bucket: bucket, Key: file} ).createReadStream();
return Promise.resolve(downloaded);
};
};
exports.storage = aws_storage;
61 changes: 61 additions & 0 deletions benchmarks/wrappers/local/bun/storage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@

const minio = require('minio'),
path = require('path'),
uuid = require('uuid'),
util = require('util'),
stream = require('stream');

class minio_storage {

constructor() {
let address = process.env.MINIO_ADDRESS;
let access_key = process.env.MINIO_ACCESS_KEY;
let secret_key = process.env.MINIO_SECRET_KEY;
this.client = new minio.Client(
{
endPoint: address.split(':')[0],
port: parseInt(address.split(':')[1], 10),
accessKey: access_key,
secretKey: secret_key,
useSSL: false
}
);
}

unique_name(file) {
let name = path.parse(file);
let uuid_name = uuid.v4().split('-')[0];
return path.join(name.dir, util.format('%s.%s%s', name.name, uuid_name, name.ext));
}

upload(bucket, file, filepath) {
let uniqueName = this.unique_name(file);
return [uniqueName, this.client.fPutObject(bucket, uniqueName, filepath)];
};

download(bucket, file, filepath) {
return this.client.fGetObject(bucket, file, filepath);
};

uploadStream(bucket, file) {
var write_stream = new stream.PassThrough();
let uniqueName = this.unique_name(file);
let promise = this.client.putObject(bucket, uniqueName, write_stream, write_stream.size);
return [write_stream, promise, uniqueName];
};

downloadStream(bucket, file) {
var read_stream = new stream.PassThrough();
return this.client.getObject(bucket, file);
};

static get_instance() {
if(!this.instance) {
this.instance = new storage();
}
return this.instance;
}


};
exports.storage = minio_storage;
48 changes: 48 additions & 0 deletions config/systems.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,29 @@
],
"packages": []
}
},
"bun": {
"base_images": {
"x64": {
"1.2": "oven/bun:1.2",
"1.3": "oven/bun:1.3"
},
"arm64": {
"1.2": "oven/bun:1.2",
"1.3": "oven/bun:1.3"
}
},
"images": [
"run",
"build"
],
"username": "docker_user",
"deployment": {
"files": [
"storage.js"
],
"packages": []
}
}
},
"architecture": ["x64"],
Expand Down Expand Up @@ -121,6 +144,31 @@
"uuid": "3.4.0"
}
}
},
"bun": {
"base_images": {
"x64": {
"1.2": "oven/bun:1.2",
"1.3": "oven/bun:1.3"
},
"arm64": {
"1.2": "oven/bun:1.2",
"1.3": "oven/bun:1.3"
}
},
"images": [
"build"
],
"deployment": {
"files": [
"handler.js",
"storage.js",
"runtime.js"
],
"packages": {
"uuid": "3.4.0"
}
}
}
},
"architecture": ["x64", "arm64"],
Expand Down
16 changes: 16 additions & 0 deletions dockerfiles/aws/bun/Dockerfile.build
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
ARG BASE_IMAGE
FROM ${BASE_IMAGE}

COPY --from=tianon/gosu:1.19-debian /usr/local/bin/gosu /usr/local/bin/gosu

RUN mkdir -p /sebs/
# For AWS we need to compile the bun/js files into a bootstrap binary
COPY dockerfiles/bun_installer.sh /sebs/installer.sh
COPY dockerfiles/entrypoint.sh /sebs/entrypoint.sh
RUN chmod +x /sebs/entrypoint.sh

# useradd and groupmod is installed in /usr/sbin which is not in PATH
ENV PATH=/usr/sbin:$PATH
ENV SCRIPT_FILE=/mnt/function/package.sh
CMD /bin/bash /sebs/installer.sh
ENTRYPOINT ["/sebs/entrypoint.sh"]
Loading