-
Notifications
You must be signed in to change notification settings - Fork 63
chore: add memory profiling in streams examples #946
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
Merged
Merged
Changes from 2 commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
# Node modules | ||
node_modules/ | ||
|
||
# Large files | ||
mem_leak_data1/* | ||
mem_leak_data_default/* |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
# Streams - Memory Stress Test and Memory Profiling | ||
This directory contains test code for running a memory profiler when using the `encryptStream` and `decryptUnsignedMessageStream`. | ||
This directory contains everything you need to run a memory profiler on these two operations. | ||
|
||
## Requirements | ||
- Node >= 12 | ||
- Chrome Browser | ||
|
||
## How to run the application and memory profiler | ||
1. For easier debugging open two chrome windows | ||
1. One where you can look at the profiler | ||
2. One where you can navigate through the application paths. | ||
1. On Chrome, navigate to: `chrome://inspect/#devices` | ||
1. Make sure you are in the `stream_mem_stress_test` directory. | ||
1. Start debugger and server by running `npm run start` | ||
1. On the devices page click on `inspect` for the remote target that just appeared | ||
1. Navigate to the Memory tab. You will have three options: | ||
1. Heap snapshot | ||
- Useful to focus on a specific action during runtime. | ||
1. Allocation instrumentation on timeline | ||
- Works better for our stress tests since we can see memory allocation and | ||
garbage collection during runtime | ||
1. Allocation Sampling (not useful for our test) | ||
1. Navigate to any of the provided paths and watch memory allocation and garbage collection in | ||
real time 🍿 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
import express from 'express' | ||
import { kmsDecryptStream, kmsEncryptStream } from './memory' | ||
|
||
const app = express() | ||
const leak = [] | ||
|
||
app.get('/', (req, res) => { | ||
// Quick little Test so we can check both in browser and in terminal | ||
res.write("Test") | ||
res.send() | ||
console.log("Back in home") | ||
}) | ||
|
||
/** This path is here to see how a memory leak would look like in the memory profiler. | ||
* To test it you can run `npm run load-mem` to see memory get allocated but never | ||
* garbage collected | ||
*/ | ||
app.get('/now', (req, res) => { | ||
let resp = JSON.stringify({ now: new Date() }) | ||
leak.push(JSON.parse(resp)) | ||
res.writeHead(200, { 'Content-Type': 'application/json' }) | ||
res.write(resp) | ||
res.end() | ||
}) | ||
|
||
/** Path to read 1mb file, parameter to readFile takes any file in the current directory | ||
* so you can test with any file you'd like. We supply 2 test files for you to use. | ||
*/ | ||
app.get('/readRandom_1mb', (req, res) => { | ||
res.write("Attempt to encrypt 1mb random") | ||
// you can optionaly pass in a frame size, otherwise uses the default frame size of 4096 bytes | ||
readFile('./random_1mb.txt') | ||
res.send() | ||
}) | ||
|
||
// Path to decrypt 1mb encrypted file | ||
app.get('/readRandom_1mbEnc', (req, res) => { | ||
res.write("Attempt to decrypt ./random_1mb.txt.encrypted") | ||
readEncryptedFile('./random_1mb.txt.encrypted') | ||
res.send() | ||
|
||
}) | ||
|
||
// Path to encrypt 5mb file | ||
app.get('/readRandom_5mb', (req, res) => { | ||
res.write("Attempt to encrypt 5mb random") | ||
readFile('./random_5mb.txt', 1) | ||
res.send() | ||
}) | ||
|
||
// Path to decrypt 5mb encrypted file | ||
app.get('/readRandom_5mbEnc', (req, res) => { | ||
res.write("Attempt to decrypt ./random_5mb.txt.encrypted") | ||
readEncryptedFile('./random_5mb.txt.encrypted') | ||
res.send() | ||
|
||
}) | ||
|
||
/** Path to encrypt 1Gb file, not included in this directory because size exceeds GitHub transfer size limit. | ||
* If you want to create a 1Gb file of random data, you can do so by running | ||
* `dd if=/dev/urandom of=rand_1gb.txt bs=1024 count=1024000` on a linux system | ||
*/ | ||
app.get('/readRandom_1gb', (req, res) => { | ||
res.write("Attempt to encrypt large random") | ||
readFile('./rand_1gb.txt') | ||
res.send() | ||
}) | ||
|
||
// Path to decrypt 1mb encrypted file | ||
app.get('/readRandom_1gbEnc', (req, res) => { | ||
res.write("Attempt to encrypt large random") | ||
kmsDecryptStream('./rand_1gb.txt.encrypted') | ||
res.send() | ||
}) | ||
|
||
app.listen(3000, () => { | ||
console.log("Listening on port 3000"); | ||
}) | ||
|
||
async function readFile(filename:string, framesize?:number) { | ||
await kmsEncryptStream(filename, framesize); | ||
} | ||
|
||
async function readEncryptedFile(filename: string) { | ||
await kmsDecryptStream(filename) | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,101 @@ | ||||||
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. | ||||||
// SPDX-License-Identifier: Apache-2.0 | ||||||
import { | ||||||
KmsKeyringNode, | ||||||
buildClient, | ||||||
CommitmentPolicy | ||||||
} from '@aws-crypto/client-node' | ||||||
import {AlgorithmSuiteIdentifier} from '@aws-crypto/material-management' | ||||||
|
||||||
/* This builds client with the REQUIRE_ENCRYPT_REQUIRE_DECRYPT commitment policy. | ||||||
* REQUIRE_ENCRYPT_REQUIRE_DECRYPT enforces that this client only encrypts using committing algorithm suites | ||||||
* and enforces that this client will only decrypt encrypted messages | ||||||
* that were created with a committing algorithm suite. | ||||||
* This is the default if you build `buildClient()`. | ||||||
*/ | ||||||
const { encryptStream, decryptUnsignedMessageStream } = buildClient( | ||||||
CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT | ||||||
) | ||||||
|
||||||
import { finished } from 'stream' | ||||||
import { createReadStream, createWriteStream } from 'fs' | ||||||
import { promisify } from 'util' | ||||||
texastony marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
const finishedAsync = promisify(finished) | ||||||
|
||||||
/* A KMS CMK is required to generate the data key. | ||||||
* You need kms:GenerateDataKey permission on the CMK in generatorKeyId. | ||||||
* This key is public, DO NOT USE in production environment. | ||||||
*/ | ||||||
const generatorKeyId = | ||||||
'arn:aws:kms:us-west-2:658956600833:alias/EncryptDecrypt' | ||||||
|
||||||
// configure keyring with CMK(s) you wish to work with | ||||||
const keyring = new KmsKeyringNode({ generatorKeyId }) | ||||||
|
||||||
/** | ||||||
* Encryption Context is very useful if you want to assert things about the encrypted data | ||||||
* See: https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context | ||||||
*/ | ||||||
const context = { | ||||||
stage: 'demo', | ||||||
purpose: 'streaming memory stress test', | ||||||
origin: 'us-west-2', | ||||||
} | ||||||
|
||||||
/** | ||||||
* kmsEncryptStream will use the encryptStream function and create a pipeline to encrypt a stream of data | ||||||
* from a file and write it to destination `./{filename}.encrypted` | ||||||
* @param filename string of file name you wish to encrypt | ||||||
* @param framesize optional parameter to determine frame size; default is 4096 bytes | ||||||
*/ | ||||||
export async function kmsEncryptStream(filename:string, framesize?:number) { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: There should be a space b/w the variable and its type:
Suggested change
|
||||||
const readable = createReadStream(filename) | ||||||
const encFile = filename + '.encrypted' | ||||||
const writeable = createWriteStream(encFile) | ||||||
|
||||||
// pipeline of read stream | ||||||
readable.pipe( | ||||||
encryptStream(keyring, { | ||||||
/** | ||||||
* Since we are streaming, and assuming that the encryption and decryption contexts | ||||||
* are equally trusted, using an unsigned algorithm suite is faster and avoids | ||||||
* the possibility of processing plaintext before the signature is verified. | ||||||
*/ | ||||||
suiteId: | ||||||
AlgorithmSuiteIdentifier.ALG_AES256_GCM_IV12_TAG16_HKDF_SHA512_COMMIT_KEY, | ||||||
encryptionContext: context, | ||||||
frameLength: framesize | ||||||
}) | ||||||
).pipe(writeable.on('finish', () => { | ||||||
// output from encryptStream will get piped to writeable | ||||||
console.log(`The new file name is ${encFile}.`); | ||||||
})) | ||||||
|
||||||
await finishedAsync(writeable) | ||||||
console.log("Finished Encrypting"); | ||||||
|
||||||
} | ||||||
/** | ||||||
* kmsDecryptStream will take a filename and create a decryption stream to decrypt contents of file | ||||||
* and write it to destination `./{filename}.decrypted | ||||||
* @param filename string of file name you wish to encrypt | ||||||
*/ | ||||||
export async function kmsDecryptStream(filename: string) { | ||||||
const readable = createReadStream(filename) | ||||||
const decFile = filename + '.decrypted' | ||||||
const writeable = createWriteStream(decFile) | ||||||
|
||||||
readable.pipe( | ||||||
/** | ||||||
* decryptUnsignedMessageStream is recommended when streaming if you don't need | ||||||
* digital signatures. | ||||||
*/ | ||||||
decryptUnsignedMessageStream(keyring) | ||||||
).pipe(writeable.on('finish', () => { | ||||||
console.log(`The new file name is ${decFile}.`); | ||||||
})) | ||||||
|
||||||
await finishedAsync(writeable) | ||||||
console.log("Finished Decrypting") | ||||||
} | ||||||
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Space b/w variable and it's type in signatures: