Try   HackMD

Database schema version ranges

Synapse currently has the concept of a database "schema version", which is used to forestall problems where a database update is performed, but the deployment is then rolled back to an earlier version, causing incompatibility.

This is implemented via the synapse.storage.prepare_database.SCHEMA_VERSION constant and the schema_version.version database column.

The problem with this approach is that it makes it impossible to roll Synapse back to an earlier version. In practice, we often take a "rolling" approach to schema changes. For example, consider the situation where a database table (say, old_table) becomes redundant; we might do the following:

  • Synapse version N: original version
  • Synapse version N+1: remove code that reads from old_table, but keep writing to it in case of rollback to version N.
  • Synapse version N+2: remove code that writes to old_table, but don't yet remove the table in case of rollback to version N+1.
  • Synapse version N+3: remove old_table completely.

The precise details vary, but such a pattern of gradually evolving database code in a way that maintains compatibility with one or two earlier versions is not uncommon.

Ideally, we would complain about attempts to roll back from N+2 to N, whilst allowing roll-back from N+1 to N or N+2 to N+1; however the schema_version implementation makes that impossible.

Proposal

  • We change the semantics of schema_version.version to indicate the oldest SCHEMA_VERSION that the database is compatible with.
  • At startup, Synapse continues to abort if schema_version.version is greater than synapse.storage.prepare_database.SCHEMA_VERSION.
  • However, schema_version.version is no longer set to prepare_database.SCHEMA_VERSION (we probably need a different constant for it).

In the above example:

  • Synapse version N+1 would have prepare_database.SCHEMA_VERSION=N+1, (and would add its delta files to delta/<N+1>) to reflect that it uses the database differently to version N, but would set schema_version.version=N, since the database will still be compatible with Synapse version N.

  • Likewise, Synapse version N+2 would set prepare_database.SCHEMA_VERSION=N+2, and must set schema_version.version=N+1, to denote that we have broken compatibility with version N.