Skip to content

Commit

Permalink
upgrade db: support defining min. and max. database schema version
Browse files Browse the repository at this point in the history
Instead of requiring an up2date database schema allow to define a
minimum and maximum compatible database schema version.
The max. schema version is always the newest existing one at the time of
a release. The minimum schema version can be an older one if it is still
compatible.

This can make upgrading baur easier, when db schema changes are
downwards compatible, multiple baur version can be used with the same
database schema.
Even better would be to use a semantic version as schema version.
This would require more work though :-)
  • Loading branch information
fho committed Nov 11, 2024
1 parent 2684cbe commit 8ec4ac1
Show file tree
Hide file tree
Showing 5 changed files with 46 additions and 13 deletions.
6 changes: 3 additions & 3 deletions internal/command/upgrade_db.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,17 +84,17 @@ func (*upgradeDbCmd) run(_ *cobra.Command, args []string) {
}
exitOnErr(err, "querying database schema version failed")

if curVer == clt.RequiredSchemaVersion() {
if curVer == clt.MaxSchemaVersion() {
stdout.Println("database schema is already up to date, nothing to do")
return
}

if curVer > clt.RequiredSchemaVersion() {
if curVer > clt.MaxSchemaVersion() {
fatal("database schema is from a newer baur version, please update baur")
}

err = clt.Upgrade(ctx)
exitOnErr(err, "upgrading database schema failed")

stdout.Printf("database schema successfully upgraded from version %d to %d\n", curVer, clt.RequiredSchemaVersion())
stdout.Printf("database schema successfully upgraded from version %d to %d\n", curVer, clt.MaxSchemaVersion())
}
2 changes: 1 addition & 1 deletion internal/command/upgrade_db_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,5 +64,5 @@ func TestUpgradeDb(t *testing.T) {
upgradeDbCmd := newUpgradeDatabaseCmd()
upgradeDbCmd.Command.Run(&upgradeDbCmd.Command, nil)

assert.Contains(t, stdoutBuf.String(), "database schema successfully upgraded from version 1 to 4")
assert.Contains(t, stdoutBuf.String(), "database schema successfully upgraded from version 1 to 5")
}
20 changes: 14 additions & 6 deletions pkg/storage/postgres/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,12 @@ import (
"github.com/simplesurance/baur/v5/pkg/storage"
)

// schemaVer is the database schema version required by this package.
const schemaVer int32 = 4
const (
// minSchemaVer is the minimum required database schema version
minSchemaVer int32 = 4
// maxSchemaVer is the highest database schema version that is compatible
maxSchemaVer int32 = 5
)

// migration represents a database schema migration.
type migration struct {
Expand Down Expand Up @@ -220,8 +224,12 @@ func (c *Client) ensureSchemaIsCompatible(ctx context.Context) error {
return nil
}

if ver != schemaVer {
return fmt.Errorf("database schema version is not compatible with baur version, schema version: %d, expected version: %d", ver, schemaVer)
if ver < minSchemaVer || ver > maxSchemaVer {
if minSchemaVer == maxSchemaVer {
return fmt.Errorf("database schema version is not compatible with baur version, schema version: %d, expecting version: %d", ver, minSchemaVer)
}

return fmt.Errorf("database schema version is not compatible with baur version, schema version: %d, expecting schema version >=%d and <=%d", ver, minSchemaVer, maxSchemaVer)
}

return nil
Expand Down Expand Up @@ -282,6 +290,6 @@ func (c *Client) SchemaVersion(ctx context.Context) (int32, error) {
return c.schemaVersion(ctx)
}

func (c *Client) RequiredSchemaVersion() int32 {
return schemaVer
func (c *Client) MaxSchemaVersion() int32 {
return maxSchemaVer
}
24 changes: 24 additions & 0 deletions pkg/storage/postgres/schema_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,30 @@ func TestIsCompatible_OldBaurSchemaExist(t *testing.T) {
require.Contains(t, err.Error(), "incompatible database schema")
}

func TestIsCompatible_MinVer(t *testing.T) {
client, cleanupFn := newTestClient(t)
t.Cleanup(cleanupFn)

require.NoError(t, client.Init(ctx))

_, err := client.db.Exec(ctx, "UPDATE migrations set schema_version = $1", minSchemaVer)
require.NoError(t, err)

require.NoError(t, client.IsCompatible(ctx))
}

func TestIsCompatible_MaxVer(t *testing.T) {
client, cleanupFn := newTestClient(t)
t.Cleanup(cleanupFn)

require.NoError(t, client.Init(ctx))

_, err := client.db.Exec(ctx, "UPDATE migrations set schema_version = $1", maxSchemaVer)
require.NoError(t, err)

require.NoError(t, client.IsCompatible(ctx))
}

func TestIsCompatible_SchemaVersionDoesNotMatch(t *testing.T) {
client, cleanupFn := newTestClient(t)
defer cleanupFn()
Expand Down
7 changes: 4 additions & 3 deletions pkg/storage/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,10 @@ type Storer interface {
// SchemaVersion returns the version of the schema that the storage is
// using.
SchemaVersion(ctx context.Context) (int32, error)
// RequiredSchemaVersion returns the schema version that the Storer
// implementation requires.
RequiredSchemaVersion() int32
// MaxSchemaVersion returns the max. supported database schema version.
// It is also the version to that [Upgrade] can migrate the current
// schema.
MaxSchemaVersion() int32
// IsCompatible verifies that the storage is compatible with the baur version
IsCompatible(context.Context) error
// Upgrade upgrades the schema to RequiredSchemaVersion().
Expand Down

0 comments on commit 8ec4ac1

Please sign in to comment.