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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
//! Watching a migration run while it happens.
//!
//! Without an observer the runner is silent until it finishes, so a
//! long run shows nothing and a failure does not say which migration
//! died. Pass an observer to
//! [`migrate_pool_with_progress`](crate::migrate::migrate_pool_with_progress)
//! and it is called as each migration starts and finishes.
//!
//! ## Observers must not block
//!
//! Events are emitted **while the migrate lock is held** (a PG advisory
//! lock, or `GET_LOCK` on MySQL), for the whole run. Every other
//! process that wants to migrate is waiting behind it. An observer that
//! blocks, does sync I/O, or sends on a full unbuffered channel makes
//! them all wait longer.
//!
//! Use a bounded channel that drops when full, or a queue another task
//! drains. Losing a progress event is cosmetic; stalling every
//! migrating process is not.
//!
//! ```ignore
//! use rustango::migrate::{migrate_pool_with_progress, MigrationEvent};
//!
//! migrate_pool_with_progress(&pool, dir, &|event: MigrationEvent| {
//! if let MigrationEvent::Finished { name, index, total, .. } = &event {
//! println!("[{index}/{total}] {name}");
//! }
//! })
//! .await?;
//! ```
use Duration;
/// Something that happened during a migration run.
///
/// Fields are owned, not borrowed, so an observer can forward the event
/// to a channel or a log without tying it to the runner's stack.
/// What the runner did with a migration it reports as finished.
///
/// Three different things are all loosely called "applied". They are
/// kept apart so a watcher does not assume DDL ran when it did not.
/// Receives [`MigrationEvent`]s as a run progresses.
///
/// Implemented for any `Fn(MigrationEvent)`, so the common case is a
/// closure:
///
/// ```ignore
/// migrate_pool_with_progress(&pool, dir, &|e| tx.try_send(e).ok()).await?;
/// ```
///
/// **Implementations must not block.** Events arrive while the migrate
/// lock is held. See the [module docs](self#observers-must-not-block).
/// Emit to an optional observer. `None` is the silent path and
/// compiles to nothing.
pub