#50 Add database versioning #64
Conversation
d037284 to
162d4b4
Compare
There was a problem hiding this comment.
I've completed an initial review of this PR. I haven't yet dug deeply into the tests though.
Correct me if I'm wrong, but I think there's some critical logic missing within the SyncVersions method at the moment: if the process dies (power shutdown, killall, etc), then the database remains in a possibly corrupted state. To fix this, I think within the method, before doing any further processing, the current directory should be checked for existence of the back-up file. If found, then the the file should be restored and the function exiting early. Then in this scenario the user is required to start lnd again in order to ensure proper crash recovery.
Other than what I pointed out above, my comments are relatively minor, great job! With the infrastructure you've set up with this PR doing future migrations should be relatively streamlined in the future once we start doing stable releases.
There was a problem hiding this comment.
I think a comment long the lines of this may be a bit more accurate:
Migration is a function which takes a prior outdated version of the database instances and mutates the key/bucket structure to arrive at a more up-to-date version of the database.
There was a problem hiding this comment.
Also, I'm not sure this type needs to be externally exported. Database migrations should essentially be silent and obscured from the view outside of this package.
There was a problem hiding this comment.
Can you please use the full object initialiaztion syntax here? I think it makes this initialzaion a bit more clear.
So something like:
DBVersions = []Version{
Version{
number: 1,
migration: nil, // The base DB version requires no migration.
}
}There was a problem hiding this comment.
Like the others above, I don't think this variable needs to be publicly exported.
There was a problem hiding this comment.
Similar to the comment above, I don't think this struct needs to be publicly exported. One sign of this is that it has no public member variables and is method-less.
There was a problem hiding this comment.
Usage of named returns for functions is purposefully avoided across the project.
Can you modify this function to use regular returns? Thanks
There was a problem hiding this comment.
In order to make the migration process more robust and fault-tolerant, the entire migration process should take place within a single bolt database transaction.
There was a problem hiding this comment.
Doubling down on this statement, I don't think most of the additional logic in this PR is necessary. Additionally, I fear a scenario where a bug in the switchover+backup leads to full database corruption.
With the the single transaction approach, the entire process either is succesful, or fails. If we go with the approach currently implemented (and initially recommend by myself), then we can possibly end up in one of many intermediate states, requiring complex code to recognize and properly restore the state of the database to a stable state.
I think simply relying on bolt db's crash resistance+recovery is a safer, and simpler approach.
There was a problem hiding this comment.
Just a minor nit, but alternative syntax which achieves the same thing is:
var migrations []MigrationAlternatively, we could also do something like this which only results in a single allocation:
migrations := make([]Migration, 0, len(versions))There was a problem hiding this comment.
This method is missing a godoc-like comment.
There was a problem hiding this comment.
The error from this function is being ignored!
There was a problem hiding this comment.
There is no error from this function, because if db version not exist it's initialised with last db version number.
There was a problem hiding this comment.
Minor nit: to save a few lines, the meta variable and err can just be returned directly.
So:
return meta, errThere was a problem hiding this comment.
Since no further processing in the function takes place past updating the meta-data state, one can simply do:
return d.store.Update(func(tx *bolt.Tx) error {
....
})22c58ca to
0bce9be
Compare
Roasbeef
left a comment
There was a problem hiding this comment.
Aside from the minor nits I pointed out in the latest review patch, LGTM!
There was a problem hiding this comment.
Minor nit: there's an extra new-line here that can be deleted.
There was a problem hiding this comment.
Same nit here about the extra new-line.
There was a problem hiding this comment.
Minor nit: I think the variable name openChanBucket in this function and the one below should be renamed to metaBucket as that's the bucket actually being used in this context.
There was a problem hiding this comment.
The name in the godoc comment (TestGlobalVersionList) and the actual function name (TestOrderOfMigrations) are mismatched.
There was a problem hiding this comment.
It's not clear to me what these if-statements are actually doing.
It seems that they should be replaced with:
if migrations[0].number != 2 {
...
}
if migrations[1].number != 3 {
...
} There was a problem hiding this comment.
@Roasbeef I added additional comment in test. Basically what I am doing in this test is checking the order of migrations that will be applied, getMigrationsToApply function returns array of migration functions.
0bce9be to
d169fc1
Compare
d169fc1 to
502d3ee
Compare
In this commit the upgrade mechanism for database was added which makes he current schema rigid and upgradeable. Additional bucket 'metaBucket' was added which stores
key that house meta-data related to the current/version state of the database. 'createChannelDB' was modified to create this new bucket+key during initializing. Also
backup logic was added which makes a complete copy of the current database during migration process and restore the previous version of database if migration failed.
502d3ee to
2f5fd8d
Compare
Roasbeef
left a comment
There was a problem hiding this comment.
Nice, LGTM!
This'll prove super useful once we start to roll out stable releases 🚀
In this commit the:
metaBucketwas added which stores key that house meta-data related to the current/version state of the database.createChannelDBwas modified to create this new bucket+key during initialisation.