Skip to content

Commit 78a4d62

Browse files
authored
Merge pull request #1419 from grafana/experimental/fs-module
2 parents 70098ed + 7ab50c9 commit 78a4d62

File tree

10 files changed

+571
-68
lines changed

10 files changed

+571
-68
lines changed

docs/sources/next/javascript-api/_index.md

+63-63
Large diffs are not rendered by default.

docs/sources/next/javascript-api/k6-experimental/_index.md

+6-5
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
11
---
2-
title: "k6/experimental"
3-
excerpt: "k6 experimental APIs"
2+
title: 'k6/experimental'
3+
description: 'k6 experimental APIs'
44
weight: 07
55
---
66

77
# k6/experimental
88

99
{{< docs/shared source="k6" lookup="experimental-module.md" version="<K6_VERSION>" >}}
1010

11-
| Modules | Description |
12-
| --------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
11+
| Modules | Description |
12+
| ------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------ |
1313
| [browser](https://grafana.com/docs/k6/<K6_VERSION>/javascript-api/k6-experimental/browser) | Provides browser-level APIs to interact with browsers and collect frontend performance metrics as part of your k6 tests. |
14+
| [fs](https://grafana.com/docs/k6/<K6_VERSION>/javascript-api/k6-experimental/fs) | Provides a memory-efficient way to handle file interactions within your test scripts. |
15+
| [grpc](https://grafana.com/docs/k6/<K6_VERSION>/javascript-api/k6-experimental/grpc) | Extends `k6/net/grpc` with the streaming capabilities. |
1416
| [redis](https://grafana.com/docs/k6/<K6_VERSION>/javascript-api/k6-experimental/redis) | Functionality to interact with [Redis](https://redis.io/). |
1517
| [timers](https://grafana.com/docs/k6/<K6_VERSION>/javascript-api/k6-experimental/timers) | `setTimeout`, `clearTimeout`, `setInterval`, `clearInterval` |
1618
| [tracing](https://grafana.com/docs/k6/<K6_VERSION>/javascript-api/k6-experimental/tracing) | Support for instrumenting HTTP requests with tracing information. |
1719
| [webcrypto](https://grafana.com/docs/k6/<K6_VERSION>/javascript-api/k6-experimental/webcrypto) | Implements the [WebCrypto API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API). |
1820
| [websockets](https://grafana.com/docs/k6/<K6_VERSION>/javascript-api/k6-experimental/websockets) | Implements the browser's [WebSocket API](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket). |
19-
| [grpc](https://grafana.com/docs/k6/<K6_VERSION>/javascript-api/k6-experimental/grpc) | Extends `k6/net/grpc` with the streaming capabilities. |
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
---
2+
title: 'FileInfo'
3+
description: 'FileInfo represents information about a file.'
4+
weight: 30
5+
---
6+
7+
# FileInfo
8+
9+
The `FileInfo` class represents information about a [file](https://grafana.com/docs/k6/<K6_VERSION>/javascript-api/k6-experimental/fs/file).
10+
11+
## Properties
12+
13+
| Property | Type | Description |
14+
| :------- | :----- | :----------------------------- |
15+
| name | string | The name of the file. |
16+
| size | number | The size of the file in bytes. |
17+
18+
## Example
19+
20+
{{< code >}}
21+
22+
```javascript
23+
import { open, SeekMode } from 'k6/experimental/fs';
24+
25+
let file;
26+
(async function () {
27+
file = await open('bonjour.txt');
28+
})();
29+
30+
export default async function () {
31+
// Retrieve information about the file
32+
const fileinfo = await file.stat();
33+
if (fileinfo.name != 'bonjour.txt') {
34+
throw new Error('Unexpected file name');
35+
}
36+
}
37+
```
38+
39+
{{< /code >}}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
---
2+
title: 'SeekMode'
3+
description: 'SeekMode is used to specify the position from which to seek in a file.'
4+
weight: 40
5+
---
6+
7+
# SeekMode
8+
9+
The `SeekMode` enum specifies the position from which to [seek](https://grafana.com/docs/k6/<K6_VERSION>/javascript-api/k6-experimental/fs/file/seek) in a file.
10+
11+
## Members
12+
13+
| Member | Value | Description |
14+
| :------ | :---- | :------------------------------------------ |
15+
| Start | 0 | Seek from the start of the file. |
16+
| Current | 1 | Seek from the current position in the file. |
17+
| End | 2 | Seek from the end of the file. |
18+
19+
## Example
20+
21+
{{< code >}}
22+
23+
```javascript
24+
import { open, SeekMode } from 'k6/experimental/fs';
25+
26+
let file;
27+
(async function () {
28+
file = await open('bonjour.txt');
29+
})();
30+
31+
export default async function () {
32+
// Seek 6 bytes from the start of the file
33+
await file.seek(6, SeekMode.Start);
34+
35+
// Seek 2 more bytes from the current position
36+
await file.seek(2, SeekMode.Current);
37+
38+
// Seek backwards 2 bytes from the end of the file
39+
await file.seek(-2, SeekMode.End);
40+
}
41+
```
42+
43+
{{< /code >}}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
---
2+
title: 'fs'
3+
description: 'k6 fs experimental API'
4+
weight: 10
5+
---
6+
7+
# fs
8+
9+
{{< docs/shared source="k6" lookup="experimental-module.md" version="<K6_VERSION>" >}}
10+
11+
The k6 filesystem experimental module provides a memory-efficient way to handle file interactions within your test scripts. It currently offers support for opening files, reading their content, seeking through their content, and retrieving metadata about them.
12+
13+
### Memory efficiency
14+
15+
One of the key advantages of the filesystem module is its memory efficiency. Unlike the traditional [open](https://grafana.com/docs/k6/latest/javascript-api/init-context/open/) function, which loads a file multiple times into memory, the filesystem module reduces memory usage by loading the file as little as possible and sharing the same memory space between all VUs. This approach reduces the risk of encountering out-of-memory errors, especially in load tests involving large files.
16+
17+
### Notes on usage
18+
19+
An important consideration when using the filesystem module is its handling of external file modifications. Once k6 loads a file, it behaves like a "view" over its contents. If you modify the underlying file during a test, k6 will not reflect those changes in the loaded [File](https://grafana.com/docs/k6/<K6_VERSION>/javascript-api/k6-experimental/fs/file/) instance.
20+
21+
## API Overview
22+
23+
The module exports functions and objects to interact with the file system:
24+
25+
| Function/Object | Description |
26+
| ----------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
27+
| [open](https://grafana.com/docs/k6/<K6_VERSION>/javascript-api/k6-experimental/fs/open) | Opens a file and returns a promise resolving to a `File` instance. |
28+
| [File](https://grafana.com/docs/k6/<K6_VERSION>/javascript-api/k6-experimental/fs/file) | Represents a file with methods for reading, seeking, and obtaining file stats. |
29+
| [SeekMode](https://grafana.com/docs/k6/<K6_VERSION>/javascript-api/k6-experimental/fs/seekmode) | Enum for specifying the reference point for seek operations. Includes `Start`, `Current`, and `End`. |
30+
31+
## Example
32+
33+
{{< code >}}
34+
35+
```javascript
36+
import { open, SeekMode } from 'k6/experimental/fs';
37+
38+
// k6 doesn't support async in the init context. We use a top-level async function for `await`.
39+
//
40+
// Each Virtual User gets its own `file` copy.
41+
// So, operations like `seek` or `read` won't impact other VUs.
42+
let file;
43+
(async function () {
44+
file = await open('bonjour.txt');
45+
})();
46+
47+
export default async function () {
48+
// About information about the file
49+
const fileinfo = await file.stat();
50+
if (fileinfo.name != 'bonjour.txt') {
51+
throw new Error('Unexpected file name');
52+
}
53+
54+
const buffer = new Uint8Array(4);
55+
56+
let totalBytesRead = 0;
57+
while (true) {
58+
// Read into the buffer
59+
const bytesRead = await file.read(buffer);
60+
if (bytesRead == null) {
61+
// EOF
62+
break;
63+
}
64+
65+
// Do something useful with the content of the buffer
66+
totalBytesRead += bytesRead;
67+
68+
// If bytesRead is less than the buffer size, we've read the whole file
69+
if (bytesRead < buffer.byteLength) {
70+
break;
71+
}
72+
}
73+
74+
// Check that we read the expected number of bytes
75+
if (totalBytesRead != fileinfo.size) {
76+
throw new Error('Unexpected number of bytes read');
77+
}
78+
79+
// Seek back to the beginning of the file
80+
await file.seek(0, SeekMode.Start);
81+
}
82+
```
83+
84+
{{< /code >}}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
---
2+
title: 'File'
3+
description: 'File represents a file with methods for reading, seeking, and obtaining file stats.'
4+
weight: 10
5+
---
6+
7+
# File
8+
9+
The `File` class represents a file with methods for reading, seeking, and obtaining file stats. It's returned by the [open](https://grafana.com/docs/k6/latest/javascript-api/init-context/open/) function.
10+
11+
## Properties
12+
13+
| Property | Type | Description |
14+
| :------- | :----- | :----------------------------- |
15+
| path | string | The absolute path to the file. |
16+
17+
## Methods
18+
19+
| Method | Description |
20+
| :------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------- |
21+
| [read](https://grafana.com/docs/k6/<K6_VERSION>/javascript-api/k6-experimental/fs/file/read) | Reads up to `buffer.byteLength` bytes from the file into the passed `buffer`. Returns a promise resolving to the number of bytes read. |
22+
| [seek](https://grafana.com/docs/k6/<K6_VERSION>/javascript-api/k6-experimental/fs/file/seek) | Sets the file position indicator for the file to the passed `offset` bytes. Returns a promise resolving to the new offset. |
23+
| [stat](https://grafana.com/docs/k6/<K6_VERSION>/javascript-api/k6-experimental/fs/file/stat) | Returns a promise resolving to a [FileInfo](https://grafana.com/docs/k6/<K6_VERSION>/javascript-api/k6-experimental/fs/fileinfo/) object with information about the file. |
24+
25+
## Example
26+
27+
{{< code >}}
28+
29+
```javascript
30+
import { open, SeekMode } from 'k6/experimental/fs';
31+
32+
let file;
33+
(async function () {
34+
file = await open('bonjour.txt');
35+
})();
36+
37+
export default async function () {
38+
// About information about the file
39+
const fileinfo = await file.stat();
40+
if (fileinfo.name != 'bonjour.txt') {
41+
throw new Error('Unexpected file name');
42+
}
43+
44+
const buffer = new Uint8Array(4);
45+
46+
let totalBytesRead = 0;
47+
while (true) {
48+
// Read into the buffer
49+
const bytesRead = await file.read(buffer);
50+
if (bytesRead == null) {
51+
// EOF
52+
break;
53+
}
54+
55+
// Do something useful with the content of the buffer
56+
totalBytesRead += bytesRead;
57+
58+
// If bytesRead is less than the buffer size, we've read the whole file
59+
if (bytesRead < buffer.byteLength) {
60+
break;
61+
}
62+
}
63+
64+
// Check that we read the expected number of bytes
65+
if (totalBytesRead != fileinfo.size) {
66+
throw new Error('Unexpected number of bytes read');
67+
}
68+
69+
// Seek back to the beginning of the file
70+
await file.seek(0, SeekMode.Start);
71+
}
72+
```
73+
74+
{{< /code >}}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
---
2+
title: 'read'
3+
description: 'the read method is used to read a chunk of the file.'
4+
weight: 20
5+
---
6+
7+
# read
8+
9+
The `read` method is used to read a chunk of the file into an [Uint8Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) buffer.
10+
11+
It resolves to either the number of bytes read during the operation, or `null` if there was nothing more to read.
12+
13+
## Parameters
14+
15+
| Parameter | Type | Description |
16+
| :-------- | :-------------------------------------------------------------------------------------------------------- | :-------------------------------- |
17+
| buffer | [Uint8Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) | The buffer to read the data into. |
18+
19+
## Returns
20+
21+
A [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) resolving to the number of bytes read or `null` if the end of the file has been reached.
22+
23+
## Example
24+
25+
### Reading a file
26+
27+
In the following example, we open a file and read it in chunks of 128 bytes until we reach the end of the file.
28+
29+
{{< code >}}
30+
31+
```javascript
32+
import { open, SeekMode } from 'k6/experimental/fs';
33+
34+
let file;
35+
(async function () {
36+
file = await open('bonjour.txt');
37+
})();
38+
39+
export default async function () {
40+
const buffer = new Uint8Array(128);
41+
42+
let totalBytesRead = 0;
43+
while (true) {
44+
// Read into the buffer
45+
const bytesRead = await file.read(buffer);
46+
if (bytesRead == null) {
47+
// EOF
48+
break;
49+
}
50+
51+
// Do something useful with the content of the buffer
52+
totalBytesRead += bytesRead;
53+
54+
// If bytesRead is less than the buffer size, we've read the whole file
55+
if (bytesRead < buffer.byteLength) {
56+
break;
57+
}
58+
}
59+
60+
// Seek back to the beginning of the file
61+
await file.seek(0, SeekMode.Start);
62+
}
63+
```
64+
65+
{{< /code >}}
66+
67+
### `readAll` helper function
68+
69+
The following helper function can be used to read the entire contents of a file into a [Uint8Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) buffer.
70+
71+
{{< code >}}
72+
73+
```javascript
74+
import { open, SeekMode } from 'k6/experimental/fs';
75+
76+
let file;
77+
(async function () {
78+
file = await open('bonjour.txt');
79+
})();
80+
81+
async function readAll(file) {
82+
const fileInfo = await file.stat();
83+
const buffer = new Uint8Array(fileInfo.size);
84+
85+
const bytesRead = await file.read(buffer);
86+
if (bytesRead !== fileInfo.size) {
87+
throw new Error(
88+
'unexpected number of bytes read; expected ' +
89+
fileInfo.size +
90+
' but got ' +
91+
bytesRead +
92+
' bytes'
93+
);
94+
}
95+
96+
return buffer;
97+
}
98+
99+
export default async function () {
100+
// Read the whole file
101+
const fileContent = await readAll(file);
102+
console.log(JSON.stringify(fileContent));
103+
104+
// Seek back to the beginning of the file
105+
await file.seek(0, SeekMode.Start);
106+
}
107+
```
108+
109+
{{< /code >}}

0 commit comments

Comments
 (0)