pinpoint-node-agent
Version:
Pinpoint node agent provided by NAVER
134 lines (111 loc) • 3.94 kB
JavaScript
/**
* Pinpoint Node.js Agent
* Copyright 2021-present NAVER Corp.
* Apache License v2.0
*/
'use strict'
const { Transform } = require('node:stream')
const GrpcWritableStreamBuilder = require('./grpc-writable-stream-builder')
// Java Agent queue size private static final int DEFAULT_AGENT_SENDER_EXECUTOR_QUEUE_SIZE = 1000;
class GrpcReadableStream {
constructor(makeWritableStream, deadlineOptionsBuilder, writableAndEnded) {
this.makeWritableStream = makeWritableStream
this.deadlineOptionsBuilder = deadlineOptionsBuilder
this.writableAndEnded = writableAndEnded
this.readableStream = this.makeReadableStream()
this.pipeWritableStream()
}
makeReadableStream() {
const readableStream = new Transform({
readableObjectMode: true,
highWaterMark: 100,
transform(chunk, encoding, callback) {
callback(null, chunk)
},
// https://nodejs.org/api/stream.html#readablepushchunk-encoding
read: () => {
this.readStart()
}
})
readableStream.on('error', () => {
// https://nodejs.org/api/stream.html#readablepipedestination-options
// `One important caveat is that if the Readable stream emits an error during processing,
// the Writable destination is not closed automatically.
// If an error occurs, it will be necessary to manually close each stream
// in order to prevent memory leaks.`
// for readable steam error memory leak prevention
this.writableStream?.end()
})
return readableStream
}
readStart() {
this.readable = true
}
push(data) {
if (this.writableStream?.shouldCreateNewStream()) {
this.pipeWritableStream()
}
if (this.readable === false) {
return
}
if (this.writableStream?.writableEnded === true) {
return
}
if (!this.readableStream.push(data)) {
this.readStop()
}
}
readStop() {
this.readable = false
}
pipeWritableStream() {
if (this.ended) {
return
}
this.writableStream?.end()
this.writableStream = new GrpcWritableStreamBuilder(this.makeWritableStream)
.setDeadlineOptionsBuilder(this.deadlineOptionsBuilder)
.setCallCallback((error, response) => {
if (typeof this.callback === 'function') {
this.callback(error, response)
}
})
.setWritableAndEnded(this.writableAndEnded)
.build((writableStream) => {
this.readableStream.pipe(writableStream)
})
}
end() {
this.ended = true
// writableStream.end() is called by function onend() { dest.end() } in Readable.prototype.pipe()
this.readableStream.end()
this.writableStream?.end()
}
}
class GrpcReadableStreamBuilder {
constructor(serviceClient, method) {
this.serviceClient = serviceClient
this.method = method
}
setDeadlineOptionsBuilder(deadlineOptionsBuilder) {
this.deadlineOptionsBuilder = deadlineOptionsBuilder
return this
}
setCallback(callback) {
this.callback = callback
return this
}
setWritableStreamWritableAndEnded(writableAndEnded) {
this.writableAndEnded = writableAndEnded
return this
}
build() {
const serviceClientCall = this.serviceClient[this.method].bind(this.serviceClient)
const stream = new GrpcReadableStream(serviceClientCall, this.deadlineOptionsBuilder, this.writableAndEnded)
if (this.callback) {
stream.callback = this.callback
}
return stream
}
}
module.exports = GrpcReadableStreamBuilder