Skip to content
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

"Callback was already called" using wait and capture/match with websocket engine #3476

Open
xentac opened this issue Feb 12, 2025 · 0 comments

Comments

@xentac
Copy link

xentac commented Feb 12, 2025

If your websocket server sends multiple messages while a wait with a capture/match is being processed, you will get a "Callback was already called" stack trace. This is mostly because artillery was written with the assumption that each request will have a response and wait doesn't quite fit into that paradigm. Should a wait consume all messages until it finds a match/capture or should it just consume the next one and the user is responsible for using loop/whileTrue to process all the messages.

Surrounding your wait step with think steps can help clear the message queue before processing a single message, but this will lead to lost messages which could be important. A good load test will need to be able to process every message at one point or another.

My theory is that websocket messages are queued up and multiples are passed to the captureOrMatch method instead of just a single one before processing can happen on the next step.

Whatever the decision is, a stacktrace shouldn't be happening in this case.

Version info:

        ___         __  _ ____
  _____/   |  _____/ /_(_) / /__  _______  __ ___
/____/ /| | / ___/ __/ / / / _ \/ ___/ / / /____/
/____/ ___ |/ /  / /_/ / / /  __/ /  / /_/ /____/
    /_/  |_/_/   \__/_/_/_/\___/_/   \__  /
                                    /____/
 
 
VERSION INFO:
 
Artillery: 2.0.21
Node.js:   v20.18.1
OS:        Darwin

Running this command:

npx artillery run minimal-artillery.yaml

I expected to see this happen:

A successful load test

Instead, this happened:

worker error, id: 1 Error: Callback was already called.
    at …/node_modules/@artilleryio/int-commons/node_modules/async/dist/async.js:969:32
    at captured (…/node_modules/@artilleryio/int-core/lib/engine_ws.js:110:16)
    at …/node_modules/@artilleryio/int-commons/engine_util.js:498:16
    at …/node_modules/@artilleryio/int-commons/node_modules/async/dist/async.js:473:16
    at replenish (…/node_modules/@artilleryio/int-commons/node_modules/async/dist/async.js:1009:25)
    at …/node_modules/@artilleryio/int-commons/node_modules/async/dist/async.js:1019:9
    at eachLimit$1 (…/node_modules/@artilleryio/int-commons/node_modules/async/dist/async.js:3199:24)
    at Object.<anonymous> (…/node_modules/@artilleryio/int-commons/node_modules/async/dist/async.js:1049:16)
    at Object.captureOrMatch (…/node_modules/@artilleryio/int-commons/engine_util.js:414:9)
    at WebSocket.messageHandler (…/node_modules/@artilleryio/int-core/lib/engine_ws.js:68:16)

Files being used:

minimal-artillery.yaml

config:
  target: "ws://localhost:8080"
  phases:
    - name: warm up
      duration: 20
      rampTo: 2
  plugins:
    metrics-by-endpoint: {}
scenarios:
  - name: Test
    engine: ws
    flow:
      - wait:
          capture:
            json: "$"
            as: "result"

minimal-websocket.js

import { WebSocketServer } from "ws";
 
const wss = new WebSocketServer({ port: 8080 });
 
wss.on("connection", function connection(ws) {
                for (let i = 0; i < 20; i++) {
                                ws.send("something");
                }
                ws.terminate();
});
@xentac xentac changed the title "Callback was already called" using wait and capture/match with websocket engie "Callback was already called" using wait and capture/match with websocket engine Feb 12, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant