Skip to content
Merged
Show file tree
Hide file tree
Changes from 57 commits
Commits
Show all changes
84 commits
Select commit Hold shift + click to select a range
08d8583
chore: add simple benchmarks
watson Jun 16, 2019
1492dc6
ci(jenkins): enable benchmark stage and added some references in the …
v1v Jun 20, 2019
1505f2e
ci(jenkins): use a docker based agent
v1v Jun 20, 2019
7e53d8b
ci(jenkins): use a docker container for testing purposes
v1v Jun 20, 2019
d757ab7
Revert "ci(jenkins): use a docker container for testing purposes"
v1v Jun 20, 2019
ce2e58c
Revert "ci(jenkins): use a docker based agent"
v1v Jun 20, 2019
88752ee
ci(jenkins): nvm is already provided in the bare metal server: https:…
v1v Jun 20, 2019
0a04001
ci(jenkins): nvm is not provided by default, let's install it within …
v1v Jun 20, 2019
75210a5
ci(jenkins): this debug command is not required
v1v Jun 20, 2019
d315458
ci(jenkins): add verbose output to track what's going on in the barem…
v1v Jun 20, 2019
eb06cbf
chore: rename npm scripts for running benchmarks
watson Jul 3, 2019
abe1237
chore(benchmark): calculate deltas
watson Jul 4, 2019
9d3befa
chore: rename benchmark files to better names
watson Jul 8, 2019
1207997
fix: don't measure metrics during benchmark
watson Jul 8, 2019
51faf6b
chore: add a few other benchmarks
watson Jul 8, 2019
9ae7564
refactor: compare with and without the agent
watson Sep 4, 2019
5ec53ae
chore: remove unused packages
watson Sep 4, 2019
8074319
fix: ensure git rev+branch is recorded
watson Sep 4, 2019
7a6555c
fix: don't try to fetch central config
watson Sep 4, 2019
b6034a9
refactor: be more explicit about agent defaults
watson Sep 4, 2019
81d7c8b
chore: code cleanup
watson Sep 4, 2019
4b5295d
feat: add debug feature, 'DEBUG=1 npm run bench'
watson Sep 4, 2019
739b54c
chore: almost ready to upload
watson Sep 4, 2019
ff18477
ci(jenkins): as review commented
v1v Sep 5, 2019
fe0156b
ci(jenkins): use -n to run benchmarks
watson Sep 5, 2019
315bdec
ci(jenkins): use args
v1v Sep 5, 2019
d231d1f
Merge branch 'benchmark' of https://github.com/watson/apm-agent-nodej…
v1v Sep 5, 2019
461244c
ci(jenkins): simplify out file
v1v Sep 5, 2019
6beec6f
ci: write benchmark in ndjson format
watson Sep 5, 2019
48f0be2
ci(benchmark): fix linting
watson Sep 5, 2019
415afd6
ci(jenkins): requires relative path
v1v Sep 5, 2019
455110d
Merge branch 'benchmark' of https://github.com/watson/apm-agent-nodej…
v1v Sep 5, 2019
4abfa7c
Merge branch 'benchmark' of https://github.com/watson/apm-agent-nodej…
v1v Sep 5, 2019
5f50b44
Merge branch 'benchmark' of https://github.com/watson/apm-agent-nodej…
v1v Sep 5, 2019
bfe0017
ci(jenkins): preserve-env with sudo
v1v Sep 5, 2019
80b195a
ci(jenkins): remove with sudo
v1v Sep 5, 2019
2b8460f
Revert "ci(jenkins): remove with sudo"
v1v Sep 5, 2019
51dde2e
chore: for debugging purposes
v1v Sep 5, 2019
30356f1
ci(jenkins): sudo env context
v1v Sep 6, 2019
bc73826
chore: for debugging purposes
v1v Sep 6, 2019
629a728
ci(jenkins): ensure concurrent builds
v1v Sep 6, 2019
decdc93
chore: if sudo run
v1v Sep 6, 2019
e0c1bb1
ci(jenkins): delete folders
v1v Sep 6, 2019
f3312c8
chore: delete later on
v1v Sep 6, 2019
05f28e5
ci(benchmark): clean up require order
watson Sep 6, 2019
41e655e
ci(benchmark): improve benchmark accuracy
watson Sep 6, 2019
c42bc9d
ci(benchmark): detailed logging of results
watson Sep 6, 2019
419e388
ci(benchmark): fix linting
watson Sep 6, 2019
13ab7a8
ci(benchmark): add missing devDependencies
watson Sep 6, 2019
0ed7bfd
chore: benchmark context for ci
v1v Sep 6, 2019
5afcaf1
ci(jenkins): delete folder
v1v Sep 6, 2019
a620bd3
ci(jenkins): node_version variable
v1v Sep 6, 2019
9b75bbd
chore: as suggested in the review
v1v Sep 6, 2019
9957434
chore: as suggested in the review
v1v Sep 6, 2019
a756c5b
Merge branch 'benchmark' of https://github.com/watson/apm-agent-nodej…
v1v Sep 6, 2019
7f1e265
ci(jenkins): enable node version in jenkins
v1v Sep 6, 2019
910a1b8
chore: remove debug
v1v Sep 6, 2019
56a9ab1
ci(benchmark): seed bulk upload file
watson Sep 6, 2019
8c59958
ci(jenkins): deletedir always
v1v Sep 6, 2019
cb53f4b
Merge branch 'benchmark' of https://github.com/watson/apm-agent-nodej…
v1v Sep 6, 2019
a8389aa
ci(benchmark): fix bulk API metadata line
watson Sep 6, 2019
81732b6
ci(jenkins): sudo is temperamental
v1v Sep 6, 2019
0acdf79
Merge branch 'benchmark' of https://github.com/watson/apm-agent-nodej…
v1v Sep 6, 2019
3ec6ee3
ci(jenkins): change permissions for sudo
v1v Sep 6, 2019
f09af4a
chore: revert debug
v1v Sep 6, 2019
407228f
ci(benchmark): add @timestamp
watson Sep 9, 2019
c80ac89
ci(jenkins): expose CI env variables
v1v Sep 9, 2019
d6258f4
ci(benchmark): improve collected ci/git/pr info
watson Sep 9, 2019
1b28436
ci(jenkins): reorder the sudo command
v1v Sep 9, 2019
1e5a6b8
ci(jenkins): env variables firstly
v1v Sep 9, 2019
6a4303d
ci(jenkins): export and source
v1v Sep 10, 2019
a705eb7
ci(jenkins): quote to avoid space issues
v1v Sep 10, 2019
e48c477
ci(benchmark): store pr.id as number
watson Sep 10, 2019
2f34190
ci(benchmark): remove unused require call
watson Sep 10, 2019
256afb6
Merge remote-tracking branch 'upstream/master' into benchmark
v1v Sep 10, 2019
37e6e65
ci(jenkins): disable benchmark from daily run
v1v Sep 10, 2019
c3ab6d9
ci(benchmark): ensure CHANGE_TITLE is properly escaped
watson Sep 10, 2019
6415e4e
ci(benchmark): try to fix escaping
watson Sep 10, 2019
cebf474
ci(benchmark): clean up old output file
watson Sep 10, 2019
9488715
ci(jenkins): when sudo chmod 777
v1v Sep 10, 2019
a8b9cfb
ci(jenkins): trap int term
v1v Sep 10, 2019
606a35b
ci(jenkins): trap int term
v1v Sep 10, 2019
7467ac2
ci(jenkins): term exit traps
v1v Sep 10, 2019
a5b76be
ci(jenkins): for fixing the workspace
v1v Sep 10, 2019
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
21 changes: 21 additions & 0 deletions .ci/scripts/prepare-benchmarks-env.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/usr/bin/env bash
set -eo pipefail

