Skip to content

Commit 697cf6f

Browse files
chore: DEM Spammer (#1522)
1 parent 23aec69 commit 697cf6f

File tree

10 files changed

+5178
-0
lines changed

10 files changed

+5178
-0
lines changed
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# This composite action is used to load test DEM Consumers and NR1 UIs
2+
3+
name: 'Consumer Load Test'
4+
5+
inputs:
6+
license-key:
7+
description: 'License key for the test'
8+
required: true
9+
app-id:
10+
description: 'App ID for the test'
11+
required: true
12+
entity-guid:
13+
description: 'Entity GUID for the test'
14+
required: true
15+
page-view:
16+
description: 'Capture PageView events'
17+
required: false
18+
type: boolean
19+
session-replay:
20+
description: 'Capture SessionReplay events'
21+
required: false
22+
type: boolean
23+
minutes:
24+
description: 'Minutes to Run'
25+
required: false
26+
27+
runs:
28+
using: "composite"
29+
steps:
30+
- name: Install dependencies
31+
run: npm install --silent --no-progress --prefix $GITHUB_ACTION_PATH/..
32+
shell: bash
33+
- name: Run action script
34+
id: action-script
35+
run: |
36+
node $GITHUB_ACTION_PATH/index.cjs \
37+
--license-key ${{ inputs.license-key }} \
38+
--app-id ${{ inputs.app-id }} \
39+
--entity-guid ${{ inputs.entity-guid }} \
40+
--page-view ${{ inputs.page-view }} \
41+
--session-replay ${{ inputs.session-replay }}
42+
--minutes ${{ inputs.minutes }}
43+
shell: bash
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
const process = require('process')
2+
const yargs = require('yargs')
3+
const {hideBin} = require('yargs/helpers')
4+
5+
const args = yargs(hideBin(process.argv))
6+
.usage('$0 [options]')
7+
8+
.string('license-key')
9+
.describe('license-key', 'License key for the load test')
10+
11+
.string('app-id')
12+
.describe('app-id', 'App ID for the load test')
13+
14+
.string('entity-guid')
15+
.describe('entity-guid', 'Entity GUID for the load test')
16+
17+
.boolean('page-view')
18+
.describe('page-view', 'harvest a page view event for every iteration')
19+
.default('page-view', false)
20+
21+
.boolean('session-replay')
22+
.describe('session-replay', 'harvest (2) session events for every iteration')
23+
.default('session-replay', false)
24+
25+
.number('minutes')
26+
.describe('minutes', 'Number of minutes to run the load test')
27+
.default('minutes', 60)
28+
29+
.demandOption(['license-key', 'app-id', 'entity-guid'], 'Please provide the required options: license-key, app-id, and entity-guid')
30+
.argv
31+
32+
module.exports = {args}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
const { args } = require('./args.cjs');
2+
const harvestSessionReplay = require('./session-replay/session-replay-payload.cjs');
3+
const harvestPageView = require('./page-view/page-view-payload.cjs');
4+
5+
let payloadsSent = 0
6+
let concurrent = 0 // increments to 5
7+
8+
const start = performance.now()
9+
10+
async function sendRequest(instanceMarker, payloadMetadata = {payloadsSentInJob: 0}) {
11+
return new Promise(async (resolve, reject) => {
12+
const appId = args.appId
13+
const agentVersion = '0.0.1'
14+
const licenseKey = args.licenseKey
15+
const entityGuid = args.entityGuid
16+
const session = Array.from({ length: 16 }, () => Math.floor(Math.random() * 16).toString(16)).join('');
17+
18+
const ptid = Array.from({ length: 16 }, () => Math.floor(Math.random() * 16).toString(16)).join('');
19+
const timeNow = Math.floor(Date.now());
20+
const enduserId = 'faker-' + timeNow + '@newrelic.com';
21+
const offset = Math.floor(Math.random() * 10000) + 40000; // random offset between 40s and 50s
22+
23+
payloadMetadata.harvestCount = 0
24+
25+
try {
26+
/** PAGEVIEW HARVEST */
27+
if (args.pageView) await harvestPageView({
28+
licenseKey,
29+
appId,
30+
agentVersion,
31+
session,
32+
ptid,
33+
timeNow,
34+
offset,
35+
enduserId,
36+
payloadMetadata,
37+
})
38+
39+
/** SNAPSHOT HARVEST */
40+
if (args.sessionReplay) await harvestSessionReplay({
41+
licenseKey,
42+
appId,
43+
timeNow,
44+
offset,
45+
isSnapshot: true,
46+
entityGuid,
47+
session,
48+
agentVersion,
49+
ptid,
50+
enduserId,
51+
payloadMetadata
52+
})
53+
54+
/** MUTATION HARVEST */
55+
if (args.sessionReplay) await harvestSessionReplay({
56+
licenseKey,
57+
appId,
58+
timeNow,
59+
offset,
60+
isSnapshot: false,
61+
entityGuid,
62+
session,
63+
agentVersion,
64+
ptid,
65+
enduserId,
66+
payloadMetadata
67+
})
68+
69+
if (!payloadMetadata.payloadsSentInJob) throw 'No payloads sent in this job, something went wrong! Check the feature settings'
70+
71+
console.log("payloads sent: ", ++payloadsSent, ' ||| payloads per second: ', (payloadsSent / ((performance.now() - start) / 1000)).toFixed(3));
72+
} catch (err) {
73+
console.error(`Error in instance ${instanceMarker}:`, err);
74+
}
75+
76+
// stage with a timeout to avoid blocking the event loop and creating a memory leak
77+
setTimeout(async () => {
78+
if (!payloadMetadata.payloadsSentInJob || performance.now() - start > (args.minutes * 60 * 1000)) {
79+
console.log(`Instance ${instanceMarker} completed after ${((performance.now() - start) / 1000).toFixed(3)} seconds`);
80+
return resolve();
81+
}
82+
await sendRequest(instanceMarker, payloadMetadata)
83+
resolve()
84+
}, 1)
85+
86+
})
87+
}
88+
89+
const fetches = []
90+
while (concurrent++ < 5) fetches.push(
91+
sendRequest(concurrent).catch(err => {
92+
console.error(err);
93+
})
94+
)
95+
96+
console.log(`Spawned ${concurrent} concurrent threads. Running for ${args.minutes} minutes.`);
97+
Promise.all(fetches).then(() => {
98+
const end = performance.now()
99+
const seconds = ((end - start) / 1000).toFixed(3);
100+
console.log(`All requests completed in ${seconds} seconds.`);
101+
console.log(payloadsSent / seconds + ' payloads per second');
102+
}).catch(err => {
103+
console.error('Error in request execution:', err);
104+
})
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
const headers = {
2+
"content-type": "text/plain",
3+
"sec-ch-ua": "\"Google Chrome\";v=\"137\", \"Chromium\";v=\"137\", \"Not/A)Brand\";v=\"24\"",
4+
"sec-ch-ua-mobile": "?0",
5+
"sec-ch-ua-platform": "\"macOS\""
6+
};
7+
8+
module.exports = async function ({
9+
licenseKey,
10+
appId,
11+
agentVersion,
12+
session,
13+
ptid,
14+
timeNow,
15+
offset,
16+
enduserId,
17+
payloadMetadata = { payloadsSentInJob: 0 }
18+
}) {
19+
const request = new URL(`https://staging-bam.nr-data.net/1/${licenseKey}`); // parameterize this later
20+
request.searchParams.set('a', appId);
21+
request.searchParams.set('sa', '1');
22+
request.searchParams.set('v', agentVersion);
23+
request.searchParams.set('t', 'Unnamed%20Transaction');
24+
request.searchParams.set('rst', '1891');
25+
request.searchParams.set('ck', '0');
26+
request.searchParams.set('s', session);
27+
request.searchParams.set('ptid', ptid);
28+
request.searchParams.set('ref', 'https://staging-one.newrelic.com/catalogs/software');
29+
request.searchParams.set('ht', '1');
30+
request.searchParams.set('hr', '1');
31+
request.searchParams.set('af', 'err,spa,xhr,stn,ins');
32+
request.searchParams.set('be', '104');
33+
request.searchParams.set('fe', '1503');
34+
request.searchParams.set('dc', '1467');
35+
request.searchParams.set('fsh', '1');
36+
request.searchParams.set('fp', '416');
37+
request.searchParams.set('fcp', '416');
38+
request.searchParams.set('timestamp', (timeNow - (offset * 2)).toString());
39+
40+
const response = await fetch(request.toString(), {
41+
method: 'POST',
42+
headers,
43+
referrer: "https://staging-one.newrelic.com/",
44+
body: JSON.stringify({ ja: { 'enduser.id': enduserId } })
45+
});
46+
if (!response.ok) console.log('harvest failed', response.status, response.statusText);
47+
else {
48+
payloadMetadata.payloadsSentInJob++
49+
payloadMetadata.harvestCount++
50+
}
51+
52+
return response
53+
}

0 commit comments

Comments
 (0)