@@ -16,15 +16,20 @@ use uuid::Uuid;
16
16
17
17
mod destroyed;
18
18
19
- /// Parameters to the start instance update saga.
20
- #[ derive( Debug , Deserialize , Serialize ) ]
21
- pub ( crate ) struct Params {
22
- /// Authentication context to use to fetch the instance's current state from
23
- /// the database.
24
- pub serialized_authn : authn:: saga:: Serialized ,
25
-
26
- pub authz_instance : authz:: Instance ,
27
- }
19
+ // The public interface to this saga is actually a smaller saga that starts the
20
+ // "real" update saga, which inherits the lock from the start saga. This is
21
+ // because the decision of which subsaga(s) to run depends on the state of the
22
+ // instance record read from the database *once the lock has been acquired*,
23
+ // and the saga DAG for the "real" instance update saga may be constructed only
24
+ // after the instance state has been fetched. However, since the the instance
25
+ // state must be read inside the lock, that *also* needs to happen in a saga,
26
+ // so that the lock is always dropped when unwinding. Thus, we have a second,
27
+ // smaller saga which starts our real saga, and then the real saga, which
28
+ // decides what DAG to build based on the instance fetched by the start saga.
29
+ //
30
+ // Don't worry, this won't be on the test.
31
+ mod start;
32
+ pub ( crate ) use self :: start:: { Params , SagaInstanceUpdate } ;
28
33
29
34
/// Parameters to the "real" instance update saga.
30
35
#[ derive( Debug , Deserialize , Serialize ) ]
@@ -43,21 +48,6 @@ const INSTANCE_LOCK_ID: &str = "saga_instance_lock_id";
43
48
declare_saga_actions ! {
44
49
instance_update;
45
50
46
- // Acquire the instance updater" lock with this saga's ID if no other saga
47
- // is currently updating the instance.
48
- LOCK_INSTANCE -> "saga_instance_lock_gen" {
49
- + siu_lock_instance
50
- - siu_lock_instance_undo
51
- }
52
-
53
- // Fetch the instance and VMM's state, and start the "real" instance update saga.
54
- // N.B. that this must be performed as a separate action from
55
- // `LOCK_INSTANCE`, so that if the lookup fails, we will still unwind the
56
- // `LOCK_INSTANCE` action and release the lock.
57
- FETCH_STATE_AND_START_REAL_SAGA -> "state" {
58
- + siu_fetch_state_and_start_real_saga
59
- }
60
-
61
51
// Become the instance updater
62
52
BECOME_UPDATER -> "generation" {
63
53
+ siu_become_updater
@@ -70,36 +60,9 @@ declare_saga_actions! {
70
60
}
71
61
72
62
// instance update saga: definition
63
+ struct SagaDoActualInstanceUpdate ;
73
64
74
- #[ derive( Debug ) ]
75
- pub ( crate ) struct SagaInstanceUpdate ;
76
- impl NexusSaga for SagaInstanceUpdate {
77
- const NAME : & ' static str = "start-instance-update" ;
78
- type Params = Params ;
79
-
80
- fn register_actions ( registry : & mut ActionRegistry ) {
81
- instance_update_register_actions ( registry) ;
82
- }
83
-
84
- fn make_saga_dag (
85
- _params : & Self :: Params ,
86
- mut builder : DagBuilder ,
87
- ) -> Result < steno:: Dag , super :: SagaInitError > {
88
- builder. append ( Node :: action (
89
- INSTANCE_LOCK_ID ,
90
- "GenerateInstanceLockId" ,
91
- ACTION_GENERATE_ID . as_ref ( ) ,
92
- ) ) ;
93
- builder. append ( lock_instance_action ( ) ) ;
94
- builder. append ( fetch_state_and_start_real_saga_action ( ) ) ;
95
-
96
- Ok ( builder. build ( ) ?)
97
- }
98
- }
99
-
100
- struct SagaRealInstanceUpdate ;
101
-
102
- impl NexusSaga for SagaRealInstanceUpdate {
65
+ impl NexusSaga for SagaDoActualInstanceUpdate {
103
66
const NAME : & ' static str = "instance-update" ;
104
67
type Params = RealParams ;
105
68
@@ -165,58 +128,6 @@ impl NexusSaga for SagaRealInstanceUpdate {
165
128
}
166
129
}
167
130
168
- // instance update saga: action implementations
169
-
170
- async fn siu_lock_instance (
171
- sagactx : NexusActionContext ,
172
- ) -> Result < ( ) , ActionError > {
173
- let osagactx = sagactx. user_data ( ) ;
174
- let Params { ref serialized_authn, ref authz_instance, .. } =
175
- sagactx. saga_params :: < Params > ( ) ?;
176
- let lock_id = sagactx. lookup :: < Uuid > ( INSTANCE_LOCK_ID ) ?;
177
- let opctx =
178
- crate :: context:: op_context_for_saga_action ( & sagactx, serialized_authn) ;
179
- slog:: info!(
180
- osagactx. log( ) ,
181
- "instance update: attempting to lock instance" ;
182
- "instance_id" => %authz_instance. id( ) ,
183
- "saga_id" => %lock_id,
184
- ) ;
185
- osagactx
186
- . datastore ( )
187
- . instance_updater_lock ( & opctx, authz_instance, & lock_id)
188
- . await
189
- . map_err ( ActionError :: action_failed)
190
- . map ( |_| ( ) )
191
- }
192
-
193
- async fn siu_fetch_state_and_start_real_saga (
194
- sagactx : NexusActionContext ,
195
- ) -> Result < ( ) , ActionError > {
196
- let osagactx = sagactx. user_data ( ) ;
197
- let Params { serialized_authn, authz_instance, .. } =
198
- sagactx. saga_params :: < Params > ( ) ?;
199
- let opctx =
200
- crate :: context:: op_context_for_saga_action ( & sagactx, & serialized_authn) ;
201
-
202
- let state = osagactx
203
- . datastore ( )
204
- . instance_fetch_with_vmms ( & opctx, & authz_instance)
205
- . await
206
- . map_err ( ActionError :: action_failed) ?;
207
- osagactx
208
- . nexus ( )
209
- . execute_saga :: < SagaRealInstanceUpdate > ( RealParams {
210
- serialized_authn,
211
- authz_instance,
212
- state,
213
- } )
214
- . await
215
- . map_err ( ActionError :: action_failed) ?;
216
-
217
- Ok ( ( ) )
218
- }
219
-
220
131
async fn siu_become_updater (
221
132
sagactx : NexusActionContext ,
222
133
) -> Result < ( ) , ActionError > {
@@ -244,7 +155,7 @@ async fn siu_become_updater(
244
155
245
156
slog:: info!(
246
157
osagactx. log( ) ,
247
- "instance update: became instance updater " ;
158
+ "Now, I am become Updater, the destroyer of VMMs. " ;
248
159
"instance_id" => %authz_instance. id( ) ,
249
160
"saga_id" => %lock_id,
250
161
"parent_id" => ?state. instance. runtime_state. updater_id,
@@ -271,17 +182,6 @@ async fn siu_unlock_instance(
271
182
unlock_instance_inner ( serialized_authn, authz_instance, & sagactx) . await
272
183
}
273
184
274
- // N.B. that this has to be a separate function just because the undo action
275
- // must return `anyhow::Error` rather than `ActionError`.
276
- async fn siu_lock_instance_undo (
277
- sagactx : NexusActionContext ,
278
- ) -> Result < ( ) , anyhow:: Error > {
279
- let Params { ref serialized_authn, ref authz_instance, .. } =
280
- sagactx. saga_params :: < Params > ( ) ?;
281
- unlock_instance_inner ( serialized_authn, authz_instance, & sagactx) . await ?;
282
- Ok ( ( ) )
283
- }
284
-
285
185
async fn unlock_instance_inner (
286
186
serialized_authn : & authn:: saga:: Serialized ,
287
187
authz_instance : & authz:: Instance ,
0 commit comments