Skip to content

Add migration that brings line column to the migration table - #435

Merged
brandur merged 1 commit into
masterfrom
brandur-migration-line
Jul 9, 2024
Merged

Add migration that brings line column to the migration table#435
brandur merged 1 commit into
masterfrom
brandur-migration-line

Conversation

@brandur

@brandur brandur commented Jul 8, 2024

Copy link
Copy Markdown
Contributor

Here, add a new River migration (version 005) that brings a line
column to the river_migration table, allowing non-main lines to be
supported. We also teach the migrator how to use it.

This one is a little trickier than it sounds (partly thanks to sqlc)
because the migrator needs to behave somewhat differently depending on
whether the line column exits yet. So for example, when migrating to
004 it needs to upsert migration records that do not include a line
field, but then when migrating to 005 or beyond, a line value is
included. The same consideration goes for performing a down migration,
or checking the existing state of migrations in the database.

This is implemented by pairing driver functions related to migrations
into one for pre-line and another for post-line. e.g.

// MigrationDeleteAssumingMainMany deletes many migrations assuming
// everything is on the main line. This is suitable for use in databases on
// a version before the `line` column exists.
MigrationDeleteAssumingMainMany(ctx context.Context, versions []int) ([]*Migration, error)

// MigrationDeleteByLineAndVersionMany deletes many migration versions on a
// particular line.
MigrationDeleteByLineAndVersionMany(ctx context.Context, line string, versions []int) ([]*Migration, error)

We don't yet pull line support into the CLI, but this should make
alternate lines fully supported up to that point. The CLI needs a little
more thought because it might involve building a separate binary.

