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

[Event Hubs] Perform sendRequest() operation handling from SDK #4322

Merged
merged 47 commits into from
Jul 29, 2019

Conversation

ramya0820
Copy link
Member

Bring over RequestResponseLink setup utilities over from core-amqp to the Event Hubs SDK.
This will be subsequently trimmed down, updated so it does only what the SDK requires (specifically exclude retries)

This is the alternate and preferred implementation that may override PR #4321 @ramya-rao-a

@ramya0820 ramya0820 self-assigned this Jul 15, 2019
@ramya0820 ramya0820 changed the title [Event Hubs] Move RequestResponseLink creation, management from the SDK [Event Hubs] Perform sendRequest() operation handling from SDK Jul 16, 2019
@ramya-rao-a
Copy link
Contributor

ramya-rao-a commented Jul 16, 2019

Bringing over the entire RequestResponseLink from core-amqp is an overkill. I would suggest the below:

  • Update sendRequest() in core-amqp to not use the retry() function at all. SendRequestOptions should not take any retry related properties other than the per try timeout
  • Update event hubs to use the retry() function and pass an operation to it that will do both link creation and calling sendRequest.
  • sendRequest would need a timeout value. Pass the per try timeout value set in retry options minus the time taken to set up the link if it was not open.

I am basically suggesting something similar to what was tried in #4321 except instead of disabling the retry in core-amqp, we remove that feature altogether. Since removing that feature will break your changes in #4265, we can update the version of core-amqp to 1.0.0-preview.2

@ramya-rao-a
Copy link
Contributor

@ramya0820 The build is failing. Can you take a look?

image

Ramya Raja and others added 2 commits July 18, 2019 17:24
name: "OperationTimeoutError",
message: desc
};
return reject(translate(e));
Copy link
Contributor

Choose a reason for hiding this comment

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

We need to remove the event listener for abort event here before returning with the rejected promise

Copy link
Member Author

Choose a reason for hiding this comment

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

Updated

Copy link
Contributor

Choose a reason for hiding this comment

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

I recall this being resolved, but now I don't see the removal of event listener when the timer times out.

Copy link
Member Author

Choose a reason for hiding this comment

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

We aren't adding listeners anymore as part of the outer abort for init.
Asynchronous abortion (by using event emitter listeners), is currently not trivial for init and right way to go about it would be to pass in the aborter to init
So that the connection gets cleanly closed if already in progress of being created.

Discussed this with @chradek (See #4322 (comment))
Since abort during init is not solved for receive as well, and needs more work and tests, we were looking to move it out of this PR.

@ramya-rao-a
Copy link
Contributor

/azp run js - eventhubs-client - tests

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@chradek
Copy link
Contributor

chradek commented Jul 25, 2019

/azp run js - eventhubs-client - tests

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

const retryTimeoutInMs = getRetryAttemptTimeoutInMs(options.retryOptions);
let timeTakenByInit = 0;

if (!this._isMgmtRequestResponseLinkOpen()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

We should check if the operation has been cancelled before this point so we don't do an unnecessary initialization.

Copy link
Contributor

Choose a reason for hiding this comment

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

We are doing that check right before this line, that should suffice right?

Copy link
Member Author

Choose a reason for hiding this comment

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

Prior to this comment, I had removed abort for around init altogether, since the test "respects cancellation token" was failing.
This was because init doesn't accept the abortSignal and so the connection, sessions, sender/receiver links and associated listeners, timers were all still staying open.
I had tried a lot of things, explicitly closing, clearing them, but looks like the connection needs to be explicitly closed and doing that was not trivial as reference to connection within abandoned init was getting lost. See #4322 (comment)

Here, I agreed with making the synchronous check for before the init for in case we know by then that operation was aborted.

} catch (err) {
return reject(translate(err));
} finally {
clearTimeout(waitTimer);
Copy link
Contributor

Choose a reason for hiding this comment

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

We should also check if the operation was cancelled after calling _init. If it has, we know we can close the connection and reject.

Copy link
Member Author

Choose a reason for hiding this comment

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

This second check is unnecessary since we already have one at the start of sendRequest

Discussed offline about how abortSignal will need to be passed in to the init and other internal operations to gracefully handle cancellation during init as well.
Clearing connection, links, listeners and timers will need to be done as part of this.
Since this needs to be done for receive() as well and would involve more work, changes for it and tests can be added as part of new PR and be linked to separate issue #4422

Copy link
Contributor

Choose a reason for hiding this comment

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

This second check is unnecessary since we already have one at the start of sendRequest

I agree

@chradek
Copy link
Contributor

chradek commented Jul 26, 2019

/azp run js - eventhubs-client - tests

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@ramya-rao-a
Copy link
Contributor

@ramya0820 Looks like when resolving merge conflicts, some changes have been missed out

See #4322 and #4322

Please do one round of review on your own

@ramya0820
Copy link
Member Author

See #4322 and #4322

The links to comments seem to be missing ^

One of them seems to have been about #4322 (comment) which wasn't a miss, but is deliberate conclusion we arrived at to handle abort during init outside of this PR.

Could you share link to the other item you think was missing? @ramya-rao-a

const result = await this._mgmtReqResLink!.sendRequest(request, sendRequestOptions);

resolve(result);
} catch (err) {
Copy link
Contributor

Choose a reason for hiding this comment

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

init() call has its own try/catch
This catch corresponds to the outer try block i.e the block that covers both init() and this._mgmtReqResLink!.sendRequest()
Any reason to have an outer try/catch block this way?

I would imagine that init() and this._mgmtReqResLink!.sendRequest() would have their own try/catch blocks

Copy link
Member Author

Choose a reason for hiding this comment

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

It allows us to catch errors from other code paths in case any gets thrown and indicate in logs with a message about source of it.

Copy link
Contributor

@ramya-rao-a ramya-rao-a Jul 29, 2019

Choose a reason for hiding this comment

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

The 2 points from where an error can be expected to be thrown (by code that this file doesnt control) are the init() call and the this._mgmtReqResLink!.sendRequest() call. Having try/catch around the 2 calls separately is enough.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants