Feature #11562
[Lyra] Multi-node tests playback
100%
History
#1 Updated by Hynek Cihlar 28 days ago
On 6/23/26 10:45, Hynek Cihlar wrote:
Greg,
regarding Lyra and Artillery. Lyra's core is designed such that the recorded tests can be executed with tools like Artillery. The recorded tests are self-contained for example. However during the implementation phase some technical depth accumulated, which would have to be cleaned up first.
Currently Lyra only supports single-node test orchestration. There is one process per VU (i.e. one browser process group and Node process per VU). OTOH Artillery runs multiple VUs in a single process.
I see two product paths. 1) Lyra supporting execution in Artillery (or similar) and 2) Lyra itself be able to drive tests on multiple nodes.
For both the paths there is the same prerequisite: Allowing multiple VUs to be executed in a single process. The following are the gaps that would need to be addressed:
- The helpers ignore the Playwright page object they're handed.
- Module-global state vs in-process VU concurrency. Lyra's helpers keep all per-VU state as module-level singletons, these must be moved into a runtime context.
- Named-timers and metrics, implement an integration layer for the multi-node player (like Artillery).
All the above is about 2 MDs of work.
Additionally for Artillery a thin integration layer would need to be implemented to allow storing metrics and the named timers in its context.
For our own multi-node orchestration some additional work would be needed, like multi-node clustering and remote test spawning, globally unique VUs, metrics transport and merging, clock alignment, etc. This would be substantially more effort, roughly 10 MDs.
Do you want me to create a Redmine issue and provide more details there?
Thanks,
Hynek
#2 Updated by Șerban Bursuc 28 days ago
As a clarification of the 1 VU -> 1 node process architecture. <customer app> still uses OsSsoAuthenticator, which requires 1 OS user per client. Because the spawner with this auth mode spawns the ClientDriver process from the OS's user shell it means that the client process is owned by that OS user.
In Lyra I did use 1 node process for all VUs, but since they were owned by the master OS user, I got several problems when it came to collecting statistics. jstack and jmap wouldn't work, I would get permission denied. Another component that relies heavily on this architecture is the PID tracking, Lyra can easily trace the Java processes because their unique UID, this allows for lifecycle management: cleanup, proper artifact generation, correct dump target (several bugs where since all processes were owned by sb, the wrong ones were used for dumping). Any tool that has strict permissions will not work unless this architecture is used.
We can totally remove this and move to whatever architecture Artillery requires, but unless <customer app> switches the SSO definitively the entire diagnostics component will be missing. I think the best solution is to add the option to switch between modes, such that once the load test machine is ready nothing is effectively changed and we can make quick progress, generate reports for client vs memory usage record performance properly etc. instead of adjusting now, and then having to re-build again the diagnostics component.
#3 Updated by Greg Shah 28 days ago
Gemini chat output:
Prompt 1¶
I'm considering a toolset for implementing load testing for my web application. I use Playwright to write my specific tests. I'm considering Artillery (https://www.artillery.io) as the harness or overall orchestration tool for running the load tests. How does it compare to other options?
Response 1¶
Artillery is the standout choice for teams looking to leverage Playwright for browser-based load testing. Its native engine seamlessly reuses existing Playwright code, tracks Web Vitals, and scales to thousands of headless browsers via serverless AWS setups.
How Artillery Compares to Other Options¶
- k6 (Grafana Labs): The current industry favorite for developer-first teams. k6 is written in Go, making it lighter, faster, and more resource-efficient than Node-based Artillery. While k6 supports browser testing via
k6-browser, integrating raw Playwright scripts into Artillery is more straightforward. - Locust: The premier choice for Python developers. It is code-driven, highly scalable, and excellent for complex, state-heavy scenarios. However, if your Playwright code is in JavaScript/TypeScript, Locust won't help you reuse it.
- Gatling: Built for Scala and Java developers. Gatling yields high concurrency per testing agent, but it lacks the direct, no-hassle Playwright integration that Artillery provides.
- Apache JMeter: The enterprise heavyweight. It's excellent for testing legacy systems and has a massive plugin ecosystem, but its GUI-heavy nature makes it difficult to pair with modern JavaScript-based Playwright suites.
Why Artillery Wins for Your Use Case¶
- Zero Rewrite for Browsers: Artillery lets you import functions from your existing Playwright Page Object Models (POMs) directly into your performance tests with minimal tweaking.
- Flexible Execution: It scales out of the box using serverless infrastructure (AWS Fargate, AWS Lambda) without requiring dedicated orchestration infrastructure.
- Hybrid Testing: You can easily mix pure HTTP API tests (very fast) with Playwright-driven browser paths (resource-heavy) within the same scenario.
Prompt 2¶
The test suite is actually in a derivative of Playwright called Lyra (https://proj.goldencode.com/projects/p2j/wiki/Lyra_Automated_Testing_Evolved). We wrote Lyra and can change or extend it as needed. It is written in Javascript, as are the tests. We are self-hosting. Our expectation is that we will have to implement an integration layer to make Lyra work with Artillery, but we also expect that this layer should be relatively easy since Lyra is based on Playwright.
Response 2¶
Because Lyra is built on top of Playwright and written in JavaScript, integrating it into Artillery is highly achievable. However, you will need to bypass Artillery's standard test-runner architecture and instead treat Lyra as a custom Processor module.
Understanding how Artillery interacts with JavaScript code reveals exactly how to build this integration layer for a self-hosted environment.
How Artillery's Playwright Integration Works Under the Hood¶
To map out your integration layer, you must first understand a major constraint in Artillery: Artillery does not use playwright test (the test runner). It bypasses the standard testing hooks, fixtures, and assertions (test(), beforeEach(), expect()).
Instead, Artillery uses the core Playwright Library API (playwright.chromium.launch()). When an Artillery virtual user (VU) spawns, Artillery automatically spins up a headless browser instance and hands a raw Playwright page object over to a standard JavaScript asynchronous function defined in a processor file.
# artillery-config.yml
config:
target: "https://myapp.local"
engines:
playwright: {} # Loads Artillery's Playwright engine
processor: "./artillery-lyra-bridge.js" # Your integration layer
scenarios:
- engine: playwright
testFunction: "runLyraScenario" # The bridge function Artillery calls
Use code with caution.
Designing the Lyra-Artillery Integration Layer¶
Since you own the Lyra codebase, your integration layer needs to act as an adapter that accepts Artillery's raw page context and feeds it directly into Lyra's internal execution engine.
- Decoupling the Runner from the Actions: If Lyra currently forces scripts to run through a custom command-line interface (CLI) or test runner that handles browser creation internally, you will need to expose a lower-level API. Modify Lyra so its automated testing actions can be executed against an existing, externally provided Playwright
pageinstance. - The Bridge Script (
artillery-lyra-bridge.js): You will write a bridge script that exports a standard function for Artillery to call. This function will receive the Artillery-managed browser context and inject it into your Lyra framework:
// artillery-lyra-bridge.js
const { LyraExecutor } = require('./path-to-your-lyra-core');
async function runLyraScenario(page, vuContext, events) {
// 1. Lyra intercepts Artillery's browser page object
const lyraTest = new LyraExecutor({ externalPage: page });
try {
// 2. Execute your Lyra-specific compiled test JSON or script
await lyraTest.run('./specs/login-and-checkout.lyra.js');
} catch (error) {
// 3. Inform Artillery's aggregator that a virtual user failed
events.emit('counter', 'lyra.errors', 1);
throw error;
}
}
module.exports = { runLyraScenario };
Use code with caution.
Architectural Gotchas for Self-Hosting¶
Since you are entirely self-hosting the infrastructure, keep these two critical constraints in mind when scaling up your Lyra tests:
- Memory Bloat per Virtual User: Because Lyra is an abstraction layer built on top of Playwright, it inherently adds processing overhead to every Virtual User. Headless browsers typically allow only 10 to 20 VUs per CPU core before collapsing from memory starvation. Ensure your self-hosted nodes are provisioned primarily for high RAM capacity.
- Metrics Tracking & Custom Events: Artillery automatically tracks HTTP metrics and core web vitals through its own Playwright engine. However, it will not natively understand custom functional checkpoints inside Lyra. You will want to utilize the
eventsemitter argument provided by Artillery to inject custom metrics or transaction timers straight from Lyra into Artillery's final terminal report usingevents.emit('histogram', 'lyra.transaction_time_ms', duration).
#5 Updated by Hynek Cihlar 28 days ago
- topics AI added
I'm adding my own analysis for completness :-).
Lyra: run recorded tests under Artillery for distributed load testing¶
Goal¶
Run Lyra-recorded tests under Artillery's Playwright engine. The primary motivation is Artillery's built-in distributed execution (run-fargate / run-lambda), which lifts the VU ceiling beyond what the current single-node orchestrator allows.
Background¶
The Lyra player spawns one OS process per virtual user (orchestrator.ts, spawn('node', [testFile])). Each process launches its own browser through wrapTestExecution(), runs the recorded flow, and writes timer results to user_<id>_timers.json. The parent aggregates exit codes and timer files in MetricsCollector. Process-per-VU isolation is the foundation of the current design.
Artillery runs many VUs concurrently inside a single worker process, one browser context per VU, and ships serverless distributed execution that aggregates per-worker reports in a statistically correct way. Artillery's published rule of thumb is one vCPU per browser VU (each VU drives a full browser context); a typically-sized worker therefore hosts on the order of tens of VUs.
The code generator already emits the Artillery contract in every generated test:
module.exports = {
testFlow: async function(page, userContext, events) {
await runTest(page, userContext);
}
};
The signature matches Artillery's Playwright engine (async (page, vuContext, events, test)). The wiring point exists. The flow will not run as-is though, because the helper layer assumes process-per-VU isolation.
Gaps¶
- Helpers ignore the passed page. Every helper (
clickAt,keyPress,clickAtWidget,waitForInputReady) readsglobal.__lyra_page, which is set only insidewrapTestExecution(). Under Artillery nothing sets it, so the first helper call dereferencesundefined. This breaks even a single-VU run. - Module-global state vs in-process concurrency. Per-VU state lives in module-level singletons:
global.__lyra_page,__lyra_activeTimers,__lyra_timerResults,__lyra_datasets,__lyra_userId,__lyra_logFile. With multiple VUs in one process these are shared and corrupt each other. This is the core work item. - VU identity from a single env var.
getData()and per-user log names key offLYRA_USER_ID, one value per process. Artillery exposes per-VU identity viavuContext.vars.$uuid. - Timer metrics do not reach Artillery. Timers are persisted to per-user JSON files harvested by the Lyra orchestrator on process exit. Artillery never reads them, and in-process the files collide.
endTimer()should emit throughevents.emit('histogram', label, duration)instead. - Browser and TLS config.
wrapTestExecutionsetsignoreHTTPSErrors: trueand defaults to Firefox. Artillery builds its own context (default Chromium) and needscontextOptions: { ignoreHTTPSErrors: true }for the self-signedhttps://localhost:7443/gui. Firefox under Artillery needs validation.
Proposed work¶
Move per-VU state out of module globals into a per-VU context object threaded through the helpers. This single refactor:
- unblocks Artillery (helpers use the passed page)
- enables denser single-node packing (multiple VUs per process)
- is the correct fix for the helper layer under any concurrent runner
Then add a thin Artillery adapter: JSON config (target, engine, TLS and browser options), getData() reading vuContext.vars, and timer emission through events.emit.
With the refactor done, artillery run-fargate --count N distributes the test bundle across N workers from the Artillery system and aggregates the reports. No bespoke coordinator, metrics transport, or report-merge code to own. Effective ceiling becomes roughly 20 x --count browser VUs.
Notes¶
The browser cost dominates regardless of the concurrency model. Each VU drives a real browser, so a node tops out around tens of VUs whether process-per-VU or context-per-VU. Many more VUs is fundamentally a horizontal-scale problem. The refactor improves density per node, distribution adds the nodes.
References¶
- Artillery Playwright engine: https://www.artillery.io/docs/reference/engines/playwright
- Playwright engine resourcing (1 vCPU per VU): https://www.artillery.io/docs/playwright
- Distributed load testing at scale: https://www.artillery.io/docs/load-testing-at-scale
- run-fargate: https://www.artillery.io/docs/reference/cli/run-fargate
#6 Updated by Hynek Cihlar 28 days ago
Serban, you should think more outside the box, literally :-). Lyra is executed on its own server node (or nodes) completely independent on FWD. This is the typical and required deployment model. Running everything on one system is typically needed only during development.
Șerban Bursuc wrote:
As a clarification of the 1 VU -> 1 node process architecture. <customer app> still uses
OsSsoAuthenticator, which requires 1 OS user per client. Because the spawner with this auth mode spawns theClientDriverprocess from the OS's user shell it means that the client process is owned by that OS user.
#8 Updated by Șerban Bursuc 28 days ago
Hynek Cihlar wrote:
Serban, you should think more outside the box, literally :-). Lyra is executed on its own server node (or nodes) completely independent on FWD. This is the typical and required deployment model. Running everything on one system is typically needed only during development.
Șerban Bursuc wrote:
As a clarification of the 1 VU -> 1 node process architecture. <customer app> still uses
OsSsoAuthenticator, which requires 1 OS user per client. Because the spawner with this auth mode spawns theClientDriverprocess from the OS's user shell it means that the client process is owned by that OS user.
And my point is that the development is not ready :-). The current state should be kept at least until we can test 10860c on our load test machine, because there might be some changes that need to be done before planning for Artillery. This would also allow the so-wanted data collection from load testing I've mentioned. If Lyra gets changed BEFORE this happens, then this prospect becomes increasingly difficult. There's still skepticism as to why tests are failing with multiple users, and while we do have evidence, I would rather consider waiting for a proper Lyra load test. The load test harness still hasn't been tested with many users, and when the distributed obfuscation comes in, then we will get these answers even later.
So in short, I have no problem with creating a complex distributed Lyra system, but for this:
1. Need a way to actually test changes as we go, not making speculative changes that will need to be fixed later anyway.
2. Need to clear some current questions, as the load test harness has been tested on a poor environment.
The order of development is sort of bothering me ATM with Lyra. Of course we can use a special branch for the targeted Artillery changes and add them when needed, but if there's a clash with the changes done after doing a load test on our machine, then it becomes complicated.
Could we expose the diagnostics tooling as admin REST APIs in the FWD server? That would allow the support to be distributed.
Some kind of harness still needs to collect those statistics. I think this is moving the job from one place to another, instead of a Lyra component doing the diagnostics on the server machine and sending it to the node machine, you rely on other means that essentially have to do the same thing. An REST API will have to be implemented for sure, but what collects this data should still be Lyra, even if in a distributed system.
This all goes out the window if jstack and jmap can somehow be used remotely without strict permissions, if there's a way to do that ignore my point.
#9 Updated by Hynek Cihlar 28 days ago
Șerban Bursuc wrote:
Hynek Cihlar wrote:
Serban, you should think more outside the box, literally :-). Lyra is executed on its own server node (or nodes) completely independent on FWD. This is the typical and required deployment model. Running everything on one system is typically needed only during development.
Șerban Bursuc wrote:
As a clarification of the 1 VU -> 1 node process architecture. <customer app> still uses
OsSsoAuthenticator, which requires 1 OS user per client. Because the spawner with this auth mode spawns theClientDriverprocess from the OS's user shell it means that the client process is owned by that OS user.And my point is that the development is not ready :-). The current state should be kept at least until we can test 10860c on our load test machine, because there might be some changes that need to be done before planning for Artillery. This would also allow the so-wanted data collection from load testing I've mentioned. If Lyra gets changed BEFORE this happens, then this prospect becomes increasingly difficult. There's still skepticism as to why tests are failing with multiple users, and while we do have evidence, I would rather consider waiting for a proper Lyra load test. The load test harness still hasn't been tested with many users, and when the distributed obfuscation comes in, then we will get these answers even later.
I don't disagree. Adding another layer may make things more complex and harder to diagnose. But to be able to run many many test VUs it will require multi-node playback, there is no way around this.
Also replicating the target deployment setup closely will help to reduce noise and hopefully simplify diagnosing found issues.
The order of development is sort of bothering me ATM with Lyra. Of course we can use a special branch for the targeted Artillery changes and add them when needed, but if there's a clash with the changes done after doing a load test on our machine, then it becomes complicated.
The direction towards a distributed load testing (whether Aretillery or an in-house orchestrator) was on the path since the beginning. No way you could achieve reasonable load from a single node.
#10 Updated by Hynek Cihlar 28 days ago
Greg Shah wrote:
Could we expose the diagnostics tooling as admin REST APIs in the FWD server? That would allow the support to be distributed.
We already have JMX, that I think would be more lighweight and standard mechanism.
#11 Updated by Șerban Bursuc 28 days ago
Hynek Cihlar wrote:
Șerban Bursuc wrote:
Hynek Cihlar wrote:
Serban, you should think more outside the box, literally :-). Lyra is executed on its own server node (or nodes) completely independent on FWD. This is the typical and required deployment model. Running everything on one system is typically needed only during development.
Șerban Bursuc wrote:
As a clarification of the 1 VU -> 1 node process architecture. <customer app> still uses
OsSsoAuthenticator, which requires 1 OS user per client. Because the spawner with this auth mode spawns theClientDriverprocess from the OS's user shell it means that the client process is owned by that OS user.And my point is that the development is not ready :-). The current state should be kept at least until we can test 10860c on our load test machine, because there might be some changes that need to be done before planning for Artillery. This would also allow the so-wanted data collection from load testing I've mentioned. If Lyra gets changed BEFORE this happens, then this prospect becomes increasingly difficult. There's still skepticism as to why tests are failing with multiple users, and while we do have evidence, I would rather consider waiting for a proper Lyra load test. The load test harness still hasn't been tested with many users, and when the distributed obfuscation comes in, then we will get these answers even later.
I don't disagree. Adding another layer may make things more complex and harder to diagnose. But to be able to run many many test VUs it will require multi-node playback, there is no way around this.
Also replicating the target deployment setup closely will help to reduce noise and hopefully simplify diagnosing found issues.
The order of development is sort of bothering me ATM with Lyra. Of course we can use a special branch for the targeted Artillery changes and add them when needed, but if there's a clash with the changes done after doing a load test on our machine, then it becomes complicated.
The direction towards a distributed load testing (whether Aretillery or an in-house orchestrator) was on the path since the beginning. No way you could achieve reasonable load from a single node.
Of course, we proceed as planned. But for this step we really need that load testing machine. We need it to both test the current state of the harness/CACT and to be able to test development for distributed systems. I am trying to say to not glance over this and directly jump to the next major Lyra step, I think the current state needs to be consolidated first and these massive changes need to have a test environment ready.
#12 Updated by Hynek Cihlar 28 days ago
Șerban Bursuc wrote:
Of course, we proceed as planned. But for this step we really need that load testing machine. We need it to both test the current state of the harness/CACT and to be able to test development for distributed systems. I am trying to say to not glance over this and directly jump to the next major Lyra step, I think the current state needs to be consolidated first and these massive changes need to have a test environment ready.
Can you use VirtualBox for the devel part? It should suffice on the small scale.
#13 Updated by Șerban Bursuc 28 days ago
Hynek Cihlar wrote:
Șerban Bursuc wrote:
Of course, we proceed as planned. But for this step we really need that load testing machine. We need it to both test the current state of the harness/CACT and to be able to test development for distributed systems. I am trying to say to not glance over this and directly jump to the next major Lyra step, I think the current state needs to be consolidated first and these massive changes need to have a test environment ready.
Can you use VirtualBox for the devel part? It should suffice on the small scale.
No, it consumes too much RAM, even without load testing I seldom can have a FWD server open + VirtualBox because I get frequent OS crashes. I have it at 8GB and if I lower I have instability (using Windows). This would be even worse with the load testing overhead. I could use a very low overhead distro, but if the server is on my machine I still will struggle with memory if the node count increases.
With a machine at GCD we could test 1 orchestrator (probably at the GCD machine) and 2 test runners, which seems like a fair development setup for this next phase. Maybe could even turn my own laptop into a compute node.
#14 Updated by Hynek Cihlar 19 days ago
- Status changed from New to WIP
#15 Updated by Hynek Cihlar 19 days ago
Serban, I will create new task branch 11562a for this work. My plan is to to perform the refactorings first and merge them before implementing the Artillery specific changes. This should minimize the impact on your current work.
#16 Updated by Șerban Bursuc 19 days ago
Hynek Cihlar wrote:
Serban, I will create new task branch 11562a for this work. My plan is to to perform the refactorings first and merge them before implementing the Artillery specific changes. This should minimize the impact on your current work.
Yes, unfortunately no load test machine means we need changes in the dark. I'm fine with this as long as the single node current approach is not regressed. I can test your changes in a distributed environment (but just barely) once they're ready.
#17 Updated by Hynek Cihlar 19 days ago
Șerban Bursuc wrote:
Hynek Cihlar wrote:
Serban, I will create new task branch 11562a for this work. My plan is to to perform the refactorings first and merge them before implementing the Artillery specific changes. This should minimize the impact on your current work.
Yes, unfortunately no load test machine means we need changes in the dark. I'm fine with this as long as the single node current approach is not regressed. I can test your changes in a distributed environment (but just barely) once they're ready.
Sounds good.
#18 Updated by Hynek Cihlar 19 days ago
- Status changed from WIP to Review
- % Done changed from 0 to 60
11562a contains the required refactorings. I did some elementary testing, more testing is WIP. Serban, please review.
#19 Updated by Hynek Cihlar 19 days ago
I have a working Artillery support implemented, not yet checked in. A simple (but distributed) smoke test passed.
#20 Updated by Șerban Bursuc 18 days ago
Hynek Cihlar wrote:
11562a contains the required refactorings. I did some elementary testing, more testing is WIP. Serban, please review.
Sorry for the delay, I mistook a bug from trunk Lyra with changes from this branch. The changes on the branch are good.
#21 Updated by Hynek Cihlar 18 days ago
Șerban Bursuc wrote:
Hynek Cihlar wrote:
11562a contains the required refactorings. I did some elementary testing, more testing is WIP. Serban, please review.
Sorry for the delay, I mistook a bug from trunk Lyra with changes from this branch. The changes on the branch are good.
Serban, plrease run your tests with the branch.
#22 Updated by Șerban Bursuc 18 days ago
Serban, plrease run your tests with the branch.
I initially did but it's good that you pressed on this because it made me realize they were done with the old helpers file :). The new helper file is fine but requires a small patch to work, I attached it. I'm not sure it's correct though but it allowed me to run all tests successfully, otherwise there are these two errors:
Error: Dataset 'credentials' not found. Call loadDataset first.
at getData (/home/sb/gcd/trunk/tools/lyra/lyra-test-helpers-1.2.0.js:147:11)
at runTest (/home/sb/gcd/trunk/tools/lyra/kbp1_account_customer_order.js:41:16)
at async wrapTestExecution.viewport.width (/home/sb/gcd/trunk/tools/lyra/kbp1_account_customer_order.js:9649:5)
at async _wrapTestExecutionWorker (/home/sb/gcd/trunk/tools/lyra/lyra-test-helpers-1.2.0.js:2796:5)
Error: [lyra] Counter 'REPRINT_DOC' not configured — call configureCounter('REPRINT_DOC', ...) first
at nextUnique (/home/sb/gcd/trunk/tools/lyra/lyra-test-helpers-1.2.0.js:3065:19)
at typeUnique (/home/sb/gcd/trunk/tools/lyra/lyra-test-helpers-1.2.0.js:3114:22)
at runTest (/home/sb/gcd/trunk/tools/lyra/kbp2_cash_collect_sale_reprint_document.js:4651:9)
at async /home/sb/gcd/trunk/tools/lyra/kbp2_cash_collect_sale_reprint_document.js:4935:5
at async _wrapTestExecutionWorker (/home/sb/gcd/trunk/tools/lyra/lyra-test-helpers-1.2.0.js:2805:5)
#23 Updated by Hynek Cihlar 12 days ago
Serban, please attach the patch.
#24 Updated by Șerban Bursuc 12 days ago
I send it to you via email when Redmine was down a few days ago. Here it is again.
--- lyra-test-helpers-1.2.0.js.orig
+++ lyra-test-helpers-1.2.0.js
@@ -142,7 +142,9 @@
* @returns {*} The data item for this user.
*/
function getData(name) {
- const dataset = _lyraCtx().datasets.get(name);
+ // Datasets are typically loaded at module scope, before wrapTestExecution() enters
+ // the per-VU context — those land in the default context, so fall back to it.
+ const dataset = _lyraCtx().datasets.get(name) || __lyra_default_ctx.datasets.get(name);
if (!dataset) {
throw new Error(`Dataset '${name}' not found. Call loadDataset first.`);
}
@@ -3052,7 +3054,9 @@
* @returns {Promise<string>} Formatted unique code, e.g. 'INV01'
*/
async function nextUnique(name) {
- const cfg = _lyraCtx().counterPatterns.get(name);
+ // Counters are typically configured at module scope, before wrapTestExecution() enters
+ // the per-VU context — those land in the default context, so fall back to it.
+ const cfg = _lyraCtx().counterPatterns.get(name) || __lyra_default_ctx.counterPatterns.get(name);
if (!cfg) throw new Error(`[lyra] Counter '${name}' not configured — call configureCounter('${name}', ...) first`);
const counterFile = _lyra_counter_file();
#25 Updated by Hynek Cihlar 12 days ago
- Status changed from Review to Merge Pending
The patch is in 11562a, regression testing passed. I'll merge 11562a to trunk when trunk freeze is lifted.
#26 Updated by Hynek Cihlar 12 days ago
- Status changed from Merge Pending to WIP
- % Done changed from 60 to 70
11562a merged to trunk revision 16645.
#27 Updated by Hynek Cihlar 5 days ago
- % Done changed from 70 to 100
- Status changed from WIP to Review
Artillery support for Lyra checked in to 11562b. Serban, please review. See the instructions how to run the Lyra tests in Artillery below (wiki doc update pending).
Prerequisites¶
- Node.js installed.
- A Lyra-generated test file (e.g.
your-test.js) with its versioned helpers filelyra-test-helpers-<version>.jssitting in the same directory (the test @require@s it by relative path). - The target application running and reachable at the URL you will set as Artillery's
target.
1. Install Artillery¶
Artillery v2 bundles the Playwright engine and its own Playwright + Chromium — there is no separate @artillery/engine-playwright package to install.
# from the directory that holds your tests (or any project with a package.json) npm install --save-dev artillery # install the Chromium build Artillery's Playwright uses, if not already cached npx playwright install chromium
Verify the install with npx artillery --version.
2. Update tests recorded with an older Lyra version¶
Tests recorded before Artillery support exported a testFlow that called the test body (runTest) directly, and their helpers file did not export runTestFlow. Under Artillery each VU needs its own per-VU runtime context, which runTestFlow sets up. Bring an old recording up to date in two steps.
Regenerate the helpers file so it exports runTestFlow (and runInVuContext). Run this in the directory that contains your test; it rewrites lyra-test-helpers-<version>.js in place:
lyra record --helpers-only --output throwaway.js # only the helpers file is (re)generated; remove any throwaway output file
Update each recorded test's export to delegate to runTestFlow. Add runTestFlow to the destructured require and replace the export body.
Before (older Lyra):
const { keyPress, holdAtWidget, /* ... */ } = require('./lyra-test-helpers-<version>.js');
module.exports = {
testFlow: async function(page, userContext, events) {
await runTest(page, userContext);
}
};
After (Artillery-ready):
const { keyPress, holdAtWidget, /* ... */, runTestFlow } = require('./lyra-test-helpers-<version>.js');
module.exports = {
testFlow: async function(page, userContext, events) {
await runTestFlow(page, userContext, events, runTest,
{ testName: require('path').basename(__filename, '.js') });
}
};
Leave the standalone block — if (require.main === module) { ... wrapTestExecution ... } — unchanged. That is the path lyra play uses, and it keeps working. Tests recorded with a current Lyra version already emit the runTestFlow form, so only older recordings need this edit.
3. Write the Artillery config (YAML)¶
Point an Artillery scenario at the generated test file. A generic artillery.yml:
config:
target: https://your-app.example
engines:
playwright:
launchOptions:
headless: true
contextOptions:
ignoreHTTPSErrors: true # if the target uses a self-signed cert
permissions: ["clipboard-read", "clipboard-write"]
# viewport: { width: 1536, height: 874 } # match the recorded viewport if it mattered
processor: "./your-test.js" # the Lyra test; lyra-test-helpers-<version>.js must be next to it
phases:
- duration: 60
arrivalRate: 1
maxVusers: 5
scenarios:
- engine: playwright
testFunction: testFlow
Key points:
processoris the generated test file; itslyra-test-helpers-<version>.jscompanion must be in the same directory so the relativerequireresolves.testFunction: testFlowis the exported entry point Artillery calls per VU.- All paths are relative to the config file's own location.
- Set the
LYRA_LOG_DIRenvironment variable to an existing directory if you want per-VU failure artifacts collected there.
4. Run¶
npx artillery run artillery.yml # distributed execution (Artillery cloud runners): npx artillery run-fargate --count 10 artillery.yml npx artillery run-lambda --count 10 artillery.yml
Metrics¶
Each Lyra startTimer(name) / endTimer(name) pair reports through Artillery as a histogram (internally events.emit('histogram', name, ms)), so your named timers appear in Artillery's own report next to the engine's counters. The per-user user_<id>_timers.json files that the Lyra orchestrator writes are not produced under Artillery.
Behavior differences vs the Lyra orchestrator¶
| Aspect | Under Artillery |
|---|---|
| Browser | Artillery's Playwright engine drives Chromium. Recordings default to Firefox, so verify any browser-sensitive flow. |
| Per-VU identity | Each VU gets an in-process numeric id (used by getData() sequence datasets) and Artillery's $uuid as its user id for logs and failure folders. LYRA_USER_ID is ignored — it is one value per process. |
| Host diagnostics | Disabled. ClientDriver PID tracking, kill-on-failure, on-stall thread dumps and the resource sampler all assume one OS process per VU. Use lyra play --load-profile when you need them. |
LYRA_KEEP_BROWSER_OPEN |
Ignored — Artillery owns the browser lifecycle. |
Other LYRA_* flags |
LYRA_DEBUG, LYRA_SKIP_WAITS, LYRA_STEP_DELAY, LYRA_LOG_DIR still apply; they are process-wide and identical for all VUs. |
#28 Updated by Șerban Bursuc 1 day ago
Hynek, I see nothing wrong with the changes but they need to be tested using the current CACT suite, at least 2 clients.