<file_sep>FROM iojs:1.6.4

RUN npm install -g cb-cloud-benchmark

CMD [ "/bin/bash -c \"while true; do sleep 1; done\"" ]
<file_sep>import { Mock, Cluster } from "couchbase";

export default function (uri) {
	let cluster;

	if (uri) {
		cluster = new Cluster(uri);
	} else {
		cluster = new Cluster("couchbase://127.0.0.1");
	}

	return cluster.openBucket("benchmark");
}
<file_sep>#! /usr/bin/env node

import cluster from "cluster";

import cli from "../lib/cli";

cli(cluster);
<file_sep>import connect from "./connect";

export default function (uri) {
	let bucket = connect(uri);

	let designDoc = {
		"withSUVs": {
			"map": `
function (doc, meta) {
	if (doc.car.model === "SUV") {
		emit(null, doc);
	}
}`
		},
		"numConvertibles": {
			"map": `
function (doc, meta) {
	if (doc.car.model === "Convertible") {
		emit(null, doc);
	}
}`,
			"reduce": "_count"
		},
		"averageAge": {
			"map": `
function (doc, meta) {
	emit(doc.make, doc.age);
}`,
			"reduce": `
function (key, values, rereduce) {
	var sum = 0;

	for(i=0; i < values.length; i++) {
		sum = sum + values[i];
	}

	return (sum / values.length);
}`
		}
	};

	bucket.manager().upsertDesignDocument("benchmark", {"views": designDoc}, (err) => {
		if (err) return console.log(err);

		console.log("views setup");

		process.exit(0);
	});
}
<file_sep>import fs from "fs";
import _ from "lodash";
import JSONStream from "JSONStream";
import { ViewQuery } from "couchbase";

import connect from "./connect";

export function load(num, series, dir, uri, loadProgress) {
	let bucket = connect(uri);

	bucket.operationTimeout = 7500;

	let stream = fs.createReadStream(`${dir}/generated_docs_${num}_${series}.json`, {"encoding": "utf8"});
	let parser = JSONStream.parse("owners.*");

	stream.pipe(parser);

	let owners = [];

	parser.on("data", (owner) => {
		owners.push(owner);
	});

	parser.on("end", () => {

		let chunks = _.chunk(owners, 1000);

		loadProgress.start(owners.length);

		let insertStage = Promise.resolve([]);

		_.forEach(_.range(0, chunks.length), (chunkIndex) => {

			let ownersChunk = chunks[chunkIndex];

			insertStage = insertStage.then(() => {

				return new Promise((resolve, reject) => {

					let inserts = ownersChunk.map((owner) => {

						return new Promise((resolve, reject) => {

							bucket.insert(owner.id, owner, (err) => {
								if (err && err.code !== 12) {
									console.log(err);
									return reject(err);
								}

								resolve();
							});
						});

					});

					Promise.all(inserts).then(() => {
						loadProgress.completed(chunkIndex);

						resolve();
					}, reject).catch(reject);

				});
			}, (err) => {
				console.log(err);
				throw err;
			});
		});

		insertStage.then(() => {

			loadProgress.end();

			process.exit(0);

		});
	});
}

export function query(uri) {
	let bucket = connect(uri);

	bucket.operationTimeout = 7500;

	return new Promise((resolve, reject) => {

		console.log("query people with SUVs");

		let start = new Date();

		let vq = ViewQuery.from("benchmark", "withSUVs");

		vq.stale(ViewQuery.Update.BEFORE);

		bucket.query(vq, (err, results) => {
			if (err) return reject(err);

			let end = new Date();

			console.log(`people with SUVs: ${results.length}`);

			console.log(`people with SUVs in: ${(end.getTime() - start.getTime())}ms`);

			return resolve(results);
		});

	}).then(() => {

		return new Promise((resolve, reject) => {

			console.log("query number of convertibles");

			let start = new Date();

			let vq = ViewQuery.from("benchmark", "numConvertibles");

			vq.stale(ViewQuery.Update.BEFORE);

			bucket.query(vq, (err, results) => {
				if (err) return reject(err);

				let end = new Date();

				console.log(`number of convertibles: ${results[0].value}`);

				console.log(`number of convertibles in: ${(end.getTime() - start.getTime())}ms`);

				return resolve(results);
			});

		});

	}, (err) => {
		console.log(err);
		throw err;
	}).then(() => {

		return new Promise((resolve, reject) => {

			console.log("query average age");

			let start = new Date();

			let vq = ViewQuery.from("benchmark", "averageAge");

			vq.stale(ViewQuery.Update.BEFORE);

			bucket.query(vq, (err, results) => {
				if (err) return reject(err);

				let end = new Date();

				console.log(`average age: ${Math.round(results[0].value)}`);

				console.log(`average age: ${(end.getTime() - start.getTime())}ms`);

				resolve();
			});

		});

	}, (err) => {
		console.log(err);
		throw err;
	});
}

