-
Notifications
You must be signed in to change notification settings - Fork 885
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
[ WIP ] pinole integration, next gen transports #937
Conversation
I would err for a more incremental approach and keep the current behavior as default - do not send data to a worker thread unless asked to. +1 for the great work. |
This is the current approach - if no transport option is passed to Pinole it returns a SonicBoom instance Transports always go in a worker thread - destinations are always written to in the main thread. This has me thinking though - if there’s only 1 cpu it’s probably faster to run the transport in the main thread which we can also do I’m also thinking of supporting multiple transports/destinations in pino next - just allow an array of transports and/or destinations to be passed as the stream option. Thoughts on that? |
😬 Not sure. I'd love to deprecate |
We should really embed pino-multi-stream if we went that route. |
ok let's get this done and look at that separately |
This still needs:
|
@davidmarkclements this looks awesome! Where we at with this? |
We are reworking the worker thread data transport as there were a few cases of data loss. You can follow our progress on that at https://www.npmjs.com/package/thread-stream. |
@davidmarkclements I'm not sure we can safely implement this concept of transform as pure functions. We need streams. |
@mcollina and I had a call about this.
I'm going to close this PR now, in favour of #1003 and #1004 |
This pull request has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs. |
For background see #614 and https://github.com/pinojs/pinole
flushSync
Roll out plan
Pino transport API
Package support:
File path support:
The options object must be clonable because it's passed to the worker thread - e.g. no functions etc.
Next Gen Transports
Transports may be ESM or CJS. I'm encouraging ESM for transports.
A canonical next gen transport runs in a worker thread and looks as follows:
For asynchronous initialisation, using ESM you have Top level await but
for any async work based on opts you can make the exported function async:
For asynchronous writes, make the returned writing function async:
Making the write function async is tolerable to performance constraints because this is
happening off the main thread - the ergonomics of async/await can win here.
Legacy Support
Support for existing transports works like so:
package.json
has a"bin"
fieldpackage.json
has a"pino"
field that doesn't have the value "legacy"Then it's determined to be legacy. This means the transports that don't work should work automatically. But they can disambiguate by adding
"pino": "legacy"
to thepackage.json
.Pinole
wraps legacy transports in a next gen transport wrapper and and feeds data intoprocess.stdin
while working around the stdout/stderr disconnect race scenario you get with worker threads during the final tick of process exit: see https://github.com/pinojs/pinole/blob/master/lib/legacy.jsThis means
flushSync
works with legacy as well.Anything that isn't a module must be a next gen transport.
Modules that have a
package.json
without a"bin"
field are interpreted as next gen transportsModules that have a
package.json
with a"bin"
field but with "pino": "v7" (or really anything that doesn't say legacy) are interpreted as next gen transportsHere's
pino-coloda
getting the legacy treatment fromPinole
- this ispino-colada
running inside a worker thread, with no modifications topino-colada
itself:Approach: Performance & Safety
This approach supports flushSync by blocking the main thread using
Atomics
which unblocks conditionally when the worker thread updates a element in aSharedArrayBuffer
. This means in the final tick (egprocess.on('exit', ...)
) we can still do asynchronous work in the worker thread while keeping the main thread alive.During normal operations no
Atomics
are used, and we purposefully avoid blocking the main thread at all costs. We use the worker thread to poll theSharedArrayBuffer
, the only work the main thread does is write into theSharedArrayBuffer
(in the same way thatsonic-boom
writes to a buffer, usingTextEncoder
) and then update a meta-data section of theSharedArrayBuffer
so that the worker thread knows to pass the log line(s) to the transport.Todo
Lot's more to do but I wanted to get eyes on this sooner rather than later