‹ BackHN Continuity

Thread

Is your Postgres migration safe or not safe?

138 points · 45 comments · vira28

  1. orf · · focus · HN ↗
    These kinds of rule-based migration safety checks are simple, but hardly complete.

    The problem is that some migration safety depends on the state of the database, which isn’t represented in the DDL statement alone. For example, altering a column type is either a no-op or an exclusive locked table rewrite depending on the original type of the column.

    There are other footguns that can happen if the column you’re altering is a foreign key, where multiple tables can be locked.

    I went down a rabbit hole a few years ago and built a system[1] to introspect a given migration against a live schema, and actually let Postgres tell you what it’s doing[2].

    It would be great to have better built-in support for this (EXPLAIN for DDL statements?), but this direction feels safer and more accurate than static rulesets.

    Safety also depends on the size/activity of a table being altered (i.e rewriting an empty table is fine). Having an accurate representation of the locks and actions performed by the database lets you integrate with production metrics to actually determine real-world safety across a fleet of databases, rather than guessing.

    1. <a href="https:&#x2F;&#x2F;github.com&#x2F;orf&#x2F;locksmith" rel="nofollow">https:&#x2F;&#x2F;github.com&#x2F;orf&#x2F;locksmith

    2. <a href="https:&#x2F;&#x2F;github.com&#x2F;orf&#x2F;locksmith&#x2F;blob&#x2F;f8798c6ee92bfae10d416c49aae33bc4afaaabf1&#x2F;crates&#x2F;locksmith&#x2F;src&#x2F;oracle.rs#L34" rel="nofollow">https:&#x2F;&#x2F;github.com&#x2F;orf&#x2F;locksmith&#x2F;blob&#x2F;f8798c6ee92bfae10d416c...

    1. williamdclt · · focus · HN ↗
      The way I wished Postgres DDLs worked (at least optionally) is that you have to explicitly acquire the correct lock before a DDL statement, or it just immediately fails. Something like:

      ACQUIRE ACCESS SHARE TABLE LOCK ON my_table ALTER TABLE my_table ALTER COLUMN my_column TYPE bigint

      This way I _know_ that if the operation needs a stronger lock than I thought or than I&#x27;m willing to give it, it will just fail rather than locking up my database and causing unexpected downtime.

      1. mxey · · focus · HN ↗
        That’s an interesting idea but not all locks are held for the duration of the statement. A lot of them take a less intrusive lock for the whole statement and take an exclusive lock for a very short time when they finish up.

        Edit: Looking this up, I’m not sure this is correct.

        1. dathinab · · focus · HN ↗
          &gt; whole statement

          simplified you can think of a statement outside of a transaction as starting an implicit transaction just for itself

          and (normal) locks are in general hold until the end of the transaction (while also allowing re-entrance from subsequent queries on the same transaction)

          practically

          - there are edge cases (e.g. Advisory Locks, but in general you don&#x27;t want to use them)

          - you normally(^1) would want to run your pg migration as a single transaction (but there are edge cases). And in turn the OPs idea of pre-acquiring locks would be for the whole transaction anyway. Plus it was just a general idea, so the end result could be more like an &quot;expect lock&quot; statement maybe with some scan ahead ability then an &quot;acquire lock&quot;.

          (^1): Exceptions include certain operations which need to be in different transactions, and some painful situations where too much data is touched&#x2F;changed&#x2F;computed and you need a lot of very careful handling you common small-ish PG DB use-case isn&#x27;t exposed to (and in turn a lot of &quot;naive but often good enough&quot; migration setups can&#x27;t handle either...)

Open on Hacker News to reply ↗

Unofficial Hacker News client; not affiliated with Y Combinator.