npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

async-leak-tracker

v0.0.2

Published

``async-leak-tracker`` tracks leaks of async events ===================================================

Downloads

3

Readme

async-leak-tracker tracks leaks of async events

VERY ALPHA WARNING: this software is very alpha and probably doesn't work very well yet.

async-leak-tracker tracks leaks of async events in Node applications.

For example::

$ cat leaky.js
import { trackLeakedEvents } from 'async-leak-tracker'

function leakSetTimeout() {
  setTimeout(() => { console.log('this leaked!') }, 100)
  return 'done!'
}

async function run() {
  const result = await trackLeakedEvents(leakSetTimeout)
  console.log('Result:', JSON.stringify(result, null, 2))
}
$ node leaky.js
Result: {
  "leakedEventCount": 1,
  "leakedEvents": [
    {
      "timestamp": "2019-03-18T02:45:58.136Z",
      "asyncID": 8,
      "stack": [
        "    at Timer.emitInitNative (internal/async_hooks.js:131:43)",
        "    at new TimersList (timers.js:212:31)",
        "    at insert (timers.js:186:27)",
        "    at exports.active (timers.js:159:3)",
        "    at new Timeout (timers.js:571:3)",
        "    at setTimeout (timers.js:449:10)",
        "    at leakSetTimeout (.../leaky.js:4:3)",
        "    at LeakedEventTracker.<anonymous> (.../async-leak-tracker/src/index.js:85:18)",
        "    at Generator.next (<anonymous>)"
      ]
    }
  ],
  "result": "done!"
}
this leaked!

Current Status

Currently tracks:

  • Timer events (ie, setTimeout)

    • See Timeout Handle Re-Use_
  • TCP socket client + server

    • Because socket.end()__ only half-closes the socket, the socket will not be considered entirely closed until the close event is emitted.

      This may lead to some confusion, as the following apparently correct code will leak an event::

      function openAndCloseSocketLeaking() { return new Promise(res => { const client = net.createConnect({ ... }, () => { client.end(() => res) }) }) }

      To be completely correct, the socket can only be considered closed once the close event is emitted::

      function openAndCloseSocketCorrect() { return new Promise(res => { const client = net.createConnect({ ... }, () => { client.end() })

        client.on('close', res)
      })

      }

__ https://nodejs.org/api/net.html#net_socket_end_data_encoding_callback

Using with Tests

The LeakedEventTracker.start() and LeakedEventTracker.stop() methods can be used in before() and after() to assert that tests don't leak.

(note: see Caveats_)

For example::

import { LeakedEventTracker } from 'async-leak-tracker'

const leakTracker = new LeakedEventTracker()

before(() => leakTracker.start()) after(() => { const res = leakTracker.stop() assert.equal( res.leakedEventCount, 0, Leaked events: ${JSON.stringify(res, null, 2)}, ) })

it('is a sync test', () => {})

it('does not leak', async () => { await new Promise(res => setTimeout(res, 1)) })

it('leaks (this is expected to fail)', () => { setTimeout(() => {}, 10) })

Testing

Run tests with npm run test or npm run test:watch (note: one is expected to fail, and some are currently failing; see Timeout Handle Re-Use_; also, tests get progressively slower in watch mode).

Debug with DEBUG=1 npm run test

Caveats

Testing Frameworks

Testing frameworks may start timers during the regular course of a test run (for example, when mocha detects that a test is async, it starts a timeout timer).

These timeouts can be ignored using the eventsToIgnore option (see LeakedEventTrackerOptions), but currently only Mocha is included.

Timeout Handle Re-Use

Node appears to re-use libuv timer handles, so under certain conditions timeout leaks will not be detected.

This is purely speculation, but it appears to happen when:

  1. Multiple timeouts are created with the same timeout at the same time::

    function leaky() { setTimeout(() => {}, 100) setTimeout(() => {}, 100) } trackLeakedEvents(leaky) => { leakedEventCount: 1 }

  2. There is a completed timeout handle which hasn't been destroyed::

    function leaky() { setTimeout(() => {}, 100) } await new Promise(res => setTimeout(res, 1) // this handle will be reused trackLeakedEvents(leaky) => { leakedEventCount: 0 }

And these leaks are not detected because, currently, async-leak-tracker uses the init async hook to detect when a timeout is created. It should be possible to resolve this by creating a single instance of the leak tracker before any timeouts are created, then tracking leaks using the before async hook instead of the init hook.

Changing to a single instance of the leak tracker would likely significantly improve performance too, as async hooks can be very slow (notice that, in watch mode, each run gets progressively slower until they start to time out; see also: comments in LeakedEventTracker.start())