export default {
	"load": load,
	"query": query
}
<file_sep>import Chance from "chance";
import _ from "lodash";
import fs from "fs-extra";

export default function (num, series, dir, docs, progress=new Progress()) {
	let chance = new Chance();

	let owners = [];

	let tick = 1000;

	progress.start(docs);

	_.forEach(_.range(0, docs), () => {
		owners.push({
			"id": chance.guid(),
			"name": {
				"first": chance.first(),
				"last": chance.last()
			},
			"gender": chance.gender(),
			"ssn": chance.ssn(),
			"age": chance.age(),
			"phone": chance.phone(),
			"email": chance.email(),
			"address": {
				"street": chance.address({"short_suffix": true}),
				"city": chance.city(),
				"state": chance.state(),
				"zip": chance.zip({"plusfour": true})
			},
			"car": {
				"make": chance.pick(["Ford", "GM", "Honda", "Nissan", "Hyundai", "Toyota", "Mazda", "BMW", "Audi", "Volkswagen"]),
				"model": chance.pick(["Coupe", "Sedan", "SUV", "Truck", "Van", "Wagon", "Convertible"]),
				"year": chance.year({"min": 1990, "max": 2015})
			}
		});

		tick--;

		if (tick <= 0) {
			progress.completed();
			tick = 1000;
		}
	});

	owners = _.uniq(owners, "id");

	fs.ensureDir(dir, function (err) {
		if (err) return console.log(err);

		console.log(`saving to: ${dir}/generated_docs_${num}_${series}.json`);

		fs.writeFile(`${dir}/generated_docs_${num}_${series}.json`, JSON.stringify({"owners": owners}), (err) => {
			if (err) return console.log(err);

			progress.end();

			process.exit(0);
		});
	});
}
<file_sep>Couchbase Cloud Benchmarking
==========================
A module to benchmark various clouds by loading and accessing a large dataset in a Couchbase cluster<file_sep>export default class Progress {

	constructor() { }

	/* eslint-disable no-unused-vars */
	start(total) { }

	completed(i) { }

	end() { }
	/* eslint-enable no-unused-vars */
}
<file_sep>import _ from "lodash";
import yargs from "yargs";
import del from "del";
import path from "path";
import ProgressBar from "progress";
import { install } from "source-map-support";

import generate from "./generate";
import Progress from "./progress";
import setup from "./setup";
import benchmark from "./benchmark";

install();

class ProgressProgressBar {

	constructor(num, operation) {
		this.operation = operation;
		this.num = num;
		this.started = 0;
		this.total = 0;
		this.qued = 0;
	}

	start(total) {
		this.started++;
		this.total += total;

		if (this.started >= this.num) {
			console.log();

			this.bar = new ProgressBar(`${this.operation} [:bar] :current/:total :percent :elapseds elapsed`, {
				"complete": "=",
				"incomplete": " ",
				"width": 20,
				"total": this.total
			});

			_.forEach(_.range(0, this.qued), () => {
				this.bar.tick(1000);
			});
		}
	}

	completed() {
		if (this.bar) {
			this.bar.tick(1000);
		} else {
			this.qued++;
		}
	}
}

class WorkerProgress extends Progress {

	constructor(num) {
		super();

		this.num = num;
	}

