github.com/anycable/anycable-go@v1.5.1/etc/k6/broker.js (about)

     1  // Build k6 with xk6-cable like this:
     2  //    xk6 build v0.38.3 --with github.com/anycable/xk6-cable@v0.3.0
     3  
     4  import { check, sleep, fail } from "k6";
     5  import cable from "k6/x/cable";
     6  import { randomIntBetween } from "https://jslib.k6.io/k6-utils/1.1.0/index.js";
     7  
     8  const rampingOptions = {
     9    scenarios: {
    10      default: {
    11        executor: "ramping-vus",
    12        startVUs: 100,
    13        stages: [
    14          { duration: "10s", target: 300 },
    15          { duration: "10s", target: 500 },
    16          { duration: "10s", target: 1000 },
    17          { duration: "10s", target: 1300 },
    18          { duration: "10s", target: 1500 },
    19          { duration: "30s", target: 1500 },
    20          { duration: "30s", target: 0 },
    21        ],
    22        gracefulStop: "1m",
    23        gracefulRampDown: "1m",
    24      },
    25    },
    26  };
    27  
    28  export const options = __ENV.SKIP_OPTIONS ? {} : rampingOptions;
    29  
    30  import { Trend, Counter } from "k6/metrics";
    31  let rttTrend = new Trend("rtt", true);
    32  let subTrend = new Trend("suback", true);
    33  let broadcastTrend = new Trend("broadcast_duration", true);
    34  let historyTrend = new Trend("history_duration", true);
    35  let historyRcvd = new Counter("history_rcvd");
    36  let broadcastsSent = new Counter("broadcasts_sent");
    37  let broadcastsRcvd = new Counter("broadcasts_rcvd");
    38  let acksRcvd = new Counter("acks_rcvd");
    39  
    40  // Load ENV from .env
    41  function loadDotEnv() {
    42    try {
    43      let dotenv = open("./.env");
    44      dotenv.split(/[\n\r]/m).forEach((line) => {
    45        // Ignore comments
    46        if (line[0] === "#") return;
    47  
    48        let parts = line.split("=", 2);
    49  
    50        __ENV[parts[0]] = parts[1];
    51      });
    52    } catch (_err) {}
    53  }
    54  
    55  loadDotEnv();
    56  
    57  let config = __ENV;
    58  
    59  config.URL = config.URL || "ws://localhost:8080/cable";
    60  
    61  let url = config.URL;
    62  let channelName = config.CHANNEL_ID || "BenchmarkChannel";
    63  
    64  let numChannels = parseInt(config.NUM_CHANNELS || "5") || 5;
    65  let channelStreamId = __VU % numChannels;
    66  
    67  let sendersRatio = parseFloat(config.SENDERS_RATIO || "0.3") || 1;
    68  let sendersMod = (1 / sendersRatio) | 0;
    69  let sender = __VU % sendersMod == 0;
    70  
    71  let sendingRate = parseFloat(config.SENDING_RATE || "0.3");
    72  
    73  let iterations = (config.N || "20") | 0;
    74  
    75  export default function () {
    76    let cableOptions = {
    77      receiveTimeoutMs: 15000,
    78      logLevel: config.DEBUG === "true" ? "debug" : "info",
    79    };
    80  
    81    // Prevent from creating a lot of connections at once
    82    sleep(randomIntBetween(2, 10) / 5);
    83  
    84    let client = cable.connect(url, cableOptions);
    85  
    86    if (
    87      !check(client, {
    88        "successful connection": (obj) => obj,
    89      })
    90    ) {
    91      // Cooldown
    92      sleep(randomIntBetween(5, 10) / 5);
    93      fail("connection failed");
    94    }
    95  
    96    let channel = client.subscribe(channelName, { id: channelStreamId });
    97  
    98    if (
    99      !check(channel, {
   100        "successful subscription": (obj) => obj,
   101      })
   102    ) {
   103      // Cooldown
   104      sleep(randomIntBetween(5, 10) / 5);
   105      fail("failed to subscribe");
   106    }
   107  
   108    subTrend.add(channel.ackDuration());
   109  
   110    let subscribedAt = Date.now();
   111  
   112    let result = channel.history({ since: ((Date.now() - 10000) / 1000) | 0 });
   113    check(result, {
   114      "history received": (obj) => obj,
   115    });
   116    historyTrend.add(channel.historyDuration());
   117  
   118    for (let i = 0; ; i++) {
   119      // Sampling
   120      if (sender && randomIntBetween(1, 10) / 10 <= sendingRate) {
   121        let start = Date.now();
   122        broadcastsSent.add(1);
   123        // Create message via cable instead of a form
   124        channel.perform("broadcast", {
   125          ts: start,
   126          content: `hello from ${__VU} numero ${i + 1}`,
   127        });
   128      }
   129  
   130      sleep(randomIntBetween(5, 10) / 100);
   131  
   132      let incoming = channel.receiveAll(1);
   133  
   134      for (let message of incoming) {
   135        let received = message.__timestamp__;
   136  
   137        if (message.action == "broadcastResult") {
   138          acksRcvd.add(1);
   139          let ts = message.ts;
   140          rttTrend.add(received - ts);
   141        }
   142  
   143        if (message.action == "broadcast") {
   144          let ts = message.ts;
   145  
   146          // Historical message, shouldn't be taken into account for broadcast duration
   147          if (ts < subscribedAt) {
   148            historyRcvd.add(1);
   149            continue;
   150          }
   151  
   152          broadcastsRcvd.add(1);
   153          broadcastTrend.add(received - ts);
   154        }
   155      }
   156  
   157      sleep(randomIntBetween(5, 10) / 10);
   158  
   159      if (i > iterations) break;
   160    }
   161  
   162    sleep(randomIntBetween(5, 10) / 10);
   163  
   164    client.disconnect();
   165  }