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

Add a design document proposal to support open in VU context #3358

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
101 changes: 101 additions & 0 deletions docs/design/020-opening-files-in-vu-context.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# Opening files within the VU stage


| Date | Change |
| --- | --- |
| Authors | @oleiade |
| Status | Draft |
| Issue | [#3020](https://github.com/grafana/k6/issues/3020)

## Problem statement

In its present state, k6's approach to handling external files, both in local/OSS and cloud contexts, restricts the ability to open files within the VU (Virtual User) stage.

Question: **How might we enable k6 to access external resources directly from the VU stage?**


### Rationale

One of k6 main features is the ability to create reproducible, idempotent archives. These archives can be run consistently across various environments, permitting users to draft scripts locally and then either upload or execute them in the cloud.

To accomplish this, k6 assembles user scripts and their associated assets (which can range from other scripts, as well as text (JSON, CSV, etc.) and binary files (protobuf, images, etc.)) into a specific tar archive format. Furthermore, k6 can initiate a test by unpacking and executing such an archive.

However, when dealing with file access, a significant limitation arises: for k6 to maintain its idempotent behavior, it must preemptively recognize which resources to incorporate in the archive and their locations.


Yet, if users dynamically determine file paths during script execution (rather than during compilation), k6 can't predict the resources accessed within the VU stage.

This unpredictability stems from the fact that the VU stage is only evaluated (and any dynamic variables are only assigned) when k6 starts the VU execution of the script. Therefore, k6's current design confines file access to the init stage, which is executed a single time before VU activities commence, and any processes within the init stage are subsequently available to the VU stage.

### Requirements

- **File access in VU stage**: The adopted solution should permit file access both within the VU stage and the init stage.
- **Idempotency and reproducibility**: The adopted solution should ensure that the archives remain reproducible and idempotent, irrespective of where files are accessed.
- **Backward compatibility**: The adopted solution should be compatible with the current k6 archive command and should not require any change to the command itself. Namely, it should remain possible to open files in the init stage.
- **Developer experience**: The adopted solution should improve the developer experience and limit the cost of adopting the new file handling.
- **Error handling**: The adopted solution should allow for and provide precise error handling and transparent reporting, especially when dealing with file access issues or conflicts in the VU stage (which is a strength of the current way of handling files).
- **Backward compatibility**: The adopted solution should refrain from introducing breaking changes to the current solution.

## Possible Solutions

### Solution A: Prior declaration of resources

#### Using a dedicated API

We can provide an API, possibly as part of the new fs module, for users to explicitly include files.

```javascript
import { open, include } from "k6/experimental/fs";

include('my/source/file.txt')
include('my/dir')

export default function () {
// succeeds
await open('my/source/file.txt');

// fails with 'resource csvs/data.csv not included by k6, ...'
await open('csvs/data.csv')
}
```

**Key Points**:
- The `include` function is only usable in the init stage. Invoking it within VU code will trigger an error.
- Using `include` ensures a file's inclusion during the archive process, making it accessible in the VU stage.
- Opening an un-included file in the VU stage will result in an error.
Files opened in the init stage without prior inclusion should still operate as they currently do (though this is debatable) to maintain backward compatibility.

#### Using dedicated option set
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#### Using dedicated option set
### Solution B: A dedicated option set

I evaluate it as a different proposal because they should be mutually exclusive. I don't see the reason why we should eventually implement both.


In addition to, or instead of the API, users can specify which files to include via a set of options.

```javascript
export const options = {
// ...

include: [
'my/source/file.txt',
'testdata',
]
}

export default function () {
// succeeds
await open('my/source/file.txt');

// fails with 'resource csvs/data.csv not included by k6, ...'
await open('csvs/data.csv')
}
```

**Key Points**:
- The rules from the explicit API still apply: if a file isn't referenced in the options and is opened within the VU stage, an error occurs.
- Attempting to open an un-referenced file within the init stage may not result in an error, ensuring backward compatibility.

### Solution B: Awaiting Proposals

Have a novel idea? Please share it with us in a dedicated commit :bow:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would argue we do not need a new API at all.

There are two problems:

  1. you can't at all open files outside of init code
  2. it might be beneficial to have an API that tells k6 to allow a given file to be openable outside of the init code.

There is also the sligth twist that actually open will return you an error even in init code if you try to open a file in a VU after the initial one that was not opened by the initial one.

#2314

open("./someotherfile.txt") // will not error out for either the initial nor the VU with id `1`
if (__VU == 1) {
  open("./somefile.txt") // will always error out as the initial VU did not open it.
}

Again the first open is called both in VU with id 0 but also for any VU that will execute a default function (for example). See #1771 for the rational and discussion.

the tl;dr is that if we want to allow open to at all execute in vu code we can have the same limitation - the file needs to be opened in the VU==0. This is one decision and change.

Adding API/configuration that adds files (again in the initial VU as discussed in #1771) is IMO a separate discussion.


## Technical Decisions

To be agreed upon and decided.