	start(total) {
		process.send({
			"num": this.num,
			"type": "start",
			"total": total
		});
	}

	completed() {
		process.send({
			"num": this.num,
			"type": "completed"
		});
	}
}

function spinCluster(cluster, dir, num, series, progress, command) {

	return new Promise((resolve) => {

		let numWorkers = 0;

		_.forEach(_.range(1, num + 1), (i) => {
			let worker = cluster.fork();

			numWorkers++;
			worker.send({
				"num": i,
				"series": series,
				"dir": dir,
				"command": command
			});
		});

		if (progress) {
			Object.keys(cluster.workers).forEach((id) => {
				cluster.workers[id].on("message", (message) => {

					switch (message.type) {
						case "start":
							progress.start(message.total);
							break;
						case "completed":
							progress.completed();
							break;
					}

				});
			});
		}

		cluster.on("exit", () => {
			numWorkers--;

			if (numWorkers === 0) {
				console.log(`completed series ${series}`);

				resolve();
			}
		});
	});
}

export default function (cluster) {

	if (cluster.isMaster) {
		let argv = yargs.usage("cloud-benchmark <command> [options]")
			.command("generate", "generate test data")
			.command("setup", "setup view models")
			.command("run", "run tests")
			.option("n", {
				"alias": "num",
				"default": 150000,
				"describe": "number of docs to generate per process (generate only)",
				"type": "number"
			})
			.option("p", {
				"alias": "process",
				"default": 2,
				"describe": "number of processes to use",
				"type": "number"
			})
			.option("s", {
				"alias": "series",
				"default": 8,
				"describe": "number of times to run the import",
				"type": "number"
			})
			.option("d", {
				"alias": "dir",
				"default": path.resolve(__dirname, "../../data"),
				"describe": "directory to use",
				"type": "string"
			})
			.option("c", {
				"alias": "cluster",
				"default": "couchbase://127.0.0.1",
				"describe": "couchbase cluster uri (setup and test only)",
				"type": "string"
			})
			.demand(1, "must provide a valid command")
			.argv;

		let command = argv._[0];

		switch (command) {
			case "generate":

				del([`${argv.dir}/generated_docs_*_*.json`], (err) => {
					if (err) throw err;

					let generateSeries = Promise.resolve();

					_.forEach(_.range(1, argv.series + 1), (seriesIndex) => {

						generateSeries = generateSeries.then(() => {

							return spinCluster(cluster, argv.dir, argv.process, seriesIndex, new ProgressProgressBar(argv.process, `series ${seriesIndex}: generate test docs`), {
								"name": "generate",
								"options": {
									"docs": argv.num
								}
							});
						});
					});

					generateSeries.then(() => {
						console.log("completed");
					});
				});

				break;
			case "setup":
				setup(argv.cluster);
				break;
			case "run":

				let clusterSeries = Promise.resolve();

				_.forEach(_.range(1, argv.series + 1), (seriesIndex) => {

					clusterSeries = clusterSeries.then(() => {

						return spinCluster(cluster, argv.dir, argv.process, seriesIndex, new ProgressProgressBar(argv.process, `series ${seriesIndex}: load test docs`), {
							"name": "run",
							"options": {
								"cluster": argv.cluster
							}
						});
					});
				});


				clusterSeries.then(() => {
					return benchmark.query(argv.cluster);
				}).then(() => {
					return new Promise((resolve) => {
						console.log("waiting 60 seconds to run queries again");
						setTimeout(resolve, 60000);
					});
				}).then(() => {
					return benchmark.query(argv.cluster);
				}).then(() => process.exit(0), (err) => {
					console.log(err);
					throw err;
				}).catch((err) => {
					console.log(err);
					throw err;
				});
				break;
			default:
				yargs.showHelp();
		}

	} else {

		process.on("message", function (message) {
			let num = message.num;
			let command = message.command;

			switch (command.name) {

				case "generate":
					generate(num, message.series, message.dir, command.options.docs, new WorkerProgress(num));
					break;
				case "run":
					benchmark.load(num, message.series, message.dir, command.options.cluster, new WorkerProgress(num));
			}
		});

	}
}
