Skip to content

shared store resource pool #10010#10029

Merged
christian-bromann merged 8 commits intowebdriverio:mainfrom
pedrorfernandes:main
Apr 5, 2023
Merged

shared store resource pool #10010#10029
christian-bromann merged 8 commits intowebdriverio:mainfrom
pedrorfernandes:main

Conversation

@pedrorfernandes
Copy link
Contributor

Solves #10010

Proposed changes

Types of changes

  • Bugfix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update

Checklist

  • I have read the CONTRIBUTING doc
  • I have added tests that prove my fix is effective or that my feature works
  • I have added necessary documentation (if appropriate)
  • I have added proper type definitions for new commands (if appropriate)

Further comments

Reviewers: @webdriverio/project-committers

@pedrorfernandes
Copy link
Contributor Author

pedrorfernandes commented Mar 22, 2023

@christian-bromann I will create docs + tests for this once the API is approved

I had to modify the setInterval code because it would be difficult to re-use it with setResourcePool, please validate if this approach is ok.

To not break contract with setValue, I had to return an empty promise when baseUrl is not set yet. Kind of confusing but I didn't test if using await setValue() in onPrepare could dead lock the hook (opted just to not break contract instead)

Copy link
Member

@christian-bromann christian-bromann left a comment

Choose a reason for hiding this comment

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

Some comments. After we agree on the implementation, let's make sure to add some unit tests and docs.

Copy link
Member

@christian-bromann christian-bromann left a comment

Choose a reason for hiding this comment

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

One comment but the implementation looks good to me. Let's add some tests for it.

import { getValue, setValue, setPort } from './client.js'
import { getValue, setValue, setPort, setResourcePool, getValueFromPool, addValueToPool } from './client.js'
import type { SharedStoreServiceCapabilities } from './types.js'
import type { JsonArray } from '@wdio/types'
Copy link
Member

Choose a reason for hiding this comment

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

We already import types in line 1, let's merge these.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

oops, added now
will work on tests tomorrow

@christian-bromann christian-bromann added the PR: New Feature 🚀 PRs that contain new features label Mar 26, 2023
resourcePoolStore.set(key, value)
return res.end()
})
.post('/getValueFromPool/:key', async (req, res, next) => {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@christian-bromann setting this to .get caused me issues, unit tests that throw errors would hang for 2s instead of an immediate reply. .post() throws the error immediately

Copy link
Member

Choose a reason for hiding this comment

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

Let's fix the unit test then, this should definitely be a get method. Given it is not really a user facing API I am happy to ignore the url path.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

figured it out, got has a retry mechanism on error, I've set all throwHttpErrors: false, retry: { limit: 0 }

Copy link
Contributor Author

Choose a reason for hiding this comment

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

also, to note: GET operations should not modify the state of data, that's why got was retrying, assuming that it wouldn't make a difference in the server. A POST operation is more suited to our api call because it's actually modifying internal data

const key = req.params.key as string

if (!resourcePoolStore.has(key)) {
next(new Error(`'${key}' resource pool does not exist. Set it first using 'setResourcePool'`))
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@christian-bromann I had to change throwing to next(new Error, this is the correct way for polka to throw error messages

Copy link
Member

Choose a reason for hiding this comment

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

sounds good, let's do:

Suggested change
next(new Error(`'${key}' resource pool does not exist. Set it first using 'setResourcePool'`))
return next(new Error(`'${key}' resource pool does not exist. Set it first using 'setResourcePool'`))

Copy link
Contributor Author

Choose a reason for hiding this comment

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

changed to

return next(`'${key}' resource pool does not exist. Set it first using 'setResourcePool'`)`

because new Error serializes to {}, serializing all errors by hand is too cumbersome
Polka recommends to just next(string) because this throws the error

I've added a catch + throw error on the client to read this message

})

describe('and the timeout is not specified', () => {
it('should return a value within the default timeout', async () => {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@christian-bromann I couldn't test the max timeout situation

the issue is: got.post needs a free event loop to run it's flush() and dispatch the request

I would need to release the event loop so that got can work. It doesn't expose the flush fn for us to make it work immediately

However, using vi.useFakeTimers() cancels all timers. This means that in the test i can not release the event loop with a setTimeout and take it back later. got can not run this way

I couldn't figure out a way to jump start got without releasing the event loop

Copy link
Contributor Author

Choose a reason for hiding this comment

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

also the test for timeout takes

 ✓ packages/wdio-shared-store-service/tests/server.test.ts (14) 1281ms

doesn't affect the unit test pipeline a lot, but want to know your thoughts

Copy link
Member

@christian-bromann christian-bromann left a comment

Choose a reason for hiding this comment

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

Some comments, great work so far 👍

resourcePoolStore.set(key, value)
return res.end()
})
.post('/getValueFromPool/:key', async (req, res, next) => {
Copy link
Member

Choose a reason for hiding this comment

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

Let's fix the unit test then, this should definitely be a get method. Given it is not really a user facing API I am happy to ignore the url path.

const key = req.params.key as string

if (!resourcePoolStore.has(key)) {
next(new Error(`'${key}' resource pool does not exist. Set it first using 'setResourcePool'`))
Copy link
Member

Choose a reason for hiding this comment

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

sounds good, let's do:

Suggested change
next(new Error(`'${key}' resource pool does not exist. Set it first using 'setResourcePool'`))
return next(new Error(`'${key}' resource pool does not exist. Set it first using 'setResourcePool'`))

const errHandler = (err: Response<Error>) => {
log.warn(err.statusCode, err.statusMessage, err.url, err.body)
const errHandler = (err: RequestError) => {
throw new Error(`${err.response?.body || 'Shared store server threw an error'}`)
Copy link
Contributor Author

@pedrorfernandes pedrorfernandes Mar 30, 2023

Choose a reason for hiding this comment

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

@christian-bromann please validate this

before: the polka server would crash on throw error inside the HTTP handler, and all wdio processes crashed suddenly
now: the polka server returns an error, this client throws it to wdio, the message is logged, the worker crashes, but wdio process continues on to the next test

i think this was the desired behaviour, not to immediately crash all running suites

Copy link
Member

Choose a reason for hiding this comment

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

Sounds good to me 👍

Copy link
Member

@christian-bromann christian-bromann left a comment

Choose a reason for hiding this comment

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

Thank you for all this work. I want to include this into the next release so I will go ahead, merge this and address the missing pieces separately.

const errHandler = (err: Response<Error>) => {
log.warn(err.statusCode, err.statusMessage, err.url, err.body)
const errHandler = (err: RequestError) => {
throw new Error(`${err.response?.body || 'Shared store server threw an error'}`)
Copy link
Member

Choose a reason for hiding this comment

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

Sounds good to me 👍

@christian-bromann christian-bromann merged commit d87519f into webdriverio:main Apr 5, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

PR: New Feature 🚀 PRs that contain new features

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants