github.com/Pankov404/juju@v0.0.0-20150703034450-be266991dceb/doc/hacking-state.txt (about)

     1  Hacking the juju-core/state package
     2  ===================================
     3  
     4  This document remains a work in progress; it's an attempt to capture
     5  the various conventions and things to bear in mind that aren't
     6  necessarily written down anywhere else.
     7  
     8  return values: ok vs err
     9  ------------------------
    10  
    11  By convention, anything that could reasonably fail must use a separate
    12  channel to communicate the failure. Broadly speaking, methods that can
    13  fail in more than one way must return an error; those that can only
    14  fail in one way (eg Machine.InstanceId: the value is either valid or
    15  missing) are expected to return a bool for consistency's sake, even if
    16  the type of the return value is such that failure can be signalled in-
    17  band.
    18  
    19  changes to entities
    20  -------------------
    21  
    22  Entity objects reflect remote state that may change at any time, but we
    23  don't want to make that too obvious. By convention, the only methods
    24  that should change an entity's in-memory document are as follows:
    25  
    26    * Refresh(), which should update every field.
    27    * Methods that set fields remotely should update only those fields
    28      locally.
    29  
    30  The upshot of this is that it's not appropriate to call Refresh on any
    31  argument to a state method, including receivers; if you're in a context
    32  in which that would be helpful, you should clone the entity first. This
    33  is simple enough that there's no Clone() method, but it would be kinda
    34  nice to implement them in our Copious Free Time; I think there are
    35  places outside state that would also find it useful.
    36  
    37  care and feeding of mgo/txn
    38  ---------------------------
    39  
    40  Just about all our writes to mongodb are mediated by the mgo/txn
    41  package, and using this correctly demands some care. Not all the code
    42  has been written in a fully aware state, and cases in which existing
    43  practice is divergent from the advice given below should be regarded
    44  with some suspicion.
    45  
    46  The txn package lets you make watchable changes to mongodb via lists of
    47  operations with the following critical properties:
    48  
    49    * transactions can apply to more than one document (this is rather
    50      the point of having them).
    51    * transactions will complete if every assert in the transaction
    52      passes; they will not run at all if any assert fails.
    53    * multi-document transactions are *not* atomic; the operations are
    54      applied in the order specified by the list.
    55    * operations, and hence assertions, can only be applied to documents
    56      with ids that are known at the time the operation list is built;
    57      this means that it takes extra work to specify a condition like
    58      "no unit document newer than X exists".
    59  
    60  The second point above deserves further discussion. Whenever you are
    61  implementing a state change, you should consider the impact of the
    62  following possible mongodb states on your code:
    63  
    64    * if mongodb is already in a state consistent with the transaction
    65      having already been run -- which is *always* possible -- you
    66      should return immediately without error.
    67    * if mongodb is in a state that indicates the transaction is not
    68      valid -- eg trying to add a unit to a dying service -- you should
    69      return immediately with a descriptive error.
    70  
    71  Each of the above situations should generally be checked as part of
    72  preparing a new []txn.Op, but in some cases it's convenient to trust
    73  (to begin with) that the entity's in-memory state reflects reality.
    74  Regardless, your job is to build a list of operations which assert
    75  that:
    76  
    77    * the transaction still needs to be applied.
    78    * the transaction is actively valid.
    79    * facts on which the transaction list's form depends remain true.
    80  
    81  If you're really lucky you'll get to write a transaction in which
    82  the third requirement collapses to nothing, but that's not really
    83  the norm. In practice, you need to be prepared for the run to return
    84  txn.ErrAborted; if this happens, you need to check for previous success
    85  (and return nil) or for known cause of invalidity (and return an error
    86  describing it).
    87  
    88  If neither of these cases apply, you should assume it's an assertion
    89  failure of the third kind; in that case, you should build a new
    90  transaction based on more recent state and try again. If ErrAborteds
    91  just keep coming, give up; there's an ErrExcessiveContention that
    92  helps to describe the situation.
    93  
    94  watching entities, and select groups thereof
    95  --------------------------------------------
    96  
    97  The mgo/txn log enables very convenient notifications of changes to
    98  particular documents and groups thereof. The state/watcher package
    99  converts the txn event log into events and send them down client-
   100  supplied channels for further processing; the state code itself
   101  implements watchers in terms of these events.
   102  
   103  All the internally-relevant watching code is implemented in the file
   104  state/watcher.go. These constructs can be broadly divided into groups
   105  as follows:
   106  
   107    * single-document watchers: dead simple, they notify of every
   108      change to a given doc. SettingsWatcher bucks the convention of
   109      NotifyWatcher in that it reads whole *Settings~s to send down the
   110      channel rather than just sending notifications (we probably
   111      shouldn't do that, but I don't think there's time to fix it right
   112      now).
   113    * entity group watchers: pretty simple, very common; they notify of
   114      every change to the Life field among a group of entities in the
   115      same collection, with group membership determined in a wide variety
   116      of ways.
   117    * relation watchers: of a similar nature to entity group watchers,
   118      but generating carefully-ordered events from observed changes to
   119      several different collections, none of which have Life fields.
   120  
   121  Implementation of new watchers is not necessarily a simple task, unless
   122  it's a genuinely trivial refactoring (or, given lack of generics, copy-
   123  and-paste job). Unless you already know exactly what you're doing,
   124  please start a discussion about what you're trying to do before you
   125  embark on further work in this area.
   126  
   127  transactions and reference counts
   128  ---------------------------------
   129  
   130  As described above, it's difficult to assert things like "no units of
   131  service X exist". It can be done, but not without additional work, and
   132  we're generally using reference counts to enable this sort of thing.
   133  
   134  In general, reference counts should not be stored within entities: such
   135  changes are part of the operation of juju and are not important enough
   136  to be reflected as a constant stream of events. We didn't figure this
   137  out until recently, though, so relationDoc has a UnitCount, and
   138  serviceDoc has both a UnitCount and a RelationCount. This doesn't
   139  matter for the relation -- nothing watches relation docs directly --
   140  but I fear it matters quite hard for service, because every added or
   141  removed unit or relation will be an unnecessary event sent to every
   142  unit agent.
   143  
   144  We'll deal with that when it's a problem rather than just a concern;
   145  but new reference counts should not be thoughtlessly added to entity
   146  documents, and opportunities to separate them from existing docs should
   147  be considered seriously.
   148  
   149  
   150  [TODO: write about globalKey and what it's good for... constraints, settings,
   151  statuses, etc; occasionaly profitable use of fast prefix lookup on indexed
   152  fields.]
   153