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