Merge lp:~themue/juju-core/024-cleanup-watcher into lp:~juju/juju-core/trunk
- 024-cleanup-watcher
- Merge into trunk
Status: | Merged |
---|---|
Merged at revision: | 1259 |
Proposed branch: | lp:~themue/juju-core/024-cleanup-watcher |
Merge into: | lp:~juju/juju-core/trunk |
Diff against target: |
157 lines (+142/-0) 2 files modified
state/state_test.go (+90/-0) state/watcher.go (+52/-0) |
To merge this branch: | bzr merge lp:~themue/juju-core/024-cleanup-watcher |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Juju Engineering | Pending | ||
Review via email: mp+167748@code.launchpad.net |
Commit message
Description of the change
state: added CleanupWatcher
The CleanupWatcher signals the demand for the running of
state.Cleanup(). It is the first in a row of CLs to add
an according worker.
Frank Mueller (themue) wrote : | # |
William Reade (fwereade) wrote : | # |
Sorry, NOT LGTM. This does not do what we agreed (notify on any change
to the cleanups collection), does stuff we don't want (notifies even if
there are no changes to the cleanups collection) is not self-consistent
(an empty collection sometimes generates an event, and sometimes does
not)... and is *not a watcher*, either in form or in function.
https:/
File state/watcher.go (right):
https:/
state/watcher.
event will include it.
Fix these please :).
https:/
state/watcher.
Why is this not a... watcher? We discussed this yesterday: there's no
call whatsoever to make this periodic. We can just use the usual,
conventional mechanism of watching the st.cleanups collection, but with
the additional simplification that I'm happy just sending out a
notification whenever there's any change whatsoever to any document in
there.
I really tried to be as explicit as possible; what do you think is the
source of the communication problems here?
Frank Mueller (themue) wrote : | # |
https:/
File state/watcher.go (right):
https:/
state/watcher.
event will include it.
On 2013/06/06 16:26:44, fwereade wrote:
> Fix these please :).
Ouch, yes!
https:/
state/watcher.
On 2013/06/06 16:26:44, fwereade wrote:
> Why is this not a... watcher? We discussed this yesterday: there's no
call
> whatsoever to make this periodic. We can just use the usual,
conventional
> mechanism of watching the st.cleanups collection, but with the
additional
> simplification that I'm happy just sending out a notification whenever
there's
> any change whatsoever to any document in there.
The usual mechanism relies on entities with TxnRevno. I wanted to use it
first, but then I've seen that the cleanupDoc doesn't uses it. Es
explicit as most of our code is I thought that this decision has not
been by accident. So I implemented a polling with the same timeframe the
standard watcher uses to recognize changes.
I surely can change it to the standard watcher adding the TxnRevno. I
only see that it leads to the sending of an event on any change. This
would later lead to events during the cleanup and so also lead to even
more Cleanup() calls. I've tried to avoid it. How would you avoid those
events during/after a Cleanup() run?
> I really tried to be as explicit as possible; what do you think is the
source of
> the communication problems here?
That's a different topic and we tomorrow should take some time to have a
talk via Hangout.
Frank Mueller (themue) wrote : | # |
Please take a look.
Dimiter Naydenov (dimitern) wrote : | # |
A few comments.
https:/
File state/state.go (right):
https:/
state/state.
You don't need to define this, it's automatically available.
https:/
File state/state_test.go (right):
https:/
state/state_
Please add c.Assert(ok, Equals, true), otherwise this is not a change
notification.
https:/
state/state_
s.AddTestingCha
do you really need to do this twice? surely only one relation followed
by destroy should be sufficient, but ymmv
https:/
File state/watcher.go (right):
https:/
state/watcher.
in chan doesn't need to be a field of the watcher, you can create it
locally inside loop()
https:/
state/watcher.
is there always an initial event that makes this line necessary? istm
this will always cause a notification to be sent, even when there's
nothing coming on the in chan.
William Reade (fwereade) wrote : | # |
LGTM with dimitern's comments addressed and some better tests for the
watcher. Sorry for the partially-outdated essay in the previous change
set.
https:/
File state/watcher.go (right):
https:/
state/watcher.
On 2013/06/06 16:54:17, mue wrote:
> The usual mechanism relies on entities with TxnRevno. I wanted to use
it first,
> but then I've seen that the cleanupDoc doesn't uses it. Es explicit as
most of
> our code is I thought that this decision has not been by accident. So
I
> implemented a polling with the same timeframe the standard watcher
uses to
> recognize changes.
You don't need TxnRevno at all. The txn-revno field on which we depend
is written by mgo/txn regardless, and you don't need to use the field in
the state package *except* when starting a single-entity watch on a
document; and relatively few of our document types actually require that
functionality.
The watching code is all present and correct and already set up for this
scenario; there is *no* justification possible for *polling* data that
changes approximately 0 times per month. Just watch the cleanups
collection and send an empty notification *every* time *anything*
happens to the collection (in addition to the conventionally-
initial event).
> I surely can change it to the standard watcher adding the TxnRevno. I
only see
> that it leads to the sending of an event on any change. This would
later lead to
> events during the cleanup and so also lead to even more Cleanup()
calls. I've
> tried to avoid it. How would you avoid those events during/after a
Cleanup()
> run?
Events during the cleanups will be coalesced by a sane implementation,
and thus lead to *one* extra cleanup call. Some months will go by
without this consequence ever coming to pass, so I think we can live
with it... as I thought we agreed the other day. If we ever decide we
can't afford that cost, we can write a more sophisticated watcher.
> > I really tried to be as explicit as possible; what do you think is
the source
> of
> > the communication problems here?
> That's a different topic and we tomorrow should take some time to have
a talk
> via Hangout.
I'm on holiday today.
https:/
File state/state_test.go (right):
https:/
state/state_
On 2013/06/07 09:15:37, dimitern wrote:
> Please add c.Assert(ok, Equals, true), otherwise this is not a change
> notification.
Meh, -1, the fatal message includes the value, it's never getting lost.
Don't see what'd be improved by a distinct assert.
https:/
state/state_
s.AddTestingCha
On 2013/06/07 09:15:37, dimitern wrote:
> do you really need to do this twice? surely only one relation followed
by
> destroy should be sufficient, but ymmv
It'd support more sophisticated t...
William Reade (fwereade) wrote : | # |
just another comment on the tests
https:/
File state/state_test.go (right):
https:/
state/state_
I think `defer stop(c, cw)` would be slightly more correct.
- 1258. By Dimiter Naydenov
-
revert r1257 due to a failing test:
Frank Mueller (themue) wrote : | # |
Please take a look.
https:/
File state/state.go (right):
https:/
state/state.
On 2013/06/07 09:15:37, dimitern wrote:
> You don't need to define this, it's automatically available.
Done.
https:/
File state/state_test.go (right):
https:/
state/state_
On 2013/06/07 09:40:10, fwereade wrote:
> I think `defer stop(c, cw)` would be slightly more correct.
Done.
https:/
state/state_
s.AddTestingCha
On 2013/06/07 09:35:44, fwereade wrote:
> On 2013/06/07 09:15:37, dimitern wrote:
> > do you really need to do this twice? surely only one relation
followed by
> > destroy should be sufficient, but ymmv
> It'd support more sophisticated testing -- for event coalescing,
effect of
> Cleanup calls, etc -- quite nicely. Would be nice to see tests for
these cases.
Added some more and detected, that Cleanup() removes each document in an
own transaction which leads to more events. Will change this later in a
followup. Tests currently handle it.
https:/
File state/watcher.go (right):
https:/
state/watcher.
On 2013/06/07 09:15:37, dimitern wrote:
> in chan doesn't need to be a field of the watcher, you can create it
locally
> inside loop()
Done.
https:/
state/watcher.
On 2013/06/07 09:15:37, dimitern wrote:
> is there always an initial event that makes this line necessary? istm
this will
> always cause a notification to be sent, even when there's nothing
coming on the
> in chan.
It only sent the initial event, afterwards out has been nil and so no
event has been sent. When theres a signal via in out is changed to the
out channel again, sents one event and is set to nil again.
But I changed it to a simpler way.
Dimiter Naydenov (dimitern) wrote : | # |
LGTM with a few comments changed.
https:/
File state/state_test.go (right):
https:/
state/state_
please change this to say "unexpected change; ok: %v"
https:/
state/state_
// Adding relations doesn't emit events.
https:/
state/state_
// Verify that Cleanup() doesn't emit unnecessary events.
https:/
state/state_
queue. Cleanups
// Not calling Cleanup() queues up the changes, so that
// multiple changes will be emitted the next time Cleanup()
// is called. This behavior will change in a follow-up.
https:/
File state/watcher.go (right):
https:/
state/watcher.
documents
// CleanupWatcher notifies of changes in the cleanups collection.
?
https:/
state/watcher.
notifies when documents
// WatchCleanups starts and returns a CleanupWatcher.
https:/
state/watcher.
nice!
- 1259. By Frank Mueller
-
state: added CleanupWatcher
The CleanupWatcher signals the demand for the running of
state.Cleanup(). It is the first in a row of CLs to add
an according worker.R=fwereade, dimitern
CC=
https://codereview. appspot. com/10078043
Frank Mueller (themue) wrote : | # |
*** Submitted:
state: added CleanupWatcher
The CleanupWatcher signals the demand for the running of
state.Cleanup(). It is the first in a row of CLs to add
an according worker.
R=fwereade, dimitern
CC=
https:/
https:/
File state/state_test.go (right):
https:/
state/state_
On 2013/06/07 12:24:50, dimitern wrote:
> please change this to say "unexpected change; ok: %v"
Done.
https:/
state/state_
On 2013/06/07 12:24:50, dimitern wrote:
> // Adding relations doesn't emit events.
Done.
https:/
state/state_
On 2013/06/07 12:24:50, dimitern wrote:
> // Verify that Cleanup() doesn't emit unnecessary events.
Done.
https:/
state/state_
queue. Cleanups
On 2013/06/07 12:24:50, dimitern wrote:
> // Not calling Cleanup() queues up the changes, so that
> // multiple changes will be emitted the next time Cleanup()
> // is called. This behavior will change in a follow-up.
That's not correct, the second part is related to the internal behavior
of Cleanup() with individual transactions per deletion. Will separate
this better.
https:/
File state/watcher.go (right):
https:/
state/watcher.
documents
On 2013/06/07 12:24:50, dimitern wrote:
> // CleanupWatcher notifies of changes in the cleanups collection.
> ?
Done.
https:/
state/watcher.
notifies when documents
On 2013/06/07 12:24:50, dimitern wrote:
> // WatchCleanups starts and returns a CleanupWatcher.
Done.
Preview Diff
1 | === modified file 'state/state_test.go' |
2 | --- state/state_test.go 2013-06-07 10:54:03 +0000 |
3 | +++ state/state_test.go 2013-06-07 12:07:30 +0000 |
4 | @@ -1236,3 +1236,93 @@ |
5 | c.Assert(id, Equals, user.Name()) |
6 | c.Assert(err, IsNil) |
7 | } |
8 | + |
9 | +func (s *StateSuite) TestWatchCleanups(c *C) { |
10 | + cw := s.State.WatchCleanups() |
11 | + defer stop(c, cw) |
12 | + |
13 | + assertNoChange := func() { |
14 | + s.State.StartSync() |
15 | + select { |
16 | + case _, ok := <-cw.Changes(): |
17 | + c.Fatalf("unexpected change: %v", ok) |
18 | + case <-time.After(50 * time.Millisecond): |
19 | + } |
20 | + } |
21 | + assertChanges := func(count int) { |
22 | + s.State.StartSync() |
23 | + for i := 0; i < count; i++ { |
24 | + select { |
25 | + case _, ok := <-cw.Changes(): |
26 | + c.Assert(ok, Equals, true) |
27 | + case <-time.After(500 * time.Millisecond): |
28 | + c.Fatalf("timed out waiting for change") |
29 | + } |
30 | + } |
31 | + assertNoChange() |
32 | + } |
33 | + |
34 | + // Check initial event. |
35 | + assertChanges(1) |
36 | + |
37 | + // Add relations doesn't emit events. |
38 | + _, err := s.State.AddService("wordpress", s.AddTestingCharm(c, "wordpress")) |
39 | + c.Assert(err, IsNil) |
40 | + _, err = s.State.AddService("mysql", s.AddTestingCharm(c, "mysql")) |
41 | + c.Assert(err, IsNil) |
42 | + eps, err := s.State.InferEndpoints([]string{"wordpress", "mysql"}) |
43 | + c.Assert(err, IsNil) |
44 | + relM, err := s.State.AddRelation(eps...) |
45 | + c.Assert(err, IsNil) |
46 | + assertNoChange() |
47 | + _, err = s.State.AddService("varnish", s.AddTestingCharm(c, "varnish")) |
48 | + c.Assert(err, IsNil) |
49 | + eps, err = s.State.InferEndpoints([]string{"wordpress", "varnish"}) |
50 | + c.Assert(err, IsNil) |
51 | + relV, err := s.State.AddRelation(eps...) |
52 | + c.Assert(err, IsNil) |
53 | + assertNoChange() |
54 | + |
55 | + // Destroy relations and cleanup. |
56 | + err = relM.Destroy() |
57 | + c.Assert(err, IsNil) |
58 | + assertChanges(1) |
59 | + err = s.State.Cleanup() |
60 | + c.Assert(err, IsNil) |
61 | + assertChanges(1) |
62 | + err = relV.Destroy() |
63 | + c.Assert(err, IsNil) |
64 | + assertChanges(1) |
65 | + err = s.State.Cleanup() |
66 | + c.Assert(err, IsNil) |
67 | + assertChanges(1) |
68 | + |
69 | + // A cleanup without need doesn't emit events. |
70 | + err = s.State.Cleanup() |
71 | + c.Assert(err, IsNil) |
72 | + assertNoChange() |
73 | + |
74 | + // Don't handle each event keeps them in the queue. Cleanups |
75 | + // have a transaction per entry. So they create the according number |
76 | + // of events. This behavior will change in a followup with a cleanup |
77 | + // redesign. |
78 | + eps, err = s.State.InferEndpoints([]string{"wordpress", "mysql"}) |
79 | + c.Assert(err, IsNil) |
80 | + relM, err = s.State.AddRelation(eps...) |
81 | + c.Assert(err, IsNil) |
82 | + assertNoChange() |
83 | + eps, err = s.State.InferEndpoints([]string{"wordpress", "varnish"}) |
84 | + c.Assert(err, IsNil) |
85 | + relV, err = s.State.AddRelation(eps...) |
86 | + c.Assert(err, IsNil) |
87 | + assertNoChange() |
88 | + err = relM.Destroy() |
89 | + c.Assert(err, IsNil) |
90 | + err = relV.Destroy() |
91 | + c.Assert(err, IsNil) |
92 | + assertChanges(2) |
93 | + err = s.State.Cleanup() |
94 | + c.Assert(err, IsNil) |
95 | + assertChanges(2) |
96 | + |
97 | +} |
98 | |
99 | === modified file 'state/watcher.go' |
100 | --- state/watcher.go 2013-06-07 10:54:03 +0000 |
101 | +++ state/watcher.go 2013-06-07 12:07:30 +0000 |
102 | @@ -1231,3 +1231,55 @@ |
103 | } |
104 | panic("unreachable") |
105 | } |
106 | + |
107 | +// CleanupWatcher notifies changes of the cleanup documents |
108 | +// signalling the need for a cleanup. |
109 | +type CleanupWatcher struct { |
110 | + commonWatcher |
111 | + out chan struct{} |
112 | +} |
113 | + |
114 | +// WatchCleanups returns a CleanupWatcher that notifies when documents |
115 | +// that where marked for removal exist. |
116 | +func (st *State) WatchCleanups() *CleanupWatcher { |
117 | + return newCleanupWatcher(st) |
118 | +} |
119 | + |
120 | +func newCleanupWatcher(st *State) *CleanupWatcher { |
121 | + w := &CleanupWatcher{ |
122 | + commonWatcher: commonWatcher{st: st}, |
123 | + out: make(chan struct{}), |
124 | + } |
125 | + go func() { |
126 | + defer w.tomb.Done() |
127 | + defer close(w.out) |
128 | + w.tomb.Kill(w.loop()) |
129 | + }() |
130 | + return w |
131 | +} |
132 | + |
133 | +// Changes returns the event channel for w. |
134 | +func (w *CleanupWatcher) Changes() <-chan struct{} { |
135 | + return w.out |
136 | +} |
137 | + |
138 | +func (w *CleanupWatcher) loop() (err error) { |
139 | + in := make(chan watcher.Change) |
140 | + |
141 | + w.st.watcher.WatchCollection(w.st.cleanups.Name, in) |
142 | + defer w.st.watcher.UnwatchCollection(w.st.cleanups.Name, in) |
143 | + |
144 | + // Initial event. |
145 | + w.out <- struct{}{} |
146 | + |
147 | + for { |
148 | + select { |
149 | + case <-w.tomb.Dying(): |
150 | + return tomb.ErrDying |
151 | + case <-in: |
152 | + // Simply emit event for each change. |
153 | + w.out <- struct{}{} |
154 | + } |
155 | + } |
156 | + panic("unreachable") |
157 | +} |
Reviewers: mp+167748_ code.launchpad. net,
Message:
Please take a look.
Description:
state: added CleanupWatcher
The CleanupWatcher signals the demand for the running of
state.Cleanup(). It is the first in a row of CLs to add
an according worker.
https:/ /code.launchpad .net/~themue/ juju-core/ 024-cleanup- watcher/ +merge/ 167748
(do not edit description out of merge proposal)
Please review this at https:/ /codereview. appspot. com/10078043/
Affected files:
A [revision details]
M state/state_test.go
M state/watcher.go