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

remove bluebird #3650

Closed
wants to merge 20 commits into from
Closed

remove bluebird #3650

wants to merge 20 commits into from

Conversation

maximelkin
Copy link
Collaborator

@maximelkin maximelkin commented Feb 5, 2020

Closing #1588

@maximelkin
Copy link
Collaborator Author

Wait for #3634

@maximelkin
Copy link
Collaborator Author

todo one of:

  1. moving tests from bluebird
  2. global or knex instance promise lib setup
    2.1) adding methods in runtime vs adding another level of prototype at knex instantiation
    2.2) searching for all external async api methods and wrapping with user defined lib

lib/client.js Outdated
});
} catch (e) {
return Bluebird.reject(e);
return Promise.reject(new Error('Unable to acquire a connection'));
Copy link

@qubyte qubyte Feb 14, 2020

Choose a reason for hiding this comment

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

No need for an explicit Promise.reject here. Since this is an async function, all you have to do is throw the error and the caller will see the promise this function returns become rejected.

Similar for the promise chain below. Better to await etc.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done

@briandamaged
Copy link
Collaborator

Howdy! Just curious: what's the intended scope of this PR?

  1. Remove the dependency on the Bluebird library?
  2. Rewrite existing logic to take advantage of async / await?
  3. Something else?

The reason I'm asking: this seems like it could potentially touch a lot of code, which will make it challenging to code review. So, perhaps the overall effort can be split into several smaller PRs?

@kibertoad
Copy link
Collaborator

I agree with the idea of smaller PRs being better and this is something we should in general strive for; however, at this point it's likely that PR split is going to take non-trivial effort; if that is the case, I'd rather review a larger PR than lose part of refactoring due to simple revert.

@kibertoad
Copy link
Collaborator

That said, since PR is pretty large already, it might make sense to wrap up what was already changed into final state, review, merge and continue in a separate PR, rather than keep expanding this one.

@maximelkin
Copy link
Collaborator Author

maximelkin commented Feb 21, 2020

In some places conversion from promise chain to async/await done because of node 8 support and .finally usage
If we can remove node 8 support, some changes can be reverted to make pr smaller

lib/interface.js Outdated Show resolved Hide resolved
@maximelkin maximelkin marked this pull request as ready for review February 21, 2020 11:24
@maximelkin maximelkin changed the title WIP: remove bluebird remove bluebird Feb 21, 2020
@briandamaged
Copy link
Collaborator

In terms of the "smaller PR" discussion: we don't necessarily need to revert any changes. Instead, we could use this branch as a basis for generating smaller incremental patches:

https://stackoverflow.com/questions/28192623/create-patch-or-diff-file-from-git-repository-and-apply-it-to-another-different

For example:

  1. Generate a patch that includes only "trivial changes" within the /lib folder. (Ex: files where you only had to rename Bluebird.resolve to Promise.resolve, etc. Also: notice that the /test folder is remaining untouched. This will give us confidence that the new code is still working correctly)
  2. Generate a patch that includes some of the "nontrivial changes" within the /lib folder. (Ex: places where code had to be rewritten because it relied upon a Bluebird feature. Once again: the /test folder remains untouched). You might need to repeat this step depending upon the complexity of the changes.
  3. Generate a patch for the /lib folder.

There might be more steps as well, of course. The main concept: you don't need to manually recreate the changes. Instead, you can use this branch as the basis for generating multiple patch sets. This will then make it significantly easier to review and merge the code.

I can assist you with this if you'd like to give it a try.

@briandamaged
Copy link
Collaborator

Side note: I also see why this is such a challenging PR. I just replaced a Bluebird Promise in 1 line of code, and suddenly 15 seemingly-unrelated sections of the code exploded. 🙀

It turns out that those sections of the code all relied upon the .finally(..) method at some point within their control flow. So, I temporarily re-wrapped the Promise w/ Bluebird.resolve(..) just to work around the issue.

test/utils.js Outdated
@@ -0,0 +1,8 @@
const assert = require('assert');
Copy link
Collaborator

Choose a reason for hiding this comment

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

could be chai for consistency

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

code removed

t.ok(
e.message.indexOf('i-refuse-to-exist') > 0,
e.message.includes('i-refuse-to-exist'),
Copy link
Collaborator

Choose a reason for hiding this comment

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

nice :)

@kibertoad
Copy link
Collaborator

Preferably we keep Node 8 support until Node 14 lands.

test/utils.js Outdated
const assert = require('assert');

const expectError = (promise) =>
promise.then(() => assert.fail("Shouldn't have gotten here."), (e) => e);
Copy link
Collaborator

Choose a reason for hiding this comment

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

You might also consider using the chai-as-promised plugin:

https://www.chaijs.com/plugins/chai-as-promised/

This encapsulates the pattern of "failing when an exception didn't occur".

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

done

};
}
);
availablePromiseMethods().forEach(function(method) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

The list produced by availablePromiseMethods() is significantly shorter than the list that it's replacing. Was this intentional? (Granted: I'm assuming that many of the methods in the original list were Bluebird-specific)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes, this methods are from bluebird


