github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/state/lease/doc.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  /*
     5  
     6  The lease package exists to implement distributed lease management on top of
     7  mgo/txn, and to expose assert operations that allow us to gate other mgo/txn
     8  transactions on lease state. This necessity has affected the package; but,
     9  apart from leaking assertion operations, it functions as a distributed lease-
    10  management system with various useful properties.
    11  
    12  These properties of course rest upon assumptions; ensuring the validity of the
    13  following statements is the job of the client.
    14  
    15    * The lease package has exclusive access to any collection it's configured
    16      to use. (Please don't store anything else in there.)
    17  
    18    * Given any (collection,namespace) pair, any client Id will be unique at any
    19      given point in time. (Run no more than one per namespace per server, and
    20      identify them according to where they're running).
    21  
    22    * Time passes at approximately the same rate for all clients. (Note that
    23      the clients do *not* have to agree what time it is, or what time zone
    24      anyone is in: just that 1s == 1s. This is likely to be true already if
    25      you use lease.SystemClock{}.)
    26  
    27  So long as the above holds true, the following statements will too:
    28  
    29    * A successful ClaimLease guarantees lease ownership until *at least* the
    30      requested duration after the start of the call. (It does *not* guaranntee
    31      any sort of timely expiry.)
    32  
    33    * A successful ExtendLease makes the same guarantees. (In particular, note
    34      that this cannot cause a lease to be shortened; but that success may
    35      indicate ownership is guaranteed for longer than requested.)
    36  
    37    * ExpireLease will only succeed when the most recent writer of the lease is
    38      known to believe the time is after the expiry time it wrote.
    39  
    40  
    41  Remarks on clock skew
    42  ---------------------
    43  
    44  When expiring a lease (or determining whether it needs to be extended) we only
    45  need to care about the writer, because everybody else is determining their own
    46  skew relative only to the writer. That is, assuming 3 clients:
    47  
    48   A) knows "the real time"; wrote a lease at 01:00:00, expiring at 01:00:30A
    49   B) is 20 seconds ahead; read the lease between 01:00:23B and 01:00:24B
    50   C) is 5 seconds behind; read the lease between 00:59:57C and 00:59:58C
    51  
    52  ...then B cannot infer an expiry time earlier than 01:00:54L (=01:00:34A) and
    53  C cannot infer an expiry time earlier than 01:00:28C (=01:00:33A). If A fails
    54  to expire its lease, then C will trigger first and try to expire it, and most
    55  likely succeed; and when C succeeds, B's subsequent attempt to expire the
    56  lease will certainly fail, because C has updated both the clock document and
    57  the lease document and invalidated B's assertions.
    58  
    59  So B can and does then Refresh; and sees the lease document written by C, and
    60  now needs only to consider its offset relative to C in order to Do The Right
    61  Thing.
    62  
    63  
    64  Schema design
    65  -------------
    66  
    67  For each namespace, we store a single clock document; and one additional
    68  document per lease. The lease document holds the name, holder, expiry, and
    69  writer of the lease; the clock document contains the most recent time
    70  acknowledged by each client that has written to the namespace.
    71  
    72  Every transaction that the lease package makes is gated on a write to the
    73  clock document (which *must* precede any lease operations) which acks a
    74  recent time and fails if it appears to be going backward in time (this could
    75  happen if we crashed at the wrong moment and left a transaction queued but
    76  unprepared for some time: we definitely don't want to accept those operations).
    77  
    78  The fact that the clock document is involved in every transaction renders it a
    79  per-namespace bottleneck, but the ability to discard outdated transactions is
    80  valuable; and the centralised record of acknowledged times mitigates the impact
    81  of client failure.
    82  
    83  That is to say: assuming client C wrote lease L at time T, and wrote lease M
    84  at time U (later than T); and then failed; then a fresh client D will be able
    85  to expire lease L earlier (by U-T) than it could infer with the information in
    86  lease L alone.
    87  
    88  (We could ofc still calculate that by storing a written time in each lease
    89  document, but it'd be more hassle to collate the data, harder to inspect the
    90  database, and would only be able to make much weaker anti-time-travel promises
    91  than we can manage with the clock doc.)
    92  
    93  
    94  Client usage considerations
    95  ---------------------------
    96  
    97    * Client operates at a relatively low level of abstraction. Claiming a held
    98      lease will fail, even on behalf of the holder; expiring an expired lease
    99      will fail; but at least we can allow lease extensions to race benignly,
   100      because they don't involve ownership change and thus can't break promises
   101      (so long as our skew logic is correct).
   102  
   103    * ErrInvalid is normal and expected; you should never pass that on to your
   104      own clients, because it indicates that you tried to manipulate the client
   105      in an impossible way. You can and should inspect Leases() and figure out
   106      what to do instead; that may well be "return an error", but please be sure
   107      to return your own error, suitable for your own level of abstraction.
   108  
   109    * You *probably* shouldn't ever need to actually call Refresh. It's perfectly
   110      safe to let state drift arbitrarily far out of sync; when you try to run
   111      operations, you will either succeed by luck despite your aged cache... or,
   112      if you fail, you'll get ErrInvalid and a fresh cache to inspect to find out
   113      recent state.
   114  
   115  
   116  */
   117  package lease