-
-
Notifications
You must be signed in to change notification settings - Fork 4.8k
#4338 pg schema upgrade #4375
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
#4338 pg schema upgrade #4375
Conversation
I just retriggered the build, should be ok. I still need to compare this against the actual upgrade issue and verify that it does in fact rectify that issue. |
Codecov Report
@@ Coverage Diff @@
## master #4375 +/- ##
==========================================
+ Coverage 92.78% 92.79% +<.01%
==========================================
Files 118 118
Lines 8373 8384 +11
==========================================
+ Hits 7769 7780 +11
Misses 604 604
Continue to review full report at Codecov.
|
spec/PostgresStorageAdapter.spec.js
Outdated
|
||
adapter.createTable(className, schema) | ||
.then(() => { | ||
schema.fields.expiration_interval = { type:'Number' }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to be pedantic can we** getColumns** and check before the schema is upgraded to ensure that it's not present before hand?
return list; | ||
} | ||
|
||
const field = schema.fields[fieldName]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you elaborate on this change here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did not understand what you said with "elaborate on this change here".. :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, I'll reword it. Could you explain the reason you made the changes above here? I'm not entirely sure why you added this at first glance.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Specifically what about the prior functionality without ...!{}.hasOwnProperty.call(schema.fields, fieldName)..
wasn't working and needed to be changed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall this is super! I got around to checking the upgrade issue and this handles it without a problem 👍 . Just a test thing and a question from me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Excellent, thanks for making those changes.
I'm going to update against the master to factor in appveyor. No guarantees on that, as it may be still in the works for windows support...
@montymxb I need to do something here? |
@montymxb How are we staying here? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@paulovitin We're looking better now. Got caught up with other things on github and beyond 😄 . Didn't realize that the last merge against master corrected those issues we were seeing.
Two things are left. One is that the reported test coverage is not yet up to spec, currently at 89.47%. We'll need you to account for untested paths to bring that to 100% or as close as you reasonable can.
The second being the note about using transactions. As for those I'm actually not too familiar with whether it would be beneficial to implement them here. @vitaly-t, per @dplewis 's note about potentially utilizing transactions in this PR, do you think there would be an added benefit to doing so?
schemaUpgrade(className, schema) { | ||
debug('schemaUpgrade', { className, schema }); | ||
return this._client.tx('schemaUpgrade', t => | ||
t.any(`SELECT column_name FROM information_schema.columns WHERE table_name = '${className}'`) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do not format the values manually, use the query-formatting engine for it
) | ||
.then(columns => { | ||
if (!columns) { | ||
return Promise.resolve(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this condition will never happen, see the API for method any
.
return Promise.resolve(); | ||
} | ||
|
||
const currentColumns = columns.map(item => item.column_name); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
perhaps should just use method map
of the database instead.
|
||
columnsToCreate.forEach(fieldName => { | ||
const type = schema.fields[fieldName]; | ||
promise.push(this.addFieldIfNotExists(className, fieldName, type)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is bad connection usage, must use a transaction here.
promise.push(this.addFieldIfNotExists(className, fieldName, type)); | ||
}); | ||
|
||
return Promise.all(promise); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Never use Promise.all
on database operations, see: https://github.com/vitaly-t/pg-promise/wiki/Common-Mistakes#tasks-versus-rootdirect-queries
@paulovitin thanks for getting the coverage up! I'll take a look at these changes now... |
return this._client.tx('schemaUpgrade', t => { | ||
return t.any('SELECT column_name FROM information_schema.columns WHERE table_name = $<className>', { className }) | ||
.then(columns => { | ||
const currentColumns = columns.map(item => item.column_name); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So I see this is the only bit you didn't change here. As is this is passing our tests and looks good. Perhaps it's fine as is. @vitaly-t do you think this is important enough to change or are we good as is?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@vitaly-t Ding? Zing? Ping?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code should be replaced with ES6 generators, the way it was done for all tasks and transaction in the code.
@dplewis ping back at ya 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good. I want to double check on the one change that wasn't made before I can consider approving this.
@montymxb ok. If needs more changes, let me know |
spec/PostgresStorageAdapter.spec.js
Outdated
const getColumns = (client, className) => { | ||
return client.any('SELECT column_name FROM information_schema.columns WHERE table_name = $<className>', { className }) | ||
.then(columns => { | ||
if (!columns.length) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What you do in this .then
block is pointless, i.e. if the condition matches, the you already end up with an empty array.
spec/PostgresStorageAdapter.spec.js
Outdated
@@ -1,6 +1,17 @@ | |||
const PostgresStorageAdapter = require('../src/Adapters/Storage/Postgres/PostgresStorageAdapter'); | |||
const databaseURI = 'postgres://localhost:5432/parse_server_postgres_adapter_test_database'; | |||
|
|||
const getColumns = (client, className) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This whole method can be implemented with a single line, using method map.
return client.map('SELECT column_name FROM information_schema.columns WHERE table_name = $<className>', { className }, a => a.column_name);
Also, I'm not sure we should have methods that take client
as parameter like that. See how all the other methods are done, they take an optional connection context as the last parameter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a pretty simple one. We should setup map and save some lines 😉. You've already done quite a lot so let me know if you can get around to this @paulovitin . If not I can put in an additional commit myself.
debug('schemaUpgrade', { className, schema }); | ||
|
||
return this._client.tx('schemaUpgrade', t => { | ||
return t.any('SELECT column_name FROM information_schema.columns WHERE table_name = $<className>', { className }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Method map should be used here.
addFields.push(this.addFieldIfNotExists(className, fieldName, type)); | ||
}); | ||
|
||
if (!addFields.length) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inefficient use of promises here. Can simply skip returning anything for the same effect.
This needs a few updates + conflicts resolved. |
Correction. No, it needs a lot of rewriting, especially in the light of the conflicts. I have tried to update it, but the conflicts with the later changes stopped me from being able to finish. @paulovitin You need to resolve the conflicts before I can help with the code refactoring. |
Adds: - schemaUpgrade method which check the fields to add and delete from old schema;
Adds: - Add PostgresStorageAdapter.schemaUpgrade spec tests: creates a table, simulates the addition of a new field and checks if its present in the database Chores: - ESLint eol-last fix;
rewriting method schemaUpgrade
Ok, I'm done here, for now :) @paulovitin I hope I didn't miss anything ;) |
@montymxb I've done my part here, do you want to re-review it? 😄 |
LGTM! |
@vitaly-t absolutely 👍 . Thanks for taking a look, postgres via node isn't exactly one of my strong suites 😆 . |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Relevant notes are in the review. You've been very helpful with accommodating to our requests, just wanted to thank you on that 😄 . If you think you can get around to these last ones here I believe we should be at a point to take this in. I would be willing to put in the last set of changes, should it be necessary.
Code is functional and tests indicate proper behavior. Anything that is non-critical can be applied in later PRs once we have this in. Big deal here is making sure we get this in place to prevent upgrade crashes as originally noticed.
spec/PostgresStorageAdapter.spec.js
Outdated
@@ -1,6 +1,17 @@ | |||
const PostgresStorageAdapter = require('../src/Adapters/Storage/Postgres/PostgresStorageAdapter'); | |||
const databaseURI = 'postgres://localhost:5432/parse_server_postgres_adapter_test_database'; | |||
|
|||
const getColumns = (client, className) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a pretty simple one. We should setup map and save some lines 😉. You've already done quite a lot so let me know if you can get around to this @paulovitin . If not I can put in an additional commit myself.
spec/PostgresStorageAdapter.spec.js
Outdated
@@ -19,4 +30,100 @@ describe_only_db('postgres')('PostgresStorageAdapter', () => { | |||
expect(adapter._client.$pool.ending).toEqual(true); | |||
done(); | |||
}); | |||
|
|||
it('schemaUpgrade, upgrade the database schema when schema change', done => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a typo
upgrade the database schema when the schema changes
spec/PostgresStorageAdapter.spec.js
Outdated
.catch(error => done.fail(error)); | ||
}); | ||
|
||
it('schemaUpgrade, matain correct schema', done => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo here too
maintains correct schema
@@ -1579,14 +1595,17 @@ export class PostgresStorageAdapter { | |||
} | |||
|
|||
performInitialization({ VolatileClassesSchemas }) { | |||
// TODO: This method needs to be rewritten to make proper use of connections (@vitaly-t) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Regarding this TODO we'll want to at least rework this in with transactions as mentioned below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will do it 😉
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DONE!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P.S. As for the last item, the TODO
thing, it definitely should be done as a separate PR. I will take care of it after this PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Noted! @paulovitin you're good for that one then, we'll address that down the road.
A-a-and we're back into conflicts 😢 While we were fixing this PR, the code was turned into some sort of TypeScript, holly 💩 ! 😂 @paulovitin man, your PR is jinxed 😂 |
Hmm, probably related to #4349 being recently merged? |
@vitaly-t yup, intoduced types for the DB adapters and I had many conflicts to survive as well :) |
Well, I volunteer @paulovitin to resolve the conflicts yet again 😂 I'm not doing it 😂 I'm drinking to the New Year already, as I am Russian 😄 🍻 |
@vitaly-t we have a very diverse team. New Years Goal: let’s get the team together sometime. Florent set it up. Let’s go to a conference or something |
@vitaly-t no need to do it! Just fixed them from the phone :) |
We have plenty of contrived on open collective, we could probab’y do something to help everyone get there :) |
@flovilmart Make it happen captain |
I did re-run of those flimsy tests a number of times till they finally all came through 😂 |
const getColumns = (client, className) => { | ||
return client.map('SELECT column_name FROM information_schema.columns WHERE table_name = $<className>', { className }, a => a.column_name); | ||
}; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Didn't realize the above was finally changed, alright!
@@ -19,4 +23,100 @@ describe_only_db('postgres')('PostgresStorageAdapter', () => { | |||
expect(adapter._client.$pool.ending).toEqual(true); | |||
done(); | |||
}); | |||
|
|||
it('schemaUpgrade, upgrade the database schema when schema changes', done => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo fixed...
.catch(error => done.fail(error)); | ||
}); | ||
|
||
it('schemaUpgrade, maintain correct schema', done => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo fixed...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sweet, thanks @vitaly-t for those commits. Finally took a look and was going to do it myself, only to see it's already been done!
All the nits from before look to be taken care of.
Thanks again @paulovitin for putting up the PR! |
* Method to upgrade schemas in Postgres; Adds: - schemaUpgrade method which check the fields to add and delete from old schema; * Remove the columns delete in schemaUpgrade method; * ESLint fix and PostgresStorageAdapter.schemaUpgrade spec test Adds: - Add PostgresStorageAdapter.schemaUpgrade spec tests: creates a table, simulates the addition of a new field and checks if its present in the database Chores: - ESLint eol-last fix; * Add check columns before and after schema upgrade, and remove the unnecessary piece of code Add: - Check the right columns is present before schema upgrade and the new field is not, then check if the right columns is present and the new field; Remove: - Piece of code unnecessary because it not need to remove diff columns; * Optimize the schemaUpgrade method following @vitaly-t instructions, and more tests; * If the class does not have any columns and needs an upgrade the code would return without doing so. Fixed this. Chore: - Allows class with no column to be upgraded; - Test for class with no columns being upgraded; * Update PostgresStorageAdapter.js rewriting method schemaUpgrade * Update PostgresStorageAdapter.spec.js * Update PostgresStorageAdapter.spec.js
Fix this problem #4338
When the Postgres Storage Adapter perform the initialization, and the "Volatile Classes Schemas" perform the table creation, the schemaUpgrade method execute a diff between database columns and the Schemas in code. If has new columns, create then through method addFieldIfNotExists.