1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
//! Repair migration for databases upgraded through early alpha releases.
//!
//! Two earlier migrations were edited in place after being released, so a
//! database that ran them at an old alpha differs from a fresh install:
//!
//! - `m0003` originally dropped the `snapshot` table; the drop was later
//! removed when snapshot support returned, but already-migrated databases
//! kept no `snapshot` table.
//! - `m0001` originally created `event.aggregator_id` / `snapshot.id` as
//! VARCHAR(26) and `event.name` as VARCHAR(20); the definitions were later
//! widened in place (64 / 50), but already-migrated databases kept the
//! narrow columns — overflowing on multi-id aggregates (silently truncating
//! on non-strict MySQL).
//!
//! This migration converges every database to the current schema: it recreates
//! the `snapshot` table (`IF NOT EXISTS`, so it is a no-op on fresh installs)
//! and widens the affected columns. It also widens `subscriber.key` and
//! `event.routing_key` to VARCHAR(255): the subscriber key is
//! `{routing_key}.{name}`, which easily exceeds 50 chars for ULID-per-tenant
//! routing keys.
//!
//! It also aligns the `event` indexes with the hot read paths: every
//! aggregate-scoped read (`ORDER BY timestamp, timestamp_subsec, version, id`
//! filtered on `(aggregator_type, aggregator_id)`) previously had no index
//! covering its sort — `idx_event_type_id` stops at the id, so the planner
//! read all of an aggregate's rows and sorted them to return one page. That
//! path runs on every projection load and every version-1 routing-key lookup.
use vec_box;
/// Migration that repairs schema drift from in-place-edited early migrations,
/// widens `subscriber.key` / `event.routing_key`, and aligns the `event`
/// indexes with the query shapes.
///
/// ## Changes
///
/// 1. Recreates the `snapshot` table (`IF NOT EXISTS`) for databases migrated
/// while `m0003` still dropped it.
/// 2. Widens `event.aggregator_id` and `snapshot.id` to VARCHAR(64),
/// `event.name` to VARCHAR(50), and `event.routing_key` /
/// `subscriber.key` to VARCHAR(255) (MySQL/PostgreSQL; SQLite ignores
/// VARCHAR lengths, so no alteration is needed there).
/// 3. Creates `idx_event_type_id_cursor` on
/// `(aggregator_type, aggregator_id, timestamp, timestamp_subsec, version, id)`,
/// so aggregate-scoped reads seek instead of scanning + sorting.
/// 4. Drops `idx_event_type_id`: it is a strict prefix of both the new index
/// and the unique `idx_event_type_id_version`, so it only cost write
/// amplification.
/// 5. Recreates `idx_event_type_routing_cursor` with a trailing `id` column,
/// so the routing-key subscription path fully covers the keyset tiebreaker
/// and needs no residual sort (matching m0005's `idx_event_type_cursor`).
///
/// ## Dependencies
///
/// This migration depends on [`M0005`](crate::M0005).
;
sqlite_migration!;
mysql_migration!;
postgres_migration!;