@brandur
brandur force-pushed the brandur-migration-line branch from f0cdbc1 to dd43d26 Compare July 8, 2024 03:42
@brandur
brandur requested a review from bgentry July 8, 2024 03:45
@brandur
brandur force-pushed the brandur-migration-line branch from dd43d26 to 9cf3342 Compare July 8, 2024 05:48
Comment thread rivermigrate/river_migrate.go Outdated
// the migration `line` comes in. If migration to a point equal or above
// 005, we can remove migrations with a line included, but otherwise we
// must omit the `line` column from queries because it doesn't exist.
if m.line == riverdriver.MigrationLineMain && slices.Min(versions) <= 5 {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The value 5 here might be clearer if put in a named constant

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yep, good call. Changed.

Here, add a new River migration (version 005) that brings a `line`
column to the `river_migration` table, allowing non-main lines to be
supported. We also teach the migrator how to use it.

This one is a little trickier than it sounds (partly thanks to sqlc)
because the migrator needs to behave somewhat differently depending on
whether the `line` column exits yet. So for example, when migrating to
004 it needs to upsert migration records that _do not_ include a `line`
field, but then when migrating to 005 or beyond, a `line` value is
included. The same consideration goes for performing a down migration,
or checking the existing state of migrations in the database.

This is implemented by pairing driver functions related to migrations
into one for pre-`line` and another for post-`line`. e.g.

    // MigrationDeleteAssumingMainMany deletes many migrations assuming
    // everything is on the main line. This is suitable for use in databases on
    // a version before the `line` column exists.
    MigrationDeleteAssumingMainMany(ctx context.Context, versions []int) ([]*Migration, error)

    // MigrationDeleteByLineAndVersionMany deletes many migration versions on a
    // particular line.
    MigrationDeleteByLineAndVersionMany(ctx context.Context, line string, versions []int) ([]*Migration, error)

We don't yet pull line support into the CLI, but this should make
alternate lines fully supported up to that point. The CLI needs a little
more thought because it might involve building a separate binary.
@brandur
brandur force-pushed the brandur-migration-line branch from 9cf3342 to c815198 Compare July 9, 2024 00:28
@brandur

brandur commented Jul 9, 2024

Copy link
Copy Markdown
Contributor Author

Thanks!

@brandur
brandur merged commit 09ac554 into master Jul 9, 2024
@brandur
brandur deleted the brandur-migration-line branch July 9, 2024 00:31
brandur added a commit that referenced this pull request Jul 11, 2024
I'd totally forgotten I'd even created it this way, but going back
through migrations recently I realized that the table's primary key is a
`bigserial`.

This isn't totally bad, and even has some advantages in that rows are
easier to target from a psql session, but in this instance it feels a
little on the sloppy side because it's fairly easy to conflate the
serial with migration `version` -- both are expected to be small
integers that a human could easily mix up.

Since we haven't shipped a new version yet containing the addition of
`line` in #435, here I propose that we modify that a little further to
reshape `river_migration` into a slightly smaller table with a compound
key on `(line, version)`:

    CREATE TABLE river_migration(
        created_at timestamptz NOT NULL DEFAULT NOW(),
        line TEXT NOT NULL,
        version bigint NOT NULL,
        CONSTRAINT line_length CHECK (char_length(line) > 0 AND char_length(line) < 128),
        CONSTRAINT version_gte_1 CHECK (version >= 1),
        PRIMARY KEY (line, version)
    );

This has two tiny side benefits that (1) the table is smaller (drops a
column), and (2) we don't need an extra index on `(line, version)` since
it's already covered by the primary key. Very similar in spirit to what
it was before, but I think a little cleaner in design.
brandur added a commit that referenced this pull request Jul 13, 2024
I'd totally forgotten I'd even created it this way, but going back
through migrations recently I realized that the table's primary key is a
`bigserial`.

This isn't totally bad, and even has some advantages in that rows are
easier to target from a psql session, but in this instance it feels a
little on the sloppy side because it's fairly easy to conflate the
serial with migration `version` -- both are expected to be small
integers that a human could easily mix up.

Since we haven't shipped a new version yet containing the addition of
`line` in #435, here I propose that we modify that a little further to
reshape `river_migration` into a slightly smaller table with a compound
key on `(line, version)`:

    CREATE TABLE river_migration(
        created_at timestamptz NOT NULL DEFAULT NOW(),
        line TEXT NOT NULL,
        version bigint NOT NULL,
        CONSTRAINT line_length CHECK (char_length(line) > 0 AND char_length(line) < 128),
        CONSTRAINT version_gte_1 CHECK (version >= 1),
        PRIMARY KEY (line, version)
    );

This has two tiny side benefits that (1) the table is smaller (drops a
column), and (2) we don't need an extra index on `(line, version)` since
it's already covered by the primary key. Very similar in spirit to what
it was before, but I think a little cleaner in design.
brandur added a commit that referenced this pull request Jul 13, 2024
I'd totally forgotten I'd even created it this way, but going back
through migrations recently I realized that the table's primary key is a
`bigserial`.

This isn't totally bad, and even has some advantages in that rows are
easier to target from a psql session, but in this instance it feels a
little on the sloppy side because it's fairly easy to conflate the
serial with migration `version` -- both are expected to be small
integers that a human could easily mix up.

Since we haven't shipped a new version yet containing the addition of
`line` in #435, here I propose that we modify that a little further to
reshape `river_migration` into a slightly smaller table with a compound
key on `(line, version)`:

    CREATE TABLE river_migration(
        created_at timestamptz NOT NULL DEFAULT NOW(),
        line TEXT NOT NULL,
        version bigint NOT NULL,
        CONSTRAINT line_length CHECK (char_length(line) > 0 AND char_length(line) < 128),
        CONSTRAINT version_gte_1 CHECK (version >= 1),
        PRIMARY KEY (line, version)
    );

This has two tiny side benefits that (1) the table is smaller (drops a
column), and (2) we don't need an extra index on `(line, version)` since
it's already covered by the primary key. Very similar in spirit to what
it was before, but I think a little cleaner in design.
brandur added a commit that referenced this pull request Jul 13, 2024
I'd totally forgotten I'd even created it this way, but going back
through migrations recently I realized that the table's primary key is a
`bigserial`.

This isn't totally bad, and even has some advantages in that rows are
easier to target from a psql session, but in this instance it feels a
little on the sloppy side because it's fairly easy to conflate the
serial with migration `version` -- both are expected to be small
integers that a human could easily mix up.

Since we haven't shipped a new version yet containing the addition of
`line` in #435, here I propose that we modify that a little further to
reshape `river_migration` into a slightly smaller table with a compound
key on `(line, version)`:

    CREATE TABLE river_migration(
        created_at timestamptz NOT NULL DEFAULT NOW(),
        line TEXT NOT NULL,
        version bigint NOT NULL,
        CONSTRAINT line_length CHECK (char_length(line) > 0 AND char_length(line) < 128),
        CONSTRAINT version_gte_1 CHECK (version >= 1),
        PRIMARY KEY (line, version)
    );

This has two tiny side benefits that (1) the table is smaller (drops a
column), and (2) we don't need an extra index on `(line, version)` since
it's already covered by the primary key. Very similar in spirit to what
it was before, but I think a little cleaner in design.
brandur added a commit that referenced this pull request Jul 14, 2024
Here, break out the CLI so that most of its implementation no longer
lives in a `main.go`, and rather in a subpackage that can be imported
from somewhere else. This lets the majority of it to be reused in
another executable and augmented in various ways.

An addition is a new `DriverProcurer` interface that can provide a
driver for various databases:

    type DriverProcurer interface {
        ProcurePgxV5(pool *pgxpool.Pool) riverdriver.Driver[pgx.Tx]
    }

For the main CLI, this gets a trivial implementation using `riverpgxv5`,
but could potentially be reimplemented elsewhere to swap in something
else;

    type DriverProcurer struct{}

    func (p *DriverProcurer) ProcurePgxV5(pool *pgxpool.Pool) riverdriver.Driver[pgx.Tx] {
            return riverpgxv5.New(pool)
    }

To make this more workable, I end up going through and refactoring quite
a lot of code, making it into a bit of a mini framework, and one that
could potentially support additional databases in the future without
having to refactor the world again.

We also add a `--line` flag to support the feature from #435 to the CLI.
brandur added a commit that referenced this pull request Jul 14, 2024
Here, break out the CLI so that most of its implementation no longer
lives in a `main.go`, and rather in a subpackage that can be imported
from somewhere else. This lets the majority of it to be reused in
another executable and augmented in various ways.

An addition is a new `DriverProcurer` interface that can provide a
driver for various databases:

    type DriverProcurer interface {
        ProcurePgxV5(pool *pgxpool.Pool) riverdriver.Driver[pgx.Tx]
    }

For the main CLI, this gets a trivial implementation using `riverpgxv5`,
but could potentially be reimplemented elsewhere to swap in something
else;

    type DriverProcurer struct{}

    func (p *DriverProcurer) ProcurePgxV5(pool *pgxpool.Pool) riverdriver.Driver[pgx.Tx] {
            return riverpgxv5.New(pool)
    }

To make this more workable, I end up going through and refactoring quite
a lot of code, making it into a bit of a mini framework, and one that
could potentially support additional databases in the future without
having to refactor the world again.

We also add a `--line` flag to support the feature from #435 to the CLI.
brandur added a commit that referenced this pull request Jul 14, 2024
Here, break out the CLI so that most of its implementation no longer
lives in a `main.go`, and rather in a subpackage that can be imported
from somewhere else. This lets the majority of it to be reused in
another executable and augmented in various ways.

An addition is a new `DriverProcurer` interface that can provide a
driver for various databases:

    type DriverProcurer interface {
        ProcurePgxV5(pool *pgxpool.Pool) riverdriver.Driver[pgx.Tx]
    }

For the main CLI, this gets a trivial implementation using `riverpgxv5`,
but could potentially be reimplemented elsewhere to swap in something
else;

    type DriverProcurer struct{}

    func (p *DriverProcurer) ProcurePgxV5(pool *pgxpool.Pool) riverdriver.Driver[pgx.Tx] {
            return riverpgxv5.New(pool)
    }

To make this more workable, I end up going through and refactoring quite
a lot of code, making it into a bit of a mini framework, and one that
could potentially support additional databases in the future without
having to refactor the world again.

We also add a `--line` flag to support the feature from #435 to the CLI.
brandur added a commit that referenced this pull request Jul 14, 2024
Here, break out the CLI so that most of its implementation no longer
lives in a `main.go`, and rather in a subpackage that can be imported
from somewhere else. This lets the majority of it to be reused in
another executable and augmented in various ways.

An addition is a new `DriverProcurer` interface that can provide a
driver for various databases:

    type DriverProcurer interface {
        ProcurePgxV5(pool *pgxpool.Pool) riverdriver.Driver[pgx.Tx]
    }

For the main CLI, this gets a trivial implementation using `riverpgxv5`,
but could potentially be reimplemented elsewhere to swap in something
else;

    type DriverProcurer struct{}

    func (p *DriverProcurer) ProcurePgxV5(pool *pgxpool.Pool) riverdriver.Driver[pgx.Tx] {
            return riverpgxv5.New(pool)
    }

To make this more workable, I end up going through and refactoring quite
a lot of code, making it into a bit of a mini framework, and one that
could potentially support additional databases in the future without
having to refactor the world again.

We also add a `--line` flag to support the feature from #435 to the CLI.
brandur added a commit that referenced this pull request Jul 14, 2024
Here, break out the CLI so that most of its implementation no longer
lives in a `main.go`, and rather in a subpackage that can be imported
from somewhere else. This lets the majority of it to be reused in
another executable and augmented in various ways.

An addition is a new `DriverProcurer` interface that can provide a
driver for various databases:

    type DriverProcurer interface {
        ProcurePgxV5(pool *pgxpool.Pool) riverdriver.Driver[pgx.Tx]
    }

For the main CLI, this gets a trivial implementation using `riverpgxv5`,
but could potentially be reimplemented elsewhere to swap in something
else;

    type DriverProcurer struct{}

    func (p *DriverProcurer) ProcurePgxV5(pool *pgxpool.Pool) riverdriver.Driver[pgx.Tx] {
            return riverpgxv5.New(pool)
    }

To make this more workable, I end up going through and refactoring quite
a lot of code, making it into a bit of a mini framework, and one that
could potentially support additional databases in the future without
having to refactor the world again.

We also add a `--line` flag to support the feature from #435 to the CLI.
brandur added a commit that referenced this pull request Jul 14, 2024
Here, break out the CLI so that most of its implementation no longer
lives in a `main.go`, and rather in a subpackage that can be imported
from somewhere else. This lets the majority of it to be reused in
another executable and augmented in various ways.

An addition is a new `DriverProcurer` interface that can provide a
driver for various databases:

    type DriverProcurer interface {
        ProcurePgxV5(pool *pgxpool.Pool) riverdriver.Driver[pgx.Tx]
    }

For the main CLI, this gets a trivial implementation using `riverpgxv5`,
but could potentially be reimplemented elsewhere to swap in something
else;

    type DriverProcurer struct{}

    func (p *DriverProcurer) ProcurePgxV5(pool *pgxpool.Pool) riverdriver.Driver[pgx.Tx] {
            return riverpgxv5.New(pool)
    }

To make this more workable, I end up going through and refactoring quite
a lot of code, making it into a bit of a mini framework, and one that
could potentially support additional databases in the future without
having to refactor the world again.

We also add a `--line` flag to support the feature from #435 to the CLI.
brandur added a commit that referenced this pull request Jul 17, 2024
Here, break out the CLI so that most of its implementation no longer
lives in a `main.go`, and rather in a subpackage that can be imported
from somewhere else. This lets the majority of it to be reused in
another executable and augmented in various ways.

An addition is a new `DriverProcurer` interface that can provide a
driver for various databases:

    type DriverProcurer interface {
        ProcurePgxV5(pool *pgxpool.Pool) riverdriver.Driver[pgx.Tx]
    }

For the main CLI, this gets a trivial implementation using `riverpgxv5`,
but could potentially be reimplemented elsewhere to swap in something
else;

    type DriverProcurer struct{}

    func (p *DriverProcurer) ProcurePgxV5(pool *pgxpool.Pool) riverdriver.Driver[pgx.Tx] {
            return riverpgxv5.New(pool)
    }

To make this more workable, I end up going through and refactoring quite
a lot of code, making it into a bit of a mini framework, and one that
could potentially support additional databases in the future without
having to refactor the world again.

We also add a `--line` flag to support the feature from #435 to the CLI.
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.

2 participants