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.