# This particular configuration is required to be installed in the baremetal
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.34.0/install.sh | bash
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
command -v nvm

## If NODE_VERSION env variable exists then use it otherwise use node as default
if [ -z "${NODE_VERSION}" ] ; then
NODE_VERSION="node"
fi
nvm install ${NODE_VERSION}

set +x
npm config list
npm install

node --version
npm --version
9 changes: 9 additions & 0 deletions .ci/scripts/run-benchmarks.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/usr/bin/env bash
set -ueo pipefail

SCRIPTPATH=$(dirname "$0")
source ./${SCRIPTPATH}/prepare-benchmarks-env.sh

RESULT_FILE=${1:-apm-agent-benchmark-results.json}

npm run bench:ci ${RESULT_FILE}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ test/types/transpile/index.js
build
coverage
node_modules
test/benchmarks/.tmp
46 changes: 46 additions & 0 deletions Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ pipeline {
}
parameters {
booleanParam(name: 'Run_As_Master_Branch', defaultValue: false, description: 'Allow to run any steps on a PR, some steps normally only run on master branch.')
booleanParam(name: 'bench_ci', defaultValue: true, description: 'Enable benchmarks.')
booleanParam(name: 'tav_ci', defaultValue: true, description: 'Enable TAV tests.')
booleanParam(name: 'tests_ci', defaultValue: true, description: 'Enable tests.')
booleanParam(name: 'test_edge_ci', defaultValue: true, description: 'Enable tests for edge versions of nodejs.')
Expand Down Expand Up @@ -271,6 +272,51 @@ pipeline {
githubNotify(context: "${env.GITHUB_CHECK_ITS_NAME}", description: "${env.GITHUB_CHECK_ITS_NAME} ...", status: 'PENDING', targetUrl: "${env.JENKINS_URL}search/?q=${env.ITS_PIPELINE.replaceAll('/','+')}")
}
}
/**
Run the benchmarks and store the results on ES.
The result JSON files are also archive into Jenkins.
*/
stage('Benchmarks') {
agent { label 'metal' }
options { skipDefaultCheckout() }
environment {
HOME = "${env.WORKSPACE}"
RESULT_FILE = 'apm-agent-benchmark-results.json'
NODE_VERSION = '12'
}
when {
beforeAgent true
allOf {
anyOf {
branch 'master'
tag pattern: 'v\\d+\\.\\d+\\.\\d+.*', comparator: 'REGEXP'
expression { return params.Run_As_Master_Branch }
}
expression { return params.bench_ci }
}
}
steps {
withGithubNotify(context: 'Benchmarks', tab: 'artifacts') {
dir(env.BUILD_NUMBER) {
deleteDir()
unstash 'source'
dir(BASE_DIR){
sh '[ -e "${HOME}/.nvm" ] && rm -rf ${HOME}/.nvm || true' // to remove a broken env
sh '.ci/scripts/run-benchmarks.sh "${RESULT_FILE}"'
}
}
}
}
post {
always {
sendBenchmarks(file: "${BUILD_NUMBER}/${BASE_DIR}/${RESULT_FILE}",
index: "benchmark-nodejs", archive: true)
dir(env.BUILD_NUMBER) {
deleteDir()
}
}
}
}
}
post {
cleanup {
Expand Down
6 changes: 6 additions & 0 deletions TESTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,3 +124,9 @@ Clean up Docker containers and volumes:
```
npm run docker:clean
```

Run the benchmarks:

```
npm run bench
```
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
"test:types": "tsc --project test/types/tsconfig.json && tsc --project test/types/transpile/tsconfig.json && node test/types/transpile/index.js",
"test:babel": "babel test/babel/src.js --out-file test/babel/out.js && node test/babel/out.js",
"test:esm": "node --experimental-modules test/esm",
"bench": "./test/benchmarks/scripts/run-benchmarks.sh",
"bench:ci": "./test/benchmarks/scripts/run-benchmarks-ci.sh",
"local:start": "./test/script/local-deps-start.sh",
"local:stop": "./test/script/local-deps-stop.sh",
"docker:start": "docker-compose -f ./test/docker-compose.yml up -d",
Expand Down Expand Up @@ -113,8 +115,10 @@
"@types/node": "^12.0.8",
"apollo-server-express": "^2.6.3",
"aws-sdk": "^2.477.0",
"benchmark": "^2.1.4",
"bluebird": "^3.5.2",
"cassandra-driver": "^4.0.0",
"columnify": "^1.5.4",
"commitlint-config-squash-pr": "^1.0.0",
"connect": "^3.7.0",
"container-info": "^1.0.1",
Expand Down Expand Up @@ -148,6 +152,7 @@
"mysql": "^2.16.0",
"mysql2": "^1.6.3",
"ndjson": "^1.5.0",
"numeral": "^2.0.6",
"nyc": "^14.1.1",
"once": "^1.4.0",
"p-finally": "^1.0.0",
Expand Down
29 changes: 29 additions & 0 deletions test/benchmarks/001-transaction-and-span-no-stack-trace.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
'use strict'

/* eslint-disable no-unused-vars, no-undef */

const bench = require('./utils/bench')

bench('transaction-and-span-no-stack-trace', {
agentConf: {
captureSpanStackTraces: false
},
setup () {
var agent = this.benchmark.agent

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this gets into the fn(...) scope below by benchmark.js generating code from the contents of the input functions and eval'ing that? 🤔

Seems a bit confusing and looks like dead code at first glance. Also, I feel like the linter would likely complain about this sort of thing.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct. Which is also why I have to use var instead of const as those are not hoisted in the generated code where it happens to wrap the setup in a try-catch. To get the linter to behave, I've added a eslint-disable comment at the top of the file.

Check out this example to see how the code compilation works: https://benchmarkjs.com/docs#prototype_setup

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gotcha. 👍

},
fn (deferred) {
if (agent) agent.startTransaction()
setImmediate(() => {
const span = agent && agent.startSpan()
setImmediate(() => {
if (agent) {
span.end()
agent.endTransaction()
}
setImmediate(() => {
deferred.resolve()
})
})
})
}
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
'use strict'

/* eslint-disable no-unused-vars, no-undef */

const bench = require('./utils/bench')

bench('transaction-and-span-overhead-realistic-size', {
agentConf: {
captureSpanStackTraces: true
},
setup () {
var agent = this.benchmark.agent
var callstack = this.benchmark.callstack

// To avoid randomness, but still generate what appears to be natural random
// call stacks, number of spans etc, use a pre-defined set of numbers
var numbers = [2, 5, 10, 1, 2, 21, 2, 5, 6, 9, 1, 11, 9, 8, 12]
var numbersSpanIndex = 5
var numbersStackLevelIndex = 0

function addSpan (amount, cb) {
setImmediate(() => {
const span = agent && agent.startSpan()
setImmediate(() => {
if (agent) span.end()
if (--amount === 0) cb()
else addSpan(amount, cb)
})
})
}
},
fn (deferred) {
if (agent) agent.startTransaction()
const amount = numbers[numbersStackLevelIndex++ % numbers.length]
callstack(amount, () => {
const amount = numbers[numbersSpanIndex++ % numbers.length]
addSpan(amount, () => {
if (agent) agent.endTransaction()
deferred.resolve()
})
})
}
})
27 changes: 27 additions & 0 deletions test/benchmarks/003-transaction-and-span-with-stack-trace.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
'use strict'

/* eslint-disable no-unused-vars, no-undef */

const bench = require('./utils/bench')

bench('transaction-and-span-with-stack-trace', {
agentConf: {
captureSpanStackTraces: false
},
setup () {
var agent = this.benchmark.agent
},
fn (deferred) {
if (agent) agent.startTransaction()
setImmediate(() => {
const span = agent && agent.startSpan()
setImmediate(() => {
if (agent) {
span.end()
agent.endTransaction()
}
deferred.resolve()
})
})
}
})
20 changes: 20 additions & 0 deletions test/benchmarks/004-transaction.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
'use strict'

/* eslint-disable no-unused-vars, no-undef */

const bench = require('./utils/bench')

bench('transaction', {
setup () {
var agent = this.benchmark.agent
},
fn (deferred) {
if (agent) agent.startTransaction()
setImmediate(() => {
if (agent) agent.endTransaction()
setImmediate(() => {
deferred.resolve()
})
})
}
})
21 changes: 21 additions & 0 deletions test/benchmarks/005-transaction-reading-file.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
'use strict'

/* eslint-disable no-unused-vars, no-undef */

const bench = require('./utils/bench')

bench('transaction-reading-file', {
setup () {
var agent = this.benchmark.agent
var fs = this.benchmark.fs
var filename = this.benchmark.testFile
},
fn (deferred) {
if (agent) agent.startTransaction()
fs.readFile(filename, err => {
if (err) throw err
if (agent) agent.endTransaction()
deferred.resolve()
})
}
})
86 changes: 86 additions & 0 deletions test/benchmarks/scripts/run-benchmarks-ci.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
#!/usr/bin/env bash

set -exo pipefail

SCRIPTPATH=$(dirname "$0")
RESULT_FILE=${1}

if [ -z "$1" ]
then
echo "Usage:"
echo " run-benchmarks-ci.sh <output-file>"
echo
echo "Examples:"
echo " run-benchmarks-ci.sh out.ndjson - Run benchmark + store result in out.ndjson"
echo
exit
fi

echo $(pwd)

function setUp() {
echo "Setting CPU frequency to base frequency"

CPU_MODEL=$(lscpu | grep "Model name" | awk '{for(i=3;i<=NF;i++){printf "%s ", $i}; printf "\n"}')
if [ "${CPU_MODEL}" == "Intel(R) Xeon(R) CPU E3-1246 v3 @ 3.50GHz " ]
then
# could also use `nproc`
CORE_INDEX=7
BASE_FREQ="3.5GHz"
elif [ "${CPU_MODEL}" == "Intel(R) Core(TM) i7-6700 CPU @ 3.40GHz " ]
then
CORE_INDEX=7
BASE_FREQ="3.4GHz"
elif [ "${CPU_MODEL}" == "Intel(R) Core(TM) i7-7700 CPU @ 3.60GHz " ]
then
CORE_INDEX=7
BASE_FREQ="3.6GHz"
elif [ "${CPU_MODEL}" == "Intel(R) Core(TM) i9-8950HK CPU @ 2.90GHz " ]
then
CORE_INDEX=9
BASE_FREQ="2.90GHz"
else
>&2 echo "Cannot determine base frequency for CPU model [${CPU_MODEL}]. Please adjust the build script."
exit 1
fi
MIN_FREQ=$(cpufreq-info -l -c 0 | awk '{print $1}')
# This is the frequency including Turbo Boost. See also http://ark.intel.com/products/80916/Intel-Xeon-Processor-E3-1246-v3-8M-Cache-3_50-GHz
MAX_FREQ=$(cpufreq-info -l -c 0 | awk '{print $2}')

# set all CPUs to the base frequency
for (( cpu=0; cpu<=${CORE_INDEX}; cpu++ ))
do
sudo -n cpufreq-set -c ${cpu} --min ${BASE_FREQ} --max ${BASE_FREQ}
done

# Build cgroups to isolate microbenchmarks and JVM threads
echo "Creating groups for OS and microbenchmarks"
# Isolate the OS to the first core
sudo -n cset set --set=/os --cpu=0-1
sudo -n cset proc --move --fromset=/ --toset=/os

# Isolate the microbenchmarks to all cores except the first two (first physical core)
# On a 4 core CPU with hyper threading, this would be 6 cores (3 physical cores)
sudo -n cset set --set=/benchmark --cpu=2-${CORE_INDEX}
}

function benchmark() {
sudo -n cset proc --exec /benchmark -- ./${SCRIPTPATH}/run-benchmarks.sh all ${RESULT_FILE}
}

function tearDown() {
echo "Destroying cgroups"
sudo -n cset set --destroy /os
sudo -n cset set --destroy /benchmark

echo "Setting normal frequency range"
for (( cpu=0; cpu<=${CORE_INDEX}; cpu++ ))
do
sudo -n cpufreq-set -c ${cpu} --min ${MIN_FREQ} --max ${MAX_FREQ}
done
}

trap "tearDown" EXIT

setUp
benchmark
Loading