Sitelet https://github.com/nodejs/node/issues/43931
Skip to content

On Windows, dgram sockets can only receive one UDP datagram per event loop iteration #43931

Description

@rgprog

Version

v16.16.0 (standalone), v16.14.2 (running within Electron 19.0.0)

Platform

Windows 11 (Microsoft Windows NT 10.0.22000.0 x64)

Subsystem

dgram

What steps will reproduce the bug?

const dgram = require('dgram');

let loopIterations = 0;
let callback;
callback = () => {
    loopIterations += 1;

    //nested setImmediate calls fire once per event loop
    setImmediate(callback);
};

let udp = dgram.createSocket({ type: 'udp4' });

udp.on('message', (data, rinfo) => {
    console.log(loopIterations);
});

const PORT = 17654;
udp.bind(PORT, () => {
    udp.connect(PORT, () => {
        udp.setSendBufferSize(1024 * 1024);
        udp.setRecvBufferSize(1024 * 1024);

        setImmediate(callback);

        //once per second, send a large number of UDP packets to our socket
        setInterval(() => {
            for (let i=0; i<16 * 1024; i++) {
                udp.send(" ");
            }
        }, 1000);
    });
});

How often does it reproduce? Is there a required condition?

This bug reproduces unconditionally on Windows, but does not seem to reproduce on Linux.

What is the expected behavior?

When multiple UDP datagrams are available, several of them should be presented to the 'message' callback within each event loop iteration. (This is the behaviour on Linux.) The test program should print the same integer multiple times in a row.

What do you see instead?

The same integer is never printed twice. Consecutive integers are printed (308, 309, 310...). This indicates that only one UDP datagram is delivered per event loop iteration, even when more datagrams are present in the socket's receive buffer.

Additional information

This bug seems inconsequential on a standalone Node installation. On my machine, the Node event loop has a throughput approaching 1,000,000 iterations per second. Assuming 1024 bytes per datagram, the event loop can deliver 8 Gbps of data.

However, in Electron, the Node event loop is much slower (presumably due to coordination with Chromium's event loop). On my machine, a typical throughput would be 20,000 to 40,000 iterations per second, intermittently dipping below 10,000 iterations per second.

This introduces a significant network bottleneck: the highest safe data rate, even through a local socket, is about 0.08 Gbps. At lower data rates, the bug still introduces up to 100ms of transfer latency per megabyte of data.

Activity

  1. added
    dgramIssues and PRs related to UDP and the dgram module.
    on Jul 21, 2022
  2. rgprog commented on Jul 21, 2022

    @rgprog
    Author

    The bug didn't reproduce on Mac. On both Linux and Mac, the maximum number of datagrams processed in each event-loop iteration seems to be 32.

  3. bnoordhuis commented on Jul 22, 2022

    @bnoordhuis
    Member

    That's right, libuv on UNIX systems reads up to 32 datagrams per event loop "tick". The reason for that limit is to prevent denial-of-service attacks.

    I've opened libuv/libuv#3704 with suggestions on how to improve the WIndows performance. I'm not going to work on it myself but I'll review pull requests.

  4. added
    windowsIssues and PRs related to the Windows platform.
    libuvIssues and PRs related to the libuv dependency or the uv binding.
    on Jul 22, 2022
  5. Yokubjon-J commented on Jul 28, 2022

    @Yokubjon-J

    I have put my test file in 'test/internet' dir, but when I run ./configure && make -j4 test (on git bash, hoping it would execute the test file), it just exits returning Python,
    image
    I would like to know how my local node environment can run the test file without me typing node test/internet/testFile.js.

  6. bnoordhuis commented on Jul 28, 2022

    @bnoordhuis
    Member

    python tools/test.py internet/test-your-test-name-here - also supports globbing

    test/internet probably isn't the best place for it, that's for tests that need an actual internet connection. Tests that can run on localhost should go into test/parallel (preferred) or test/sequential.

  7. santigimeno commented on Nov 13, 2022

    @santigimeno
    Member

    This is already fixed on libuv. It should be available in the next libuv release.

  8. 1 remaining item

  9. added a commit that references this issue on Sep 10, 2023
  10. added a commit that references this issue on Feb 18, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    dgramIssues and PRs related to UDP and the dgram module.libuvIssues and PRs related to the libuv dependency or the uv binding.windowsIssues and PRs related to the Windows platform.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions