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

[PECO-618] Better handling of in-progress/subsequent action taken on closed session/operation #129

Merged
merged 23 commits into from
Aug 17, 2023

Conversation

kravets-levko
Copy link
Contributor

@kravets-levko kravets-levko commented Mar 29, 2023

PECO-618

Asynchronous nature of NodeJs makes possible situations when user started working with some operation (e.g. fetching data), and at the same time the operation or even the corresponding session gets closed (sometimes it’s a result of race condition). Session also may expire after some time of inactivity, which also causes similar issue.

We need to properly handle cases when some action is performed on closed operation/session:

  • if operation/session was closed by the library - mark the operation/session as closed, and use this flag to throw a meaningful error on all subsequent actions (this solution is already implemented in PySQL)
  • if operation/session was closed by server, handle the returned errors and show meaningful message to user.

Checklist:

  • Keep track on active sessions and operations and close them when parent session/connection gets closed
  • Reject all actions on closed connections/sessions/operations
  • Better handling of sessions/operations closed by server (e.g. expired)
  • Tests

Signed-off-by: Levko Kravets <levko.ne@gmail.com>
Signed-off-by: Levko Kravets <levko.ne@gmail.com>
Signed-off-by: Levko Kravets <levko.ne@gmail.com>
Signed-off-by: Levko Kravets <levko.ne@gmail.com>
Signed-off-by: Levko Kravets <levko.ne@gmail.com>
Signed-off-by: Levko Kravets <levko.ne@gmail.com>
Signed-off-by: Levko Kravets <levko.ne@gmail.com>
…esponse only when operation is being closed

Signed-off-by: Levko Kravets <levko.ne@gmail.com>
…losed, it still attempted to fetch data

Signed-off-by: Levko Kravets <levko.ne@gmail.com>
Signed-off-by: Levko Kravets <levko.ne@gmail.com>
Signed-off-by: Levko Kravets <levko.ne@gmail.com>
@databricks databricks deleted a comment from codecov-commenter Apr 3, 2023
@databricks databricks deleted a comment from codecov-commenter Apr 3, 2023
@databricks databricks deleted a comment from codecov-commenter Apr 7, 2023
@databricks databricks deleted a comment from codecov-commenter Apr 7, 2023
@databricks databricks deleted a comment from codecov-commenter Apr 17, 2023
@databricks databricks deleted a comment from codecov-commenter Apr 25, 2023
Signed-off-by: Levko Kravets <levko.ne@gmail.com>
@kravets-levko kravets-levko marked this pull request as ready for review April 27, 2023 14:07
lib/DBSQLClient.ts Outdated Show resolved Hide resolved
public async closeAll() {
const items = [...this.items];
for (const item of items) {
await item.close(); // eslint-disable-line no-await-in-loop

Choose a reason for hiding this comment

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

Could using an async closure and Promise.all() avoid disabling the eslint warning?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I wasn't sure if that's safe to run all the close operations in parallel - if there will be a lot of objects in collection, server may reject some of them. So for me it looked safer to run them in sequence. Also, there's nothing wrong with disabling no-await-in-loop - docs even suggest doing that if developer is sure that promises should be executed in sequence

Copy link

@andrefurlan-db andrefurlan-db left a comment

Choose a reason for hiding this comment

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

there is something intrinsically complicated about this PR that makes me uncomfortable. I need to understand what is it that causes this to be this complicated.

@kravets-levko
Copy link
Contributor Author

@andrefurlan-db it's not that complicated as it looks. Brief idea of what's implemented here: we use DBSQLClient to create instances of DBSQLSession, which later runs some operations returning DBSQLOperation instances. Previously that "nested" object were created standalone. So if you, for example, close connection (which in our case doesn't really close anything because it's a nature of HTTP), or close session (which doesn't actully close session immediately because that's how our backend works) - it was still possible to use those nested objects. However, users expected other behavior - if you close session, all operations that belong to it should be closed as well; same for connection - when you close it, you should invalidate everything that belongs to it.

So what we do here: clients and sessions keep track of objects they create, and once being closed - they first close all owned object, and only then close themselves. It's very similar to what PySQL does, just a little bit more complicated: first because PySQL creates the only session under the hood so it's just a single level of nested objects, and secondly - because in Node a lot of things may happen asynchronously, and we need to handle that as accurately as possible.

https://github.com/databricks/databricks-sql-python/blob/d684bf8bf03601e0cc966f95a1d1868c0024d570/src/databricks/sql/client.py#L243-L248

@codecov-commenter
Copy link

Codecov Report

Patch coverage: 99.14% and project coverage change: +0.13% 🎉

Comparison is base (f265368) 94.98% compared to head (4b9579b) 95.11%.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #129      +/-   ##
==========================================
+ Coverage   94.98%   95.11%   +0.13%     
==========================================
  Files          57       58       +1     
  Lines        1076     1167      +91     
  Branches      178      191      +13     
==========================================
+ Hits         1022     1110      +88     
- Misses         21       22       +1     
- Partials       33       35       +2     
Files Changed Coverage Δ
lib/DBSQLOperation/index.ts 90.12% <96.77%> (+3.08%) ⬆️
lib/DBSQLClient.ts 87.95% <100.00%> (+0.77%) ⬆️
lib/DBSQLOperation/OperationStatusHelper.ts 97.87% <100.00%> (ø)
lib/DBSQLSession.ts 98.16% <100.00%> (+0.98%) ⬆️
lib/errors/OperationStateError.ts 100.00% <100.00%> (ø)
lib/utils/CloseableCollection.ts 100.00% <100.00%> (ø)

... and 1 file with indirect coverage changes

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link
Contributor

@nithinkdb nithinkdb left a comment

Choose a reason for hiding this comment

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

LGTM

if (!this._status.hasResultSet) {
return [];
}

await this._status.waitUntilReady(options);
await this.waitUntilReady(options);
Copy link
Contributor

Choose a reason for hiding this comment

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

cool, I've wanted this change for a while

@nithinkdb nithinkdb self-requested a review August 16, 2023 23:09
@kravets-levko kravets-levko merged commit c9e27f4 into main Aug 17, 2023
3 checks passed
@kravets-levko kravets-levko deleted the PECO-618-handle-closed-objects branch August 17, 2023 06:17
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.

5 participants