Skip to content

Migrating to 0.16

From 0.15 to 0.16

Protean 0.16 is a stability and reliability release. Most upgrades are drop-in. The one structural change that may require operator action is the outbox column bounds described below.

On this page:


Outbox column bounds

Tier-3 structural change

The string fields on the Outbox aggregate now declare max_length. SQL providers therefore emit VARCHAR(N) columns instead of TEXT / VARCHAR(MAX). This unblocks index creation on the outbox table: SQL Server refuses to index VARCHAR(MAX), MySQL requires a blind prefix length on indexed TEXT, and bounded columns are cheaper to store and scan everywhere.

The affected fields and their bounds:

Field max_length
status 32
target_broker 128
stream_name 255
type 255
locked_by 128
correlation_id 255
causation_id 255
message_id 255

message_id, causation_id, and correlation_id are bounded at 255 rather than a UUID-sized value because they hold composite Protean message ids (for example testdomain::order-<aggregate-id>-3) or caller-supplied tracing strings, not bare UUIDs.

The data and metadata_ columns remain unbounded JSON blobs.

Who is affected

  • New deployments: nothing to do. Tables created on 0.16 get the bounded VARCHAR(N) columns automatically.
  • Existing deployments with a populated outbox table: Protean never alters a populated table. The table keeps working as-is, but to gain the storage and indexing benefits (and to match the schema Protean now emits) run the one-time ALTER TABLE for your backend below.

Pre-flight verification

No value Protean writes to these fields exceeds the new bounds. Before applying the migration, confirm your existing data also fits:

SELECT
  MAX(LENGTH(status))         AS max_status,
  MAX(LENGTH(target_broker))  AS max_target_broker,
  MAX(LENGTH(stream_name))    AS max_stream_name,
  MAX(LENGTH(type))           AS max_type,
  MAX(LENGTH(locked_by))      AS max_locked_by,
  MAX(LENGTH(correlation_id)) AS max_correlation_id,
  MAX(LENGTH(causation_id))   AS max_causation_id,
  MAX(LENGTH(message_id))     AS max_message_id
FROM outbox;

On SQL Server, substitute LEN(...) for LENGTH(...) (SQL Server has no LENGTH function).

If any column exceeds its bound, investigate before applying the migration.

Migration recipes

=== "PostgreSQL"

```sql
ALTER TABLE outbox
  ALTER COLUMN status TYPE varchar(32),
  ALTER COLUMN target_broker TYPE varchar(128),
  ALTER COLUMN stream_name TYPE varchar(255),
  ALTER COLUMN type TYPE varchar(255),
  ALTER COLUMN locked_by TYPE varchar(128),
  ALTER COLUMN correlation_id TYPE varchar(255),
  ALTER COLUMN causation_id TYPE varchar(255),
  ALTER COLUMN message_id TYPE varchar(255);
```

=== "MySQL"

```sql
ALTER TABLE outbox
  MODIFY status varchar(32) NOT NULL,
  MODIFY target_broker varchar(128) NULL,
  MODIFY stream_name varchar(255) NOT NULL,
  MODIFY type varchar(255) NOT NULL,
  MODIFY locked_by varchar(128) NULL,
  MODIFY correlation_id varchar(255) NULL,
  MODIFY causation_id varchar(255) NULL,
  MODIFY message_id varchar(255) NOT NULL;
```

=== "SQL Server"

```sql
ALTER TABLE outbox ALTER COLUMN status varchar(32) NOT NULL;
ALTER TABLE outbox ALTER COLUMN target_broker varchar(128) NULL;
ALTER TABLE outbox ALTER COLUMN stream_name varchar(255) NOT NULL;
ALTER TABLE outbox ALTER COLUMN type varchar(255) NOT NULL;
ALTER TABLE outbox ALTER COLUMN locked_by varchar(128) NULL;
ALTER TABLE outbox ALTER COLUMN correlation_id varchar(255) NULL;
ALTER TABLE outbox ALTER COLUMN causation_id varchar(255) NULL;
ALTER TABLE outbox ALTER COLUMN message_id varchar(255) NOT NULL;
```

=== "SQLite"

SQLite does not enforce declared column lengths, so the migration is a
no-op for storage. New databases get the `VARCHAR(N)` declaration;
existing databases continue to function unchanged.