then(onResolve, onReject) {
return this._promise.then(onResolve, onReject);
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

FYI: this is probably related to the "availablePromiseMethods() list is significantly shorter than the list that it's replacing" concern that I mentioned elsewhere. The availablePromiseMethods() method is currently omitting "then".

In other words: if the list produced by availablePromiseMethods() included "then", then you probably could just rely upon meta-programming to generate this method automatically.

(On the flipside: given that the list of Promise methods only contains 2-3 elements now, it might no longer be worth leveraging meta-programming for this.)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This will not work, because interface has non-trivial then (but this can be replaced to some internal method too, but this too complex

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

helper which generates array of promise methods helps with optional .finally

Copy link
Collaborator

Choose a reason for hiding this comment

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

Right -- we're saying the same thing. Basically: you can't use meta-programming for this specific case. However, given that there are only a handful of cases now, it might be worth discarding the meta-programming in favor of something more explicit.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

done: 9ef6369
Sorry for delay, didn't quickly realize your proposed solution

@@ -0,0 +1,5 @@
function availablePromiseMethods() {
return Promise.prototype.finally ? ['catch', 'finally'] : ['catch'];
Copy link
Collaborator

Choose a reason for hiding this comment

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

Hmm... based upon the Knex documentation, it looks like the .finally(..) method is intentionally being made available to older versions of Node. (Specifically: release notes for version 0.20.2). So, perhaps we should eliminate the conditional logic here and just always provide the .finally(..) method? (Of course, the meta-programming might interfere with this. So... perhaps we should avoid meta-programming for generating these methods?)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

0.20.2 is about typings only, as I can see

Copy link
Collaborator

Choose a reason for hiding this comment

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

These release notes for 0.20.2 on http://knexjs.org/ mention this:

Fix regression in older version of node when Promise#finally was not available #3507

(Perhaps this line was omitted from another copy of the release notes?)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I checked #3507 pr

@maximelkin
Copy link
Collaborator Author

I can't change lib/ code without changing test/, because tests depends on .finally, which is incompatible with node 8

this.driver.connect(this.connectionSettings, (err, connection) => {
if (err) return rejecter(err);
Bluebird.promisifyAll(connection);
connection.executeAsync = promisify(connection.execute);
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

here attention needed: check all file and notice, what from connection only execute (with suffix async) is used

Copy link
Collaborator

Choose a reason for hiding this comment

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

FYI: I discovered earlier today that lib/dialects/oracle is actually partially-dead code. It is not used anywhere except as a parent class for lib/dialects/oracledb.

So, you might want to double-check that this method is even used by lib/dialects/oracledb. If not, then it might be easiest to just remove the entire method.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It's easier to drop support for https://www.npmjs.com/package/oracle entirely, but this is not logical part of this merge request

function(value, index) {
const OutBindsOffset = index * modifiedRowsCount;
updatedOutBinds.push(outBinds[i + OutBindsOffset]);
return connection
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

github is bad at diffing with spaces: here only Bluebird.resolve removed

@@ -1148,39 +1147,37 @@ module.exports = function(knex) {
if (/redshift/i.test(knex.client.driverName)) {
return;
}
this.timeout(10000);
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

previously this was timeout on bluebird chain

@briandamaged
Copy link
Collaborator

@maximelkin + @kibertoad : I've been reviewing the code, but the huge number of changes makes it difficult to be very confident about the code review (fun fact: it's actually 63 pages worth of changes). Would it be possible to split this into at least 2 separate PRs? In particular:

  1. The first PR contains only the revisions to the /test code. This way, we can at least be confident that the old code works with the new test cases. (In other words: this helps us reduce the possibility of having 2 bugs that cancel one another out. Ex: a bug in the /src code that gets ignored due to a bug in the /test code)
  2. The second PR contains the revisions to the actual code. (And ideally: no more changes to the test cases. This way, we can be confident that the code is doing the same thing before and after the changes)

(Ideally, I'd really like to see each of these split into 2-3 separate PRs as well. But, splitting things this way will at least allow us to be a lot more confident in the code reviews).

In terms of strategy, you can do the following:

  1. Create a copy of your branch, and rebase it against master:
git checkout bluebird_removal
git checkout -b bluebird_removal-rebase
git rebase -p master

(This will replay your commits against master, which ensures that we'll get a clean diff in the next step)

  1. Create a patch of the changes against master:
git diff master > bluebird_removal.patch
  1. Switch back to master, and apply only the test-related changes from the patch:
git checkout master
git apply --include="test/*" bluebird_removal.patch
  1. Reintroduce the chai-as-promised dependency to package.json. (Note: we can't use the branch's package.json since it also removed bluebird)
npm install --save-dev chai-as-promised
  1. Create a new branch for these changes:
git checkout -b bluebird_removal-tests_only

At this point, it should be possible to run the tests against the older code. Once we're confident that the tests are good, we can merge them. Afterwards, we can apply the patch to create a new PR that contains the other half of the changes:

# Note: We're assuming that the test changes have already been merged into master
git checkout master
git apply --exclude="test/*" --exclude="package.json" bluebird_removal.patch

Again: I apologize for asking you to do this, but it would really allow everybody to be a lot more confident about the changes that were implemented.

@maximelkin
Copy link
Collaborator Author

maximelkin commented Feb 23, 2020

Split on 2 prs
#3683
#3682

@maximelkin maximelkin closed this Feb 23, 2020
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.

None yet

4 participants