github.com/eliastor/durgaform@v0.0.0-20220816172711-d0ab2d17673e/docs/destroying.md (about) 1 # Durgaform Core Resource Destruction Notes 2 3 This document intends to describe some of the details and complications 4 involved in the destructions of resources. It covers the ordering defined for 5 related create and destroy operations, as well as changes to the lifecycle 6 ordering imposed by `create_before_destroy`. It is not intended to enumerate 7 all possible combinations of dependency ordering, only to outline the basics 8 and document some of the more complicated aspects of resource destruction. 9 10 The graph diagrams here will continue to use the inverted graph structure used 11 internally by Durgaform, where edges represent dependencies rather than order 12 of operations. 13 14 ## Simple Resource Creation 15 16 In order to describe resource destruction, we first need to create the 17 resources and define their order. The order of creation is that which fulfills 18 the dependencies for each resource. In this example, `A` has no dependencies, 19 `B` depends on `A`, and `C` depends on `B`, and transitively depends on `A`. 20 21 ![Simple Resource Creation](./images/simple_create.png) 22 <!-- 23 digraph create { 24 subgraph nodes { 25 rank=same; 26 a [label="A create"]; 27 b [label="B create"]; 28 c [label="C create"]; 29 b -> c [dir=back]; 30 a -> b [dir=back]; 31 } 32 } 33 --> 34 35 Order of operations: 36 1. `A` is created 37 1. `B` is created 38 1. `C` is created 39 40 ## Resource Updates 41 42 An existing resource may be updated with references to a newly created 43 resource. The ordering here is exactly the same as one would expect for 44 creation. 45 46 ![Simple Resource Updates](./images/simple_update.png) 47 <!-- 48 digraph update { 49 subgraph nodes { 50 rank=same; 51 a [label="A create"]; 52 b [label="B update"]; 53 c [label="C update"]; 54 b -> c [dir=back]; 55 a -> b [dir=back]; 56 } 57 } 58 --> 59 60 Order of operations: 61 1. `A` is created 62 1. `B` is created 63 1. `C` is created 64 65 ## Simple Resource Destruction 66 67 The order for destroying resource is exactly the inverse used to create them. 68 This example shows the graph for the destruction of the same nodes defined 69 above. While destroy nodes will not contain attribute references, we will 70 continue to use the inverted edges showing dependencies for destroy, so the 71 operational ordering is still opposite the flow of the arrows. 72 73 ![Simple Resource Destruction](./images/simple_destroy.png) 74 <!-- 75 digraph destroy { 76 subgraph nodes { 77 rank=same; 78 a [label="A destroy"]; 79 b [label="B destroy"]; 80 c [label="C destroy"]; 81 a -> b; 82 b -> c; 83 } 84 } 85 --> 86 87 Order of operations: 88 1. `C` is destroyed 89 1. `B` is destroyed 90 1. `A` is Destroyed 91 92 ## Resource Replacement 93 94 Resource replacement is the logical combination of the above scenarios. Here we 95 will show the replacement steps involved when `B` depends on `A`. 96 97 In this first example, we simultaneously replace both `A` and `B`. Here `B` is 98 destroyed before `A`, then `A` is recreated before `B`. 99 100 ![Replace All](./images/replace_all.png) 101 <!-- 102 digraph replacement { 103 subgraph create { 104 rank=same; 105 a [label="A create"]; 106 b [label="B create"]; 107 a -> b [dir=back]; 108 } 109 subgraph destroy { 110 rank=same; 111 a_d [label="A destroy"]; 112 b_d [label="B destroy"]; 113 a_d -> b_d; 114 } 115 116 a -> a_d; 117 a -> b_d [style=dotted]; 118 b -> a_d [style=dotted]; 119 b -> b_d; 120 } 121 --> 122 123 Order of operations: 124 1. `B` is destroyed 125 1. `A` is destroyed 126 1. `A` is created 127 1. `B` is created 128 129 130 This second example replaces only `A`, while updating `B`. Resource `B` is only 131 updated once `A` has been destroyed and recreated. 132 133 ![Replace Dependency](./images/replace_one.png) 134 <!-- 135 digraph replacement { 136 subgraph create { 137 rank=same; 138 a [label="A create"]; 139 b [label="B update"]; 140 a -> b [dir=back]; 141 } 142 subgraph destroy { 143 rank=same; 144 a_d [label="A destroy"]; 145 } 146 147 a -> a_d; 148 b -> a_d [style=dotted]; 149 } 150 --> 151 152 Order of operations: 153 1. `A` is destroyed 154 1. `A` is created 155 1. `B` is updated 156 157 158 While the dependency edge from `B update` to `A destroy` isn't necessary in 159 these examples, it is shown here as an implementation detail which will be 160 mentioned later on. 161 162 A final example based on the replacement graph; starting with the above 163 configuration where `B` depends on `A`. The graph is reduced to an update of 164 `A` while only destroying `B`. The interesting feature here is the remaining 165 dependency of `A update` on `B destroy`. We can derive this ordering of 166 operations from the full replacement example above, by replacing `A create` 167 with `A update` and removing the unused nodes. 168 169 ![Replace All](./images/destroy_then_update.png) 170 <!-- 171 digraph destroy_then_update { 172 subgraph update { 173 rank=same; 174 a [label="A update"]; 175 } 176 subgraph destroy { 177 rank=same; 178 b_d [label="B destroy"]; 179 } 180 181 a -> b_d; 182 } 183 --> 184 ## Create Before Destroy 185 186 Currently, the only user-controllable method for changing the ordering of 187 create and destroy operations is with the `create_before_destroy` resource 188 `lifecycle` attribute. This has the obvious effect of causing a resource to be 189 created before it is destroyed when replacement is required, but has a couple 190 of other effects we will detail here. 191 192 Taking the previous replacement examples, we can change the behavior of `A` to 193 be that of `create_before_destroy`. 194 195 ![Replace all, dependency is create_before_destroy](./images/replace_all_cbd_dep.png) 196 <!-- 197 digraph replacement { 198 subgraph create { 199 rank=same; 200 a [label="A create"]; 201 b [label="B create"]; 202 a -> b [dir=back]; 203 } 204 subgraph destroy { 205 rank=same; 206 a_d [label="A destroy"]; 207 b_d [label="B destroy"]; 208 a_d -> b_d; 209 } 210 211 a -> a_d [dir=back]; 212 a -> b_d; 213 b -> a_d [dir=back]; 214 b -> b_d; 215 } 216 --> 217 218 219 Order of operations: 220 1. `B` is destroyed 221 2. `A` is created 222 1. `B` is created 223 1. `A` is destroyed 224 225 Note that in this first example, the creation of `B` is inserted in between the 226 creation of `A` and the destruction of `A`. This becomes more important in the 227 update example below. 228 229 230 ![Replace dependency, dependency is create_before_destroy](./images/replace_dep_cbd_dep.png) 231 <!-- 232 digraph replacement { 233 subgraph create { 234 rank=same; 235 a [label="A create"]; 236 b [label="B update"]; 237 a -> b [dir=back]; 238 } 239 subgraph destroy { 240 rank=same; 241 a_d [label="A destroy"]; 242 } 243 244 a -> a_d [dir=back, style=dotted]; 245 b -> a_d [dir=back]; 246 } 247 --> 248 249 Order of operations: 250 1. `A` is created 251 1. `B` is updated 252 1. `A` is destroyed 253 254 Here we can see clearly how `B` is updated after the creation of `A` and before 255 the destruction of the _deposed_ resource `A`. (The prior resource `A` is 256 sometimes referred to as "deposed" before it is destroyed, to disambiguate it 257 from the newly created `A`.) This ordering is important for resource that 258 "register" other resources, and require updating before the dependent resource 259 can be destroyed. 260 261 The transformation used to create these graphs is also where we use the extra 262 edges mentioned above connecting `B` to `A destroy`. The algorithm to change a 263 resource from the default ordering to `create_before_destroy` simply inverts 264 any incoming edges from other resources, which automatically creates the 265 necessary dependency ordering for dependent updates. This also ensures that 266 reduced versions of this example still adhere to the same ordering rules, such 267 as when the dependency is only being removed: 268 269 ![Update a destroyed create_before_destroy dependency](./images/update_destroy_cbd.png) 270 <!-- 271 digraph update { 272 subgraph create { 273 rank=same; 274 b [label="B update"]; 275 } 276 subgraph destroy { 277 rank=same; 278 a_d [label="A destroy"]; 279 } 280 281 b -> a_d [dir=back]; 282 } 283 --> 284 285 Order of operations: 286 1. `B` is updated 287 1. `A` is destroyed 288 289 ### Forced Create Before Destroy 290 291 In the previous examples, only resource `A` was being used as is it were 292 `create_before_destroy`. The minimal graphs used show that it works in 293 isolation, but that is only when the `create_before_destroy` resource has no 294 dependencies of it own. When a `create_before_resource` depends on another 295 resource, that dependency is "infected" by the `create_before_destroy` 296 lifecycle attribute. 297 298 This example demonstrates why forcing `create_before_destroy` is necessary. `B` 299 has `create_before_destroy` while `A` does not. If we only invert the ordering 300 for `B`, we can see that results in a cycle. 301 302 ![Incorrect create_before_destroy replacement](./images/replace_cbd_incorrect.png) 303 <!-- 304 digraph replacement { 305 subgraph create { 306 rank=same; 307 a [label="A create"]; 308 b [label="B create"]; 309 a -> b [dir=back]; 310 } 311 subgraph destroy { 312 rank=same; 313 a_d [label="A destroy"]; 314 b_d [label="B destroy"]; 315 a_d -> b_d; 316 } 317 318 a -> a_d; 319 a -> b_d [style=dotted]; 320 b -> a_d [style=dotted]; 321 b -> b_d [dir=back]; 322 } 323 --> 324 325 In order to resolve these cycles, all resources that precede a resource 326 with `create_before_destroy` must in turn be handled in the same manner. 327 Reversing the incoming edges to `A destroy` resolves the problem: 328 329 ![Correct create_before_destroy replacement](./images/replace_all_cbd.png) 330 <!-- 331 digraph replacement { 332 subgraph create { 333 rank=same; 334 a [label="A create"]; 335 b [label="B create"]; 336 a -> b [dir=back]; 337 } 338 subgraph destroy { 339 rank=same; 340 a_d [label="A destroy"]; 341 b_d [label="B destroy"]; 342 a_d -> b_d; 343 } 344 345 a -> a_d [dir=back]; 346 a -> b_d [dir=back, style=dotted]; 347 b -> a_d [dir=back, style=dotted]; 348 b -> b_d [dir=back]; 349 } 350 --> 351 352 Order of operations: 353 1. `A` is created 354 1. `B` is created 355 1. `B` is destroyed 356 1. `A` is destroyed 357 358 This also demonstrates why `create_before_destroy` cannot be overridden when 359 it is inherited; changing the behaviour here isn't possible without removing 360 the initial reason for `create_before_destroy`; otherwise cycles are always 361 introduced into the graph.