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

fix(deps): update module go.k6.io/k6 to v0.55.0 #30

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

renovate[bot]
Copy link
Contributor

@renovate renovate bot commented Oct 17, 2024

This PR contains the following updates:

Package Change Age Adoption Passing Confidence
go.k6.io/k6 v0.52.0 -> v0.55.0 age adoption passing confidence

Release Notes

grafana/k6 (go.k6.io/k6)

v0.55.0

Compare Source

k6 v0.55.0 is here 🎉! This release includes:

  • ⚠️ The deprecated StatsD output has been removed.
  • ⚠️ The experimental k6/experimental/tracing module has been removed.
  • 🆕 URL grouping support in the browser module.
  • 🆕 Top-level await support.
  • 🔐 Complete RSA support for k6/experimental/webcrypto.

Breaking changes

k6/experimental/tracing module removed #3855

The experimental k6/experimental/tracing module has been removed, in favor of a replacement jslib polyfill, please consult our guide on how to migrate, #3855.

StatsD output removed #3849

The StatsD output was deprecated in k6 v0.47.0 and is now removed. You could still output results to StatsD using the community xk6 extension LeonAdato/xk6-output-statsd. Thanks, @​LeonAdato for taking over the extension!

open will have a breaking change in the future.

Currently, open opens relative files based on an unusual root, similar to how require behaved before it was updated for ESM compatibility. To make k6 more consistent, open and other functions like it will start handling relative paths in the same way as imports and require.
For a more in-depth explanation, please refer to the related issue.

With this version, k6 will start emitting warnings when it detects that in the future, this will break. We recommend using import.meta.resolve() as a way to make your scripts future proof.

http.file#data now truly has the same type as the provided data #4009

Previously http.file#data was always a slice of byte ([]byte) - which was very likely a bug and a leftover from years past.

The original aim (also documented) was to have the same type as the data provided when creating the http.file object, and it is now effectively the case.

New features

Top-level await support 4007

After the initial native support for ECMAScript modules, k6 can now load those modules asynchronously which also allows await to be used in the top-level of a module. That is you can write await someFunc() directly in the top most level of a module instead of having to make an async function that you call that can than use await.

Until now, you had to wrap your code in an async function to use await in the top-level of a module. For example, the following code:

import { open } from 'k6/experimental/fs'
import csv from 'k6/experimental/csv'

let file;
let parser;
(async function () {
	file = await open('data.csv');
	parser = new csv.Parser(file);
})();

Can now be written as:

import { open } from 'k6/experimental/fs'
import csv from 'k6/experimental/csv'

const file = await open('data.csv');
const parser = new csv.Parser(file);

This should make using the increasing number of async APIs in k6 easier in the init context.

This is not allowed in case of using the CommonJS modules, only ECMAScript modules, as CommonJS modules are synchronous by definition.

Complete[^1] RSA support for k6/experimental/webcrypto #4025

This update includes support for the RSA family of algorithms, including RSA-OAEP, RSA-PSS and RSASSA-PKCS1-v1_5. You can use these algorithms with the crypto.subtle API in the same way as the other algorithms, precisely for generateKey, importKey, exportKey, encrypt, decrypt, sign, and verify operations.

By implementing RSA support, we make our WebCrypto API implementation more complete and useful for a broader range of use cases.

[^1]: Since under the hood we do fully rely on the Golang's SDK, our implementation doesn't support zero salt lengths for the RSA-PSS sign/verify operations.

Example usage
Expand to see an example of generation RSA-PSS key pair.
import { crypto } from "k6/experimental/webcrypto";

