Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add switchboard artillery target #25

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 11 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
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
},
"dependencies": {
"artillery": "^2.0.0-33",
"artillery-plugin-metrics-by-endpoint": "^1.2.0",
"csv-parse": "^5.4.0",
"csv-stringify": "^6.4.0",
"dotenv": "^16.1.4",
Expand Down
5 changes: 5 additions & 0 deletions targets/switchboard/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
SWITCHBOARD_CLIENT_ID="00000000-0000-4000-0000-000000000000"
Copy link

Choose a reason for hiding this comment

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

Question (not important): Do you intentionally use 4000 in the middle 😆

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, UUIDv4 always has a 4 at that position

SWITCHBOARD_AUTH_TOKEN="SomethingLongAndSecure"
SWITCHBOARD_SENDING_ACCOUNT_ID="00000000-0000-4000-0000-000000000000"
SWITCHBOARD_PROFILE_ID="00000000-0000-4000-0000-000000000000"
BANDWIDTH_CALLBACK_BASIC_AUTH="Base64Encoded(Username:Password)"
56 changes: 56 additions & 0 deletions targets/switchboard/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Switchboard Artillery Load Testing

Perform load test against a dry run mode (no calls to 3rd party telecom providers) Switchboard deployment.

## Setup

**Stand up Switchboard shard in dry mode**

TBD

**Set up billing.clients, sms.sending_accounts, sms.profiles, etc.**

TBD

**Set values for Artillery use**

Copy and update the provided example `.env`

```sh
cp .env.example .env
vi .env
```

## Running

Artillery scripts can be run locally like this:

```sh
yarn artillery run \
--platform=local \
--environment=staging \
--dotenv=targets/switchboard/.env \
targets/switchboard/artillery-scripts/send-message.yaml
```

Artillery scripts can be run from AWS Lambda like this:

Get session for MFA-enabled users. The session returned by the command may be set as the AWS credentials [a few different ways](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html).

```sh
aws sts get-session-token \
--serial-number arn:aws:iam::123456789012:mfa/user \
--token-code code-from-token
```

Run the script on Lambda:

```sh
yarn artillery run \
--platform=aws:lambda \
--platform-opt region=us-east-1 \
--platform-opt memory-size=2048 \
--environment=staging \
--dotenv=targets/switchboard/.env \
targets/switchboard/artillery-scripts/send-message.yaml
```
106 changes: 106 additions & 0 deletions targets/switchboard/artillery-scripts/send-message.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
config:
environments:
staging:
target: "https://staging.switchboard.assemble.live"
phases:
Copy link

Choose a reason for hiding this comment

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

Question: Do we want to treat these like unit tests where these tests all need to pass before a production release occurs?

If failing tests are ok, considering you saw good performance up to 500 so far, you could add tests up to 1000? Our Bandwidth rate limit for the registered account is now set to 900, so that feels like a goal to shoot for even if we're not ready to get there yet.

Copy link

@lediur lediur Jul 14, 2023

Choose a reason for hiding this comment

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

I like the idea of testing on a kind of log scale beyond our current external rate limits so we know our sending rate is artificially limited by policy rather than software performance.

So like Aashish mentioned I would set phases for like 100, 200, 400, 800, 1600, 3200 to start with, then doing a bit of a binary search to add new phases so we have a good sense of "max possible performance" within 100 req/s or so.

Then we can decide how to do a testing strategy once we know the software perf limits.

Copy link
Member Author

@bchrobot bchrobot Jul 14, 2023

Choose a reason for hiding this comment

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

Question: Do we want to treat these like unit tests where these tests all need to pass before a production release occurs?

Treating stress test performance as part of release acceptance seems like a good idea to me but out of scope for this PR.

If failing tests are ok, considering you saw good performance up to 500 so far, you could add tests up to 1000? Our Bandwidth rate limit for the registered account is now set to 900, so that feels like a goal to shoot for even if we're not ready to get there yet.

This is currently blocked by stress test runner limitations. I encountered file descriptor quota exhaustion on Lambda (see 19709a5) and in attempting to fix that ended up down a rabbit hole debugging other AWS runner issues.

- name: Spike - Spoke Autosending @ 100
duration: 180
arrivalRate: 100
- name: Spike - Spoke Autosending @ 200
duration: 180
arrivalRate: 200
- name: Spike - Spoke Autosending @ 300
duration: 180
arrivalRate: 300
- name: Spike - Spoke Autosending @ 400
duration: 180
arrivalRate: 400
- name: Spike - Spoke Autosending @ 500
duration: 180
arrivalRate: 500
plugins:
metrics-by-endpoint: {}
processor: "../processors/sendMessageProcessor.js"

scenarios:
- name: "Sending Bandwidth messages"
flow:
# Execute sendMessage mutation
- post:
url: "/sms/graphql"
beforeRequest: generateSendMessageParams
headers:
token: "{{ $processEnvironment.SWITCHBOARD_AUTH_TOKEN }}"
json:
query: |
mutation sendMessage($profileId: UUID!, $to: PhoneNumber!, $body: String!, $contactZipCode: ZipCode, $mediaUrls: [Url], $sendBefore: Datetime!) {
sendMessage(input: {profileId: $profileId, to: $to, body: $body, contactZipCode: $contactZipCode, mediaUrls: $mediaUrls, sendBefore: $sendBefore}) {
outboundMessage {
id
createdAt
}
}
}
variables:
profileId: "{{ $processEnvironment.SWITCHBOARD_PROFILE_ID }}"
to: "{{ toNumber }}"
body: "An SMS from Artillery load test!"
bchrobot marked this conversation as resolved.
Show resolved Hide resolved
contactZipCode: "11238"
mediaUrls: []
sendBefore: "2024-01-01"
bchrobot marked this conversation as resolved.
Show resolved Hide resolved
capture:
- json: "$.data.sendMessage.outboundMessage.id"
as: "messageId"
- json: "$.data.sendMessage.outboundMessage.createdAt"
as: "messageCreatedAt"

# Give process-grey-route-message a chance to work
- think: 0.2
Copy link

Choose a reason for hiding this comment

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

🧠


# Receive 'message-sending' DLR
- post:
url: "/hooks/status/{{ $processEnvironment.SWITCHBOARD_SENDING_ACCOUNT_ID }}"
headers:
Authorization: "Basic {{ $processEnvironment.BANDWIDTH_CALLBACK_BASIC_AUTH }}"
json:
- time: ""
type: "message-sending"
to: "+16463893770"
errorCode: ""
description: ""
message:
id: "service-{{ messageId }}"
# owner: ""
# applicationId: ""
time: "{{ messageCreatedAt }}"
segmentCount: 1
direction: "outbound"
to:
- "+16463893770"
media: []
text: "An SMS from Artillery load test!"
tag: "v1|{{ messageId }}|{{ messageCreatedAt }}"

# Receive 'message-delivered' DLR
Copy link

Choose a reason for hiding this comment

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

Question: Do we want some think time in btwn here before receiving the 2nd DLR or does it not really make a difference?

Copy link
Member Author

Choose a reason for hiding this comment

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

It doesn't make a difference and in practice we have actually seen the second DLR arrive first (that's why there's logic in Spoke for handling DLR looking at previous message status so we don't accidentally go from DELIVERED to SENT if the sequence gets mixed up I can't actually find this logic in Spoke codebase anymore 🤔).

- post:
url: "/hooks/status/{{ $processEnvironment.SWITCHBOARD_SENDING_ACCOUNT_ID }}"
headers:
Authorization: "Basic {{ $processEnvironment.BANDWIDTH_CALLBACK_BASIC_AUTH }}"
json:
- time: ""
type: "message-delivered"
to: "+16463893770"
errorCode: ""
description: ""
message:
id: "service-{{ messageId }}"
# owner: ""
# applicationId: ""
time: "{{ messageCreatedAt }}"
segmentCount: 1
direction: "outbound"
to:
- "+16463893770"
media: []
text: "An SMS from Artillery load test!"
tag: "v1|{{ messageId }}|{{ messageCreatedAt }}"
10 changes: 10 additions & 0 deletions targets/switchboard/processors/sendMessageProcessor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
function generateSendMessageParams(requestParams, ctx, ee, next) {
const tn = [...Array(10)].map(() => Math.floor(Math.random() * 10)).join("");
const toNumber = `+1${tn}`;

ctx.vars.toNumber = toNumber;

return next();
}

module.exports = { generateSendMessageParams };
7 changes: 7 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2161,6 +2161,13 @@ artillery-plugin-expect@latest:
debug "^4.3.2"
lodash "^4.17.21"

artillery-plugin-metrics-by-endpoint@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/artillery-plugin-metrics-by-endpoint/-/artillery-plugin-metrics-by-endpoint-1.2.0.tgz#eaf9850ab86e200f816cf5d9d4e3b1308a17bbca"
integrity sha512-4SatrmYffyex/j/wjCBW2gVGwgRfLSJFBTuOGM1pJH3l0iEOeHM+/HOzVIUL8IQbnjA/G1WpkJL3bB7jOuX4Dg==
dependencies:
debug "^4.3.2"

artillery-plugin-metrics-by-endpoint@latest:
version "1.0.2"
resolved "https://registry.yarnpkg.com/artillery-plugin-metrics-by-endpoint/-/artillery-plugin-metrics-by-endpoint-1.0.2.tgz#268928dbd4938cfd7b08ccf84d4ada20f009cf99"
Expand Down