Skip to content

Commit

Permalink
Merge branch 'newrelic:main' into unit-tests
Browse files Browse the repository at this point in the history
  • Loading branch information
amychisholm03 authored Sep 17, 2024
2 parents a8a90e8 + a5a1526 commit 1d72273
Show file tree
Hide file tree
Showing 24 changed files with 1,006 additions and 1,163 deletions.
7 changes: 7 additions & 0 deletions .github/workflows/azure-site-extension.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,20 @@ name: Azure Site Extension

on:
workflow_dispatch:
workflow_run:
workflows: ["Create Release"]
types:
- completed

env:
SPEC_FILE_TEMPLATE: 'NewRelic.Azure.WebSites.Extension.NodeAgent.nuspec'

jobs:
create_extension_bundle:
runs-on: windows-latest
if:
(github.event.workflow_run && github.event.workflow_run.conclusion == 'success') ||
(github.event_name == 'workflow_dispatch')

strategy:
matrix:
Expand Down
10 changes: 5 additions & 5 deletions compatibility.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ version.
| `@grpc/grpc-js` | 1.4.0 | 1.11.2 | 8.17.0 |
| `@hapi/hapi` | 20.1.2 | 21.3.10 | 9.0.0 |
| `@koa/router` | 11.0.2 | 13.0.1 | 3.2.0 |
| `@langchain/core` | 0.1.17 | 0.2.32 | 11.13.0 |
| `@langchain/core` | 0.1.17 | 0.3.1 | 11.13.0 |
| `@nestjs/cli` | 9.0.0 | 10.4.5 | 10.1.0 |
| `@prisma/client` | 5.0.0 | 5.19.1 | 11.0.0 |
| `@smithy/smithy-client` | 2.0.0 | 3.3.1 | 11.0.0 |
| `@smithy/smithy-client` | 2.0.0 | 3.3.2 | 11.0.0 |
| `amqplib` | 0.5.0 | 0.10.4 | 2.0.0 |
| `apollo-server` | 3.0.0 | 3.13.0 | `@newrelic/apollo-server-plugin@1.0.0` |
| `apollo-server-express` | 3.0.0 | 3.13.0 | `@newrelic/apollo-server-plugin@1.0.0` |
Expand All @@ -36,7 +36,7 @@ version.
| `cassandra-driver` | 3.4.0 | 4.7.2 | 1.7.1 |
| `connect` | 3.0.0 | 3.7.0 | 2.6.0 |
| `express` | 4.6.0 | 4.21.0 | 2.6.0 |
| `fastify` | 2.0.0 | 4.28.1 | 8.5.0 |
| `fastify` | 2.0.0 | 5.0.0 | 8.5.0 |
| `generic-pool` | 3.0.0 | 3.9.0 | 0.9.0 |
| `ioredis` | 4.0.0 | 5.4.1 | 1.26.2 |
| `kafkajs` | 2.0.0 | 2.2.4 | 11.19.0 |
Expand All @@ -46,9 +46,9 @@ version.
| `memcached` | 2.2.0 | 2.2.2 | 1.26.2 |
| `mongodb` | 4.1.4 | 6.9.0 | 1.32.0 |
| `mysql` | 2.2.0 | 2.18.1 | 1.32.0 |
| `mysql2` | 2.0.0 | 3.11.2 | 1.32.0 |
| `mysql2` | 2.0.0 | 3.11.3 | 1.32.0 |
| `next` | 13.4.19 | 14.2.11 | 12.0.0 |
| `openai` | 4.0.0 | 4.61.0 | 11.13.0 |
| `openai` | 4.0.0 | 4.61.1 | 11.13.0 |
| `pg` | 8.2.0 | 8.12.0 | 9.0.0 |
| `pg-native` | 2.0.0 | 3.1.0 | 9.0.0 |
| `pino` | 7.0.0 | 9.4.0 | 8.11.0 |
Expand Down
260 changes: 124 additions & 136 deletions test/unit/adaptive-sampler.test.js
Original file line number Diff line number Diff line change
@@ -1,163 +1,151 @@
/*
* Copyright 2020 New Relic Corporation. All rights reserved.
* Copyright 2024 New Relic Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/

'use strict'

const tap = require('tap')
const test = require('node:test')
const assert = require('node:assert')
const sinon = require('sinon')

const helper = require('../lib/agent_helper')
const AdaptiveSampler = require('../../lib/adaptive-sampler')
const sinon = require('sinon')

tap.test('AdaptiveSampler', (t) => {
let sampler = null
const shared = {
'should count the number of traces sampled': (t) => {
t.equal(sampler.sampled, 0)
t.ok(sampler.shouldSample(0.1234))
t.equal(sampler.sampled, 1)
t.end()
},

'should not sample transactions with priorities lower than the min': (t) => {
t.equal(sampler.sampled, 0)
sampler._samplingThreshold = 0.5
t.notOk(sampler.shouldSample(0))
t.equal(sampler.sampled, 0)
t.ok(sampler.shouldSample(1))
t.equal(sampler.sampled, 1)
t.end()
},

'should adjust the min priority when throughput increases': (t) => {
sampler._reset(sampler.samplingTarget)
sampler._seen = 2 * sampler.samplingTarget
sampler._adjustStats(sampler.samplingTarget)
t.equal(sampler.samplingThreshold, 0.5)
t.end()
},

'should only take the first 10 on the first harvest': (t) => {
t.equal(sampler.samplingThreshold, 0)

// Change this to maxSampled if we change the way the back off works.
for (let i = 0; i <= 2 * sampler.samplingTarget; ++i) {
sampler.shouldSample(0.99999999)
}

t.equal(sampler.sampled, 10)
t.equal(sampler.samplingThreshold, 1)
t.end()
},

'should backoff on sampling after reaching the sampled target': (t) => {
sampler._seen = 10 * sampler.samplingTarget

// Flag the sampler as not in the first period
sampler._reset()

// The minimum sampled priority is not adjusted until the `target` number of
// transactions have been sampled, this is why the first 10 checks are all
// 0.9. At that point the current count of seen transactions should be close
// to the previous period's transaction count.
//
// In this test, however, the seen for this period is small compared the
// previous period (10 vs 100). This causes the MSP to drop to 0.3 but
// quickly normalizes again. This is an artifact of the test's use of infinite
// priority transactions in order to make the test predictable.
const epsilon = 0.000001
const expectedMSP = [
0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.316227766016838, 0.5500881229337736,
0.6957797474657306, 0.7910970452225743, 0.8559144986383691, 0.9013792551037068,
0.9340820391176599, 0.9580942670418969, 0.976025777575764, 0.9896031249412947, 1.0
]

// Change this to maxSampled if we change the way the back off works.
for (let i = 0; i <= 2 * sampler.samplingTarget; ++i) {
const expected = expectedMSP[i]
t.ok(
sampler.samplingThreshold >= expected - epsilon &&
sampler.samplingThreshold <= expected + epsilon
)

sampler.shouldSample(Infinity)
}
t.end()
const shared = {
'should count the number of traces sampled': (t) => {
const { sampler } = t.nr
assert.equal(sampler.sampled, 0)
assert.ok(sampler.shouldSample(0.1234))
assert.equal(sampler.sampled, 1)
},

'should not sample transactions with priorities lower than the min': (t) => {
const { sampler } = t.nr
assert.equal(sampler.sampled, 0)
sampler._samplingThreshold = 0.5
assert.equal(sampler.shouldSample(0), false)
assert.equal(sampler.sampled, 0)
assert.ok(sampler.shouldSample(1))
assert.equal(sampler.sampled, 1)
},

'should adjust the min priority when throughput increases': (t) => {
const { sampler } = t.nr
sampler._reset(sampler.samplingTarget)
sampler._seen = 2 * sampler.samplingTarget
sampler._adjustStats(sampler.samplingTarget)
assert.equal(sampler.samplingThreshold, 0.5)
},

'should only take the first 10 on the first harvest': (t) => {
const { sampler } = t.nr
assert.equal(sampler.samplingThreshold, 0)

// Change this to maxSampled if we change the way the back off works.
for (let i = 0; i <= 2 * sampler.samplingTarget; ++i) {
sampler.shouldSample(0.99999999)
}
}

t.test('in serverless mode', (t) => {
let agent = null
t.beforeEach(() => {
agent = helper.loadMockedAgent({
serverless_mode: {
enabled: true
}
})
sampler = agent.transactionSampler
})

t.afterEach(() => {
helper.unloadAgent(agent)
sampler = null
})
assert.equal(sampler.sampled, 10)
assert.equal(sampler.samplingThreshold, 1)
},

'should backoff on sampling after reaching the sampled target': (t) => {
const { sampler } = t.nr
sampler._seen = 10 * sampler.samplingTarget

// Flag the sampler as not in the first period
sampler._reset()

// The minimum sampled priority is not adjusted until the `target` number of
// transactions have been sampled, this is why the first 10 checks are all
// 0.9. At that point the current count of seen transactions should be close
// to the previous period's transaction count.
//
// In this test, however, the seen for this period is small compared the
// previous period (10 vs 100). This causes the MSP to drop to 0.3 but
// quickly normalizes again. This is an artifact of the test's use of infinite
// priority transactions in order to make the test predictable.
const epsilon = 0.000001
const expectedMSP = [
0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.316227766016838, 0.5500881229337736,
0.6957797474657306, 0.7910970452225743, 0.8559144986383691, 0.9013792551037068,
0.9340820391176599, 0.9580942670418969, 0.976025777575764, 0.9896031249412947, 1.0
]

// Change this to maxSampled if we change the way the back off works.
for (let i = 0; i <= 2 * sampler.samplingTarget; ++i) {
const expected = expectedMSP[i]
assert.ok(
sampler.samplingThreshold >= expected - epsilon &&
sampler.samplingThreshold <= expected + epsilon
)

sampler.shouldSample(Infinity)
}
}
}

Object.getOwnPropertyNames(shared).forEach((testName) => {
t.test(testName, shared[testName])
test('in serverless mode', async (t) => {
t.beforeEach((ctx) => {
ctx.nr = {}
ctx.nr.agent = helper.loadMockedAgent({
serverless_mode: { enabled: true }
})

t.test(
'should reset itself after a transaction outside the window has been created',
async (t) => {
const spy = sinon.spy(sampler, '_reset')
sampler.samplingPeriod = 50
t.equal(spy.callCount, 0)
agent.emit('transactionStarted', { timer: { start: Date.now() } })
t.equal(spy.callCount, 1)

return new Promise((resolve) => {
setTimeout(() => {
t.equal(spy.callCount, 1)
agent.emit('transactionStarted', { timer: { start: Date.now() } })
t.equal(spy.callCount, 2)
resolve()
}, 100)
})
}
)
t.end()
ctx.nr.sampler = ctx.nr.agent.transactionSampler
})

t.test('in standard mode', (t) => {
t.beforeEach(() => {
sampler = new AdaptiveSampler({
period: 100,
target: 10
})
})

t.afterEach(() => {
sampler.samplePeriod = 0 // Clear sample interval.
})
t.afterEach((ctx) => {
helper.unloadAgent(ctx.nr.agent)
})

Object.getOwnPropertyNames(shared).forEach((testName) => {
t.test(testName, shared[testName])
})
for (const [name, fn] of Object.entries(shared)) {
await t.test(name, fn)
}

t.test('should reset itself according to the period', async (t) => {
await t.test(
'should reset itself after a transaction outside the window has been created',
async (t) => {
const { agent, sampler } = t.nr
const spy = sinon.spy(sampler, '_reset')
sampler.samplingPeriod = 50
assert.equal(spy.callCount, 0)
agent.emit('transactionStarted', { timer: { start: Date.now() } })
assert.equal(spy.callCount, 1)

return new Promise((resolve) => {
await new Promise((resolve) => {
setTimeout(() => {
t.equal(spy.callCount, 4)
assert.equal(spy.callCount, 1)
agent.emit('transactionStarted', { timer: { start: Date.now() } })
assert.equal(spy.callCount, 2)
resolve()
}, 235)
}, 100)
})
}
)
})

test('in standard mode', async (t) => {
t.beforeEach((ctx) => {
ctx.nr = {}
ctx.nr.sampler = new AdaptiveSampler({ period: 100, target: 10 })
})

for (const [name, fn] of Object.entries(shared)) {
await t.test(name, fn)
}

await t.test('should reset itself according to the period', async (t) => {
const { sampler } = t.nr
const spy = sinon.spy(sampler, '_reset')
sampler.samplingPeriod = 50

await new Promise((resolve) => {
setTimeout(() => {
assert.equal(spy.callCount, 4)
resolve()
}, 235)
})
t.end()
})
t.end()
})
Loading

0 comments on commit 1d72273

Please sign in to comment.