export default async function () {
  const keyPair = await crypto.subtle.generateKey(
    {
      name: "RSA-PSS",
      modulusLength: 2048,
      publicExponent: new Uint8Array([1, 0, 1]),
      hash: { name: "SHA-1" },
    },
    true,
    ["sign", "verify"]
  );

  console.log(JSON.stringify(keyPair));
}
page.on('metric) to group urls browser#371, browser#1487

Modern websites are complex and make a high number of requests to function as intended by their developers. These requests no longer serve only content for display to the end user but also retrieve insights, analytics, advertisements, and for cache-busting purposes. Such requests are usually generated dynamically and may contain frequently changing IDs, posing challenges when correlating and analyzing your k6 test results.

When load testing a website using the k6 browser module, these dynamic requests can result in a high number of similar-looking requests, making it difficult to correlate them and extract valuable insights. This can also lead to test errors, such as a "too-many-metrics" error, due to high cardinality from metrics tagged with similar but dynamically changing URLs.

This issue also affects synthetic tests. While you may not encounter the "too-many-metrics" error, you may end up with a large amount of uncorrelated metric data that cannot be tracked effectively over time.

To address this in the browser module, we have implemented page.on('metric'), which allows you to define URL patterns using regex for matching. When a match is found, the URL and name tags for the metric are replaced with the new name.

Example usage
Expand to see an example of working with `page.on('metric')`.
import { browser } from 'k6/browser';

export const options = {
  scenarios: {
    ui: {
      executor: 'shared-iterations',
      options: {
        browser: {
            type: 'chromium',
        },
      },
    },
  },
}

export default async function() {
  const page = await browser.newPage();

  // Here, we set up an event listener using page.on('metric').
  // You can call page.on('metric') multiple times, and each callback function
  // will be executed in the order that page.on was called.
  page.on('metric', (metric) => {
    // Currently, metric.tag is the only available method on the metric object.
    // It enables matching on the URL tag using a specified regex pattern.
    // You can call metric.tag multiple times within the callback function.
    metric.tag({
      // This is the new name assigned to any metric that matches the defined
      // URL pattern below.
      name: 'test',
      // Provide one or more match patterns here. Any metrics that match a pattern
      // will use the new name specified above.
      matches: [
        // Each match pattern can include a URL and an optional method.
        // When a method is specified, the metric must match both the URL pattern
        // and the method. If no method is provided, the pattern will match all
        // HTTP methods.
        {url: /^https:\/\/test\.k6\.io\/\?q=[0-9a-z]+$/, method: 'GET'},
      ]
    });
  });

  try {
    // The following lines are for demonstration purposes.
    // Visiting URLs with different query parameters (q) to illustrate matching.
    await page.goto('https://test.k6.io/?q=abc123');
    await page.goto('https://test.k6.io/?q=def456');
  } finally {
    // Ensure the page is closed after testing.
    await page.close();
  }
}
ControlOrMeta support in the keyboard browser#1457

This approach enables tests to be written for all platforms, accommodating either Control or Meta for keyboard actions. For example, Control+click on Windows and Meta+clickon Mac to open a link in a new window.

Example usage
Expand to see an example usage of `ControlOrMeta`
  await page.keyboard.down('ControlOrMeta');

  // Open the link in a new tab.
  // Wait for the new page to be created.
  const browserContext = browser.context();
  const [newTab] = await Promise.all([
    browserContext.waitForEvent('page'),
    await page.locator('a[href="/my_messages.php"]').click()
  ]);

  await page.keyboard.up('ControlOrMeta');

UX improvements and enhancements

  • browser#1462 Enhances waitForSelector error message to better reflect why a selector doesn't resolve to an element.
  • #​4028 Adds support of SigV4 signing for the experimental-prometheus-rw output. This allows users to authenticate with AWS services that require SigV4 signing. Thanks, @​obanby for the contribution!
  • #​4026 Allows setting of service.name from the OTEL_SERVICE_NAME environment variable for the experimental-opentelemetry output. This aligns better with standard OTEL practices. Thanks, @​TimotejKovacka for the contribution!
  • browser#1426 Instruments page.waitForTimeout with tracing which will allow it to be displayed in the timeline.

Bug fixes

  • browser#1452 Fixes a possible deadlock when working with page.on.
  • browser#1469 Fixes locator.waitFor so it waits between navigations and doesn't throw an error.
  • browser#1488, browser#1493 Fixes memory leaks.
  • #​4017 Fixes a bug where k6 would not stop when a test was aborted right after an await statement.

Maintenance and internal improvements

v0.54.0

Compare Source

k6 v0.54.0 is here 🎉! This release includes:

  • A new experimental CSV module
  • New k6 cloud commands for local execution and uploading script files
  • New ECMAScript features
  • Updated logo and branding

Breaking changes

  • #​3913 changes the mapping of Golang's math/big.Int type to bigint type in k6.
  • #​3922 removes lib.Min and lib.Max from k6's Go API, which could affect custom extensions that rely on these functions.
  • #​3838 removes k6/experimental/timers - they are now available globally and no import is needed.
  • #​3944 updates to k6/experimental/websockets, which makes the binaryType default value equal to "blob". With this change, k6/experimental/websockets is now compliant with the specification.

New features

Branding changes and logo #3946, #3953, #3969

As part of joining Grafana Labs in 2021, k6 was renamed to Grafana k6. The original k6 logo and branding was purple, which didn't fit very well next to the Grafana Labs orange logo and all its other products.

In this release, we have a new logo in a new color, and the terminal banner has been redesigned to match the current branding more closely.

Grafana k6 logo

New experimental CSV module for efficient CSV data handling #3743

We’ve added a new experimental CSV module to k6 for more efficient and convenient CSV parsing and streaming, addressing the limitations of preexisting JavaScript-based solutions like papaparse.

What is it?

The CSV module offers two key features:

  • csv.parse(): This function parses a CSV file into a SharedArray at once using Go-based processing for faster parsing and lower memory usage compared to JavaScript alternatives.
  • csv.Parser: This class provides a streaming parser to read CSV files line-by-line, minimizing memory consumption and offering more control over parsing through a stream-like API. This is ideal for scenarios where memory optimization or fine-grained control of the parsing process is crucial.
Benefits for users
  • Faster Parsing: csv.parse bypasses most of the JavaScript runtime, offering significant speed improvements for large files.
  • Lower Memory Usage: Both solutions support shared memory across virtual users (VUs) with the fs.open function.
  • Flexibility: Choose between full-file parsing with csv.parse() or memory-efficient streaming with csv.Parser.
Tradeoffs
  • csv.Parse: Parses the entire file in the initialization phase of the test, which can increase startup time and memory usage for large files. Best suited for scenarios where performance is prioritized over memory consumption.
  • csv.Parser: Reads the file line-by-line, making it more memory-efficient but potentially slower due to reading overhead for each line. Ideal for scenarios where memory usage is a concern or where fine-grained control over parsing is needed.
Example usage
Expand to see an example of Parsing a full CSV file into a SharedArray.
import { open } from 'k6/experimental/fs'
import csv from 'k6/experimental/csv'
import { scenario } from 'k6/execution'

export const options = {
    iterations: 10,
}

let file;
let csvRecords;
(async function () {
    file = await open('data.csv');

    // The `csv.parse` function consumes the entire file at once, and returns
    // the parsed records as a SharedArray object.
    csvRecords = await csv.parse(file, {delimiter: ','})
})();

export default async function() {
    // The csvRecords a SharedArray. Each element is a record from the CSV file, represented as an array
    // where each element is a field from the CSV record.
    //
    // Thus, `csvRecords[scenario.iterationInTest]` will give us the record for the current iteration.
    console.log(csvRecords[scenario.iterationInTest])
}
Expand to see an example of streaming a CSV file line-by-line.
import { open } from 'k6/experimental/fs'
import csv from 'k6/experimental/csv'

export const options = {
    iterations: 10,
}

let file;
let parser;
(async function () {
    file = await open('data.csv');
    parser = new csv.Parser(file);
})();

export default async function() {
    // The parser `next` method attempts to read the next row from the CSV file.
    //
    // It returns an iterator-like object with a `done` property that indicates whether
    // there are more rows to read, and a `value` property that contains the row fields
    // as an array.
    const {done, value} = await parser.next();
    if (done) {
        throw new Error("No more rows to read");
    }

    // We expect the `value` property to be an array of strings, where each string is a field
    // from the CSV record.
    console.log(done, value);
}
New k6 cloud run --local-execution flag for local execution of cloud tests #3904, and #​3931

This release introduces the --local-execution flag for the k6 cloud run command, allowing you to run test executions locally while sending metrics to Grafana Cloud k6.

k6 cloud run --local-execution script.js

By default, using the --local-execution flag uploads the test archive to Grafana Cloud k6. If you want to disable this upload, use the --no-archive-upload flag.

The --local-execution flag currently functions similarly to the k6 run -o cloud command, which is now considered deprecated (though it is not planned to be removed). Future updates will enhance --local-execution with additional capabilities that the k6 run -o cloud command does not offer.

New k6 cloud upload command for uploading test files to the cloud #3906

We continue to refine and improve the cloud service to improve how we handle uploading test files, so we've added a new k6 cloud upload command that replaces the k6 cloud --upload-only flag, which is now considered deprecated.

gRPC module updates driven by contributors
New discardResponseMessage option

#​3877 and #​3820 add a new option for the gRPC module discardResponseMessage, which allows users to discard the messages received from the server.

const resp = client.invoke('main.RouteGuide/GetFeature', req, {discardResponseMessage: true});

This reduces the amount of memory required and the amount of garbage collection, which reduces the load on the testing machine and can help produce more reliable test results.

Thank you, @​lzakharov!

New argument meta for gRPC's stream callbacks

#​3801 adds a new argument meta to gRPC's stream callback, which handles the timestamp of the original event (for example, when a message has been received).

let stream = new grpc.Stream(client, "main.FeatureExplorer/ListFeatures")
stream.on('data', function (data, meta) {
    // will print the timestamp when message has been received
    call(meta.ts);
});

Thank you, @​cchamplin!

Allow missing file descriptors for gRPC reflection

#​3871 allows missing file descriptors for gRPC reflection.

Thank you, @​Lordnibbler!

Sobek updates brings support of new ECMAScript features into k6 #3899, #3925, #3913

With this release, we've updated Sobek (the ECMAScript implementation in Go) which contains the new ECMAScript features that are now available in k6.

This includes support for numeric literal separators:

const billion = 1_000_000_000

Support for BigInt, the values which are too large to be represented by the number primitive:

const huge = BigInt(9007199254740991);

Note: Before k6 version v0.54, Golang's type math/big.Int mapped to another type, so this might be a breaking change for some extensions or users.

RegExp dotAll support, where you can match newline characters with .:

const str1 = "bar\nexample foo example";

const regex1 = /bar.example/s;

console.log(regex1.dotAll); // true

Support for ES2023 Array methods: with, toSpliced, toReversed and toSorted.

Thank you @​shiroyk for adding both the new array methods and BitInt 🙇.

New setChecked method for the browser module browser#1403

Previously, users could check or uncheck checkbox and radio button elements using the check and uncheck methods. Now, we've added a setChecked method that allows users to set a checkbox or radio button to either the checked or unchecked state with a single method and a boolean argument.

await page.setChecked('#checkbox', true);   // check the checkbox
await page.setChecked('#checkbox', false);  // uncheck the checkbox

Page, Frame, ElementHandle, and Locator now support the new setChecked method.

Async check function utility k6-utils#13

Writing concise code can be difficult when using the k6 check function with async code since it doesn't support async APIs. A solution that we have suggested so far is to declare a temporary variable, wait for the value that is to be checked, and then check the result later. However, this approach can clutter the code with single-use declarations and unnecessary variable names, for example:

const checked = await p.locator('.checked').isChecked();

check(checked, {
    'checked': c => c,
});

To address this limitation, we've added a version of the check function to jslib.k6.io that makes working with async/await simpler. The check function is a drop-in replacement for the built-in check, with added support for async code. Any Promises will be awaited, and the result is reported once the operation has been completed:

// Import the new check function from jslib.k6.io/k6-utils
import { check } from 'https://jslib.k6.io/k6-utils/1.5.0/index.js';

// ...

// Use the new check function with async code
check(page, {
    'checked': async p => p.locator('.checked').isChecked(),
});

Check out the check utility function's documentation for more information on how to use it.

k6/experimnetal/websockets updates towards WebSockets API compatibility
Support ArrayBufferViews in send for k6/experimental/websockets #​3944

As part of making k6/experimental/websockets compliant with the WebSocket API, it now supports Uint8Array and other ArrayBufferViews directly as arguments to send, instead of having to specifically provide their buffer.

This should make the module more compliant with libraries that use the WebSocket API.

Thanks to @​pixeldrew for reporting this. 🙇

readyState actually being a number #​3972

Due to goja/Sobek internal workings, readyState wasn't exactly a number in JavaScript code. This had some abnormal behavior, which limited interoperability with libraries.

This has been fixed, and readyState is a regular number from the JavaScript perspective.

Thanks to @​dougw-bc for reporting this. 🙇

Rework of how usage is being collected internally and additional counters #3917 and #3951

As part of working on k6 over the years, we have often wondered if users use certain features or if they see a strange corner case behavior.

We have usually made guesses or tried to extrapolate from experience on issues we see. Unfortunately, this isn't always easy or possible, and it's definitely very skewed. As such, we usually also add warning messages to things we intend to break to inform people and ask them to report problems. But we usually see very little activity, especially before we make changes.

This also only works for things we want to remove, and it doesn't help us know if people use new functionality at all or if there are patterns we don't expect.

While k6 has been collecting usage for a while, they're surface-level things, such as how many VUs were run, the k6 version, or which internal modules were loaded. The system for this was also very rigid, requiring a lot of work to add simple things, such as if someone used the require function.

This process has been reworked to make things easier, and a few new usage reports have been added:

  • When Grafana Cloud is used, it will also tell us which test run it is. We already have most of the information reported through metrics and from the test being executed in the cloud. But this will help us filter cloud test runs from local runs and potentially warn cloud users if they're using experimental modules that will be removed.
  • The number of files parsed, as well as the number of .ts files parsed. This will help us understand if people use small or big projects and if TypeScript support is being used.
  • Usage of require. Now that we have ESM native support, using require and CommonJS adds complexity. It's interesting to us whether removing this in the future - likely years, given its support in other runtimes, is feasible.
  • Usage of global. This will help us decide if we can drop compatibility-mode differences or even the whole concept.

UX improvements and enhancements

  • #​3898 adds SetupTimeout option validation. Thank you, @​tsukasaI!
  • #​3930 adds token validation for k6 cloud login, so now you get immediate feedback right after logging in.
  • #​3876, #​3923 adjusts the process' exit code for cloud test runs where thresholds have failed.
  • #​3765 stops using the confusing and for Rate metrics, and instead uses the form: {x} out of {y}.

Bug fixes

  • #​3947 fixes panic when options is nil (e.g. exported from a module where it isn't really exported).
  • #​3901 fixes the cloud command not being display in the k6 command's help text.
  • browser#1406 fixes panic on iframe attach when iframe didn't contain any UI elements.
  • browser#1420 uses the VU context to control the iterations lifecycle which helps k6 shutdown in a timely manner when a test is aborted.
  • browser#1421 fixes the navigation span by starting when the page starts to load so that it's a better representation of how long the test was on a page.
  • browser#1408, browser#1422 fixes the page.reload API so handles null responses without exceptions.
  • browser#1435 fixes an NPD when performing a click action.
  • browser#1438 fixes a goroutine that waits indefinitely when writing to a channel.
  • #​3968 fixes an issue with the event system where it was dropping events which led to it hanging indefinitely.
  • browser#1442 fixes browser.close to abort the cdp close request when the browser process has already exited.

Maintenance and internal improvements

Roadmap

In version 0.52.0, the browser module transitioned from experimental to stable. The new module is more stable and has a full Async API. To ensure your scripts continue working, you must migrate to the new k6/browser module and discontinue using the previous k6/experimental/browser module. Please see the migration guide for more details.

v0.53.0

Compare Source

k6 v0.53.0 is here 🎉! This release includes:

  • Native ECMAScript modules support
  • New experimental OpenTelemetry metrics output
  • Blob support in experimental websockets module
  • Consolidate cloud features and commands under k6 cloud
  • Breaking change: remove magic URL resolutions

Breaking changes

Require is now specification compliant and always resolves based on the file it is written in #​3534

The require function in k6 used to resolve identifiers based on the current "root of execution" (more on that later). In a lot of cases, that aligns with the file the require is written in or a file in the same folder, which leads to the same result. In a small subset of cases, this isn't the case.

In every other implementation, and more or less by the CommonJS specification, require should always be relative to the file it is written in.

This also aligns with how ESM and dynamic import also work. In order to align with them require now uses the same underlying implementation.

There was a warning message for the last 2 releases trying to tease out cases where that would be problematic.

"root of execution" explanation

This is very much an implementation detail that has leaked and likely a not intended one.

Whenever a file is require-ed it becomes the "root of execution", and both require and open become relative to it. Once the require finishes, the previous "root of execution" gets restored. Outside of the init context execution, the main file is the "root of execution".

Example:

Have 3 files:
main.js

const s = require("./A/a.js")
if (s() != 5) {
	throw "Bad"
}
module.exports.default = () =>{} // just for k6 to not error

/A/a.js:

module.exports = function () {
  return require("./b.js");
}

/A/b.js

module.exports = 5

In this example when require is called in /A/a.js the main.js is once again the "root of execution". If you call the function in /A/a.js just after defining it though, it will work as expected.

You can use the newly added import.meta.resolve() function if you want to create a path that is relevant to the currently calling module. That will let you call it outside of a helper class and provide the path to it. Refer to docs for more details.

ECMAScript Modules (ESM) Native Support related breaking changes

As part of the ESM native support implementation, two common broken patterns in the ecosystem became apparent.

One is arguably a developer experience improvement, and the other is a consequence of the previous implementation.

Mixing CommonJS and ESM

Previously, k6 used a transpiler (Babel) internally to transpile ESM syntax to CommonJS. That led to all code always being CommonJS, and if you had CommonJS next to it, Babel would not complain.

As k6 (or the underlying JS VM implementation) did not understand ESM in itself and that CommonJS is a 100% during execution feature, this was not easy to detect or prevent.

We added a warning in v0.52.0 to give users time for migration.

To fix this - all you need is to stick to either CommonJS or ESM within each file.

Code examples and proposed changes
import { sleep } from "k6";

module.exports.default = func() { ...}

In the example above both ESM and CommonJS are used in the same file.

You can either replace:

module.exports.default = func() {}

With the ESM syntax:

export default func() {}

Or replace:

import { sleep } from "k6";

With CommonJS:

const sleep = require("k6").sleep;
Imported identifier that can't be resolved are now errors

Previous to this, if you were using the ESM syntax and imported the foo identifier, but the exporting file didn't export it, there wouldn't be an error.

bar.js:

export const notfoo = 5;

main.js

import { foo } from "./bar.js"
export default function () {
    foo.bar(); // throws exception here
}

The example would not error out, but when it is accessed, there would be an exception as foo would be undefined.

With native ESM support, that is an error as defined by the specification and will occur sooner.

This arguably improves UX/DX, but we have reports that some users have imports like this but do not use them. So, they wouldn't be getting exceptions, but they would now get errors.

The solution, in this case, is to stop importing the not exported identifiers.

No more "magic" URL resolution

For a long time, k6 has supported special magic URLs that aren't really that.

Those were URLs without a scheme that:

  1. Started with github.com, and if pasted to a browser won't open to a file. Their appeal was that you can more easily write them by hand if you know the path within a GitHub repo.
  2. Started with cdnjs.com, and if pasted to a browser will open a web page with all the versions of the library. The appeal here is that you will get the latest version.

Both of them had problems though.

The GitHub ones seemed to have never been used by users, likely because you need to guess what the path should look like, and you can always just go get a real URL to the raw file.

While the cdnjs ones have some more usage, they are both a lot more complicated to support, as they require multiple requests to figure out what needs to be loaded. They also change over time. In addition the only known use at the moment is based on a very old example from an issue and it is even pointing to concrete, old version, of a library.

Given that this can be done with a normal URL, we have decided to drop support for this and have warned users for the last couple of versions.

Deprecated k6/experimental/tracing in favor of a JavaScript implementation

k6/experimental/tracing is arguably not very well named, and there is a good chance we would like to use the name for actual trace and span support within k6 in the future.

On top of that it can now be fully supported in js code, which is why http-instrumentation-tempo
was created.

The JavaScript implementation is a drop-in replacement, so all you need to do is replace k6/experimental/tracing with https://jslib.k6.io/http-instrumentation-tempo/1.0.0/index.js.

The module is planned to be removed in v0.55.0, planned for November 11th, 2024.

Experimental websockets now require binaryType to be set to receive binary messages

As part of the stabilization of the k6/experimental/websockets we need to move the default value of binaryType to blob. It was previously arraybuffer and since the last version there was a warning that it needs to be set in order for binary messages to be received.

That warning is now an error.

In the future we will move the default value to blob and remove the error.

New features

The new features include:

  • Native ESM support, which also brings some quality of life JavaScript features
  • Blob support in the experimental websockets module
  • Experimental OpenTelemetry metrics output
  • Consolidating cloud related commands and features under k6 cloud
Native ESM support #​3456

With this feature k6 is now ES6+ compliant natively. Which means (asterisk free) support for the spread operator with object, private class fields and optional chaining

But also faster startup times, more consistent errors and easier addition of features as we now only need to add them to Sobek instead of also them being supported in the internal Babel.

History of compatibility mode and ECMAScript specification compliance

Some history: More than 6 years ago k6 started using core-js and babel to get ES6+ features. core-js is a implementation of a lot of the types and their features such as String.prototype.matchAll among other things, and Babel gets one piece of code that uses some syntax and returns a piece of code doing the same thing (mostly) but with different syntax. Usually with the idea of supporting newer syntax but returning code that can run on runtimes which only support old syntax.

This is great, but it means that:

  1. For core-js each VU needs to run a bunch of JS code each initialization so it can polyfill everything that is missing
  2. Babel needs to be parsed and loaded and then given files to transpile on each start.

Both of those aren't that big problems usually, but the runtime k6 uses is fairly fast, but isn't V8. What it lacks in speed it gets back in being easy to interact with from Go, the language k6 is written in.

But it means that now on each start it needs to do a bunch of work that adds up.

So long time ago for people who would want to not have to do this we added compatibility-mode=base. This allowed you to potentially not use this features and get a big speedup. Or use them outside of k6 and likely still get significant speed up if you cut down on it.

At the same time the author and maintainer of the JS runtime we used (goja) did implement a big portion of what we were missing from core-js and also Babel. After some experiments to cut down the core-js we import we ended up contributing back the remaining parts and dropping the whole library. Which lead to 5 times reduction of memory per VU for simple scripts. And even for fairly complicated ones.

With this in mind we did try to cut down Babel as well and contribute back the simpler things it was used for. This over the years lead to small pieces of what Babel did being moved to goja and then disabled in Babel. Some of those were just easy wins, some of those were things that had very bad pathological cases where using a particular syntax made transpilation times explode.

In all of that work there always were small (or not so small) breaking changes due to many factors - sometimes our new implementation was slightly wrong and we needed to fix, sometimes more than what was in the standard was enabled in core-js or Babel, sometimes the standard changed on those. And sometimes the implementation in Babel or core-js wasn't as full and didn't account for all corner cases.

ECMAScript Modules(ESM) is the last such feature that Babel was used for. It also happens to be likely the one most people used, due to the fact that it is the standard way to reuse code and import libraries.

While the work on this feature started over 2 years ago, it both depended on other features that weren't there yet, but also interacts with more or less every other feature that is part of the ECMAScript standard.

Along the way there were many internal refactors as well as additional tests to make certain we can be as backwards compatible as possible. But there also ended up being things that just weren't going to be compatible, like the listed breaking changes.

After ESM now being natively supported, compatibility-mode base vs extended has only 1 feature difference - aliasing global to globalThis to make it a bit more compatible with (old) Node.js. There is ongoing discussion if that as well should be removed.

For the purposes of having less intrusive changes and shipping this earlier a few things have not been implemented in k6. That includes top-level-await and dynamic import support. Both of them are likely to land in the next version.

import.meta.resolve() gets an URL from a relative path the same way import or require does #​3873

As part of the move to ESM a lot of cases where k6 currently do not resolve the same relative path to the same file were found. Some of those were fixed - as those in require, but others haven't.

It also became apparent some users do use the relativity of require, but also open. As we move to make this consistent among uses, we decided to let users have a better transition path forward.

Using import.meta.resolve will give you just a new URL that can be used in all functions and it will give you the same result.

import.meta.resolve uses the same algorithm and relativity as ESM import syntax. Refer to docs for more details.

Blob support in the experimental websockets module grafana/xk6-websockets#74

In order to support the default WebSocket.binaryType type as per spec ("blob"), we have added support for the Blob interface as part of the features included in the xk6-websockets module.

So, from now on it can be used with import { Blob } from "k6/experimental/websockets";. In the future, apart from graduating this module to stable, we might also want to expose the Blob interface globally (no imports will be required). But for now, please remind that its support is still experimental, as the entire module is. Refer to the docs for more details.

Experimental OpenTelemetry Output #​3834

This release introduces a new experimental output for OpenTelemetry. This allows users to send k6 metrics to any OpenTelemetry-compatible backends. More details and usage examples can be found in the documentation.

To output metrics to OpenTelemetry, use the experimental-opentelemetry output option:

 k6 run -o experimental-opentelemetry examples/script.js

If you have any feedback or issues, please let us know directly in the extension repository.

Consolidating cloud features under k6 cloud #​3813

This release introduces the first iteration of the revamped cloud-related commands under the k6 cloud command, featuring two new subcommands:

  • k6 cloud login: replaces k6 login cloud for authenticating with the cloud service. It supports token-based authentication only. The previous authentication method using email and password will still be available through the legacy k6 login cloud command, which is now deprecated and will be removed in a future release (no removal date set yet).

  • k6 cloud run: is the new official way to run k6 on the cloud service, serving as an alternative to the existing k6 cloud command. The k6 cloud command will remain available for a few more versions but will eventually function only as a wrapper for all cloud-related commands, without any direct functionality.

UX improvements and enhancements

Bug fixes

Maintenance and internal improvements

Roadmap

Future breaking changes
Experimental browser module removal

In the previous release, the browser module graduated from experimental to stable. The k6/experimental/browser module will be removed in v0.54.0. To keep your scripts working you need to migrate to the k6/browser module.

Experimental timers module removal

The experimental timers module has been deprecated for a few versions. It both has a stable import path k6/timers, but also all of it's current exports are available globally.

In the next version v0.54.0 the experimental timers module will be removed.

Experimental tracing module removal

The experimental tracing module is deprecated in this version. In two versions(v0.55.0) the experimental module will be removed.

To keep your scripts working you need to migrate to http-instrumentation-tempo jslib.

StatsD removal

In this release, we also fixed the version where we will remove the StatsD output. The StatsD output is going to be removed in the v0.55.0 release. If you are using the StatsD output, please consider migrating to the extension LeonAdato/xk6-output-statsd.

Potentially dropping global from extended compatibility-mode

Currently global is aliased to globalThis when extended compatibility-mode is used. This is currently the only difference with the base compatibility-mode.

Given that this seems to have very low usage it might be dropped in the future. See the issue for more info or if you want to comment on this.


Configuration

📅 Schedule: Branch creation - "after 10pm every weekday,before 5am every weekday,every weekend" in timezone Asia/Tokyo, Automerge - At any time (no schedule defined).

🚦 Automerge: Enabled.

Rebasing: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about this update again.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

@renovate renovate bot added the renovate label Oct 17, 2024
Copy link
Contributor Author

renovate bot commented Oct 17, 2024

ℹ Artifact update notice

File name: go.mod

In order to perform the update(s) described in the table above, Renovate ran the go get command, which resulted in the following additional change(s):

  • 19 additional dependencies were updated

Details:

Package Change
github.com/cenkalti/backoff/v4 v4.2.1 -> v4.3.0
github.com/cespare/xxhash/v2 v2.2.0 -> v2.3.0
github.com/fatih/color v1.17.0 -> v1.18.0
github.com/go-logr/logr v1.4.1 -> v1.4.2
github.com/grafana/sobek v0.0.0-20240613124309-cb36746e8fee -> v0.0.0-20241024150027-d91f02b05e9b
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 -> v2.22.0
go.opentelemetry.io/otel v1.24.0 -> v1.29.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0 -> v1.29.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0 -> v1.29.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0 -> v1.29.0
go.opentelemetry.io/otel/metric v1.24.0 -> v1.29.0
go.opentelemetry.io/otel/sdk v1.24.0 -> v1.29.0
go.opentelemetry.io/otel/trace v1.24.0 -> v1.29.0
go.opentelemetry.io/proto/otlp v1.1.0 -> v1.3.1
golang.org/x/time v0.5.0 -> v0.7.0
google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de -> v0.0.0-20240822170219-fc7c04adadcd
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de -> v0.0.0-20240822170219-fc7c04adadcd
google.golang.org/grpc v1.63.2 -> v1.67.1
google.golang.org/protobuf v1.33.0 -> v1.35.1

@renovate renovate bot changed the title fix(deps): update module go.k6.io/k6 to v0.54.0 fix(deps): update module go.k6.io/k6 to v0.55.0 Nov 11, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

0 participants