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

Generate database structure via migration #3436

Merged
merged 4 commits into from
Feb 7, 2024
Merged

Conversation

knolleary
Copy link
Member

Closes #3426

Description

As detailed in #3426, to enable scalability of the app we need to change how we handle migrations and the initialisation of the database.

When running on Postgres, the database migration logic that is applied when the app starts is now wrapped in a transaction that holds a lock on the MetaVersions table. This ensures two concurrently started apps do not both try to apply migrations.

With that fixed, the next issue is that we use sequelize.sync() to initialise the database. We have seen this taking 10+s in staging. In a scaled-out environment, having two instances running sync at the same time causes errors.

To fix that, this PR introduces a new 'baseline' migration that, if no tables exist, will create the entire database structure as expected today.

For a new install, the migration logic will skip 'old' migrations (as it does today), but then apply the baseline migration and any ones that follow.

For an existing install, the baseline migration will no-op as the tables already exist.

Creating this baseline migration has been quite the task. I'm describing the process here for future reference, but also to document what testing I've done.

I wrote some code to generate the bulk of it based on our models. I then used the DDL of each table from an sqlite instance to check all of the generated fields and fill in anything missing - including indexes.

I then ran the migration to generate a new database (sqlite), and manually compared the constraints, indexes and DDL of each table against an existing database.

I then ran against postgres.

Finally I ensured our unit tests passed cleanly against both sqlite and postgres.

Whoever reviews this, please do a test of running this up with a new sqlite and/org pg database and verify some basic ff functionality.

@@ -1,6 +1,9 @@
const should = require('should') // eslint-disable-line
const setup = require('../setup')

let objectCount = 0
const generateName = (root = 'object') => `${root}-${objectCount++}`
Copy link
Member Author

@knolleary knolleary Feb 2, 2024

Choose a reason for hiding this comment

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

The TeamType sequelize model doesn't have 'unique: true' set on the name field. This means, for a clean install, the constraint doesn't get applied and the tests pass.

However, the migration that was added to introduce TeamType does set the unique constraint - so that is what we have in production.

I chose to match production with our baseline - so the constraint is applied. So I had to update the tests to use unique names below.

Copy link

codecov bot commented Feb 2, 2024

Codecov Report

Attention: 11 lines in your changes are missing coverage. Please review.

Comparison is base (2331d72) 41.31% compared to head (7a43a52) 41.54%.
Report is 15 commits behind head on main.

Files Patch % Lines
forge/db/migrations/index.js 74.28% 9 Missing ⚠️
...tions/20240202-01-initialise-database-structure.js 96.00% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3436      +/-   ##
==========================================
+ Coverage   41.31%   41.54%   +0.22%     
==========================================
  Files         610      611       +1     
  Lines       22881    22944      +63     
  Branches     5524     5531       +7     
==========================================
+ Hits         9453     9531      +78     
+ Misses      13428    13413      -15     
Flag Coverage Δ
backend 78.43% <88.37%> (+0.25%) ⬆️
frontend 2.03% <0.00%> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

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

@hardillb
Copy link
Contributor

hardillb commented Feb 2, 2024

I've given everything but the 1000 line migration the once over and looks ok so far.

@knolleary knolleary requested a review from Pezmc February 2, 2024 16:58
Copy link
Contributor

@Pezmc Pezmc left a comment

Choose a reason for hiding this comment

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

I've done a full pass of the migration file and forge/db/migrations/index.js, nothing stood out of the ordinary including a comparison with my local PostgreSQL tables. That being said it's a super long migration, and not particularly human readable.

If this works as expected on temporary staging (create new), and real staging (no-op upgrade existing), I'd expect it to behave in production too, thus 👍

@knolleary knolleary merged commit 8b76c12 into main Feb 7, 2024
11 checks passed
@knolleary knolleary deleted the 3426-scalable-db-strategy branch February 7, 2024 12:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Scalability: Database Migration strategy
3 participants