#juju-dev 2012-12-01 <fwereade> niemeyer, davecheney: you're probably on your weekends, but just in case: https://codereview.appspot.com/6845120 <fwereade> niemeyer, davecheney: that is the Deployer; sorry it took so long, I saw a way to make it neater #juju-dev 2012-12-03 <davecheney> [ANN] juju-core 1.9.3 has been released <davecheney> https://lists.ubuntu.com/archives/juju-dev/2012-December/000333.html <wallyworld_> davecheney: g'day, i have a problem you might me able to help me solve if you have a moment to take a look. <davecheney> wallyworld_: shoot <wallyworld_> davecheney: see https://codereview.appspot.com/6874049/patch/1/7, the TODO about 2/3 of the way down // TODO: WHY DOESN't THIS WORK? <wallyworld_> you may not have an idea of what's wrong. i can't see it right now <wallyworld_> the array ends up filled with the last id <wallyworld_> so if there are 2 servers, the array becomes [id2, id2] instead of [id1, id1] <wallyworld_> [id1, id2] i mean <davecheney> wallyworld_: yes, this is one of two gotchas with go <wallyworld_> only 2? <davecheney> (the other is also to do with scoping of variables <davecheney> (well, two that I consider gotchas) <wallyworld_> so what am i missing? <wallyworld_> i use append elsewhere in similar circumstances and it seems to work <davecheney> if you do s := server ; insts = append( ...) <davecheney> it will work <wallyworld_> wtf? why? <davecheney> hold on, i'll explain <davecheney> just looking at why you're doing := range *servers <wallyworld_> ok <wallyworld_> the api returns a pointer to an array of structs <wallyworld_> due to that big discussion i had on irc last week <wallyworld_> about returning nil if the json failed <davecheney> ok, i don't want to get into that <wallyworld_> me either :-) <wallyworld_> just providing context <wallyworld_> as to why it is a pointer <davecheney> if we break down the for loop, what is happening behind hte scenes you're getting <davecheney> var server Nova.server <davecheney> for i := 0 ; i < len(*servers); i++ { <davecheney> server = *servers[j] <davecheney> ... then your code <davecheney> } <davecheney> (this is part of the explanation) <wallyworld_> *servers[i]? <davecheney> do you agree that this can be subtituted for the range above <wallyworld_> yes <wallyworld_> with the i <davecheney> yes, sorry <wallyworld_> np, just making sure <davecheney> so inside each loop iteration it becomes <davecheney> server = *servers[i] (hidden) <davecheney> insts = append(insts, &instance{e, &server}) <davecheney> &server is capturing the address of the local server variable every time <davecheney> the same local server variable <davecheney> which is why they all end up as the last value assigned to it * wallyworld_ thinks <wallyworld_> davecheney: so by doing s:= server, it forces a copy to be made? <davecheney> yes <wallyworld_> ok, i think i get it. is the moral of the story not to use loop variables in certain circumstances? <davecheney> when taking their address <davecheney> sadly this is just one thing you have to be ever vigilant for <wallyworld_> right, ok. thanks so much. i wasted a loooong time on this <davecheney> sorry, it is one of the rights of passage <wallyworld_> lol <wallyworld_> i still don't feel like Go is "native" just yet, but it's getting there <davecheney> that's good to hear <davecheney> i'm constantly amazed at how quickly can pick up the language <davecheney> it tells me it's doing something right <wallyworld_> yeah, there's some stuff i don't like, but it's interesting to work with <davecheney> wallyworld_: btw, do you feel that any sort of decision was reached about the 'when and how should we meet' discussion ? <davecheney> in the meeting I got the impression, and looking at the notes, that the decision to not change anything was made <wallyworld_> my understanding was alternating times 21:00UTC and 09:00UTC <davecheney> then the day after Mark posted an email saying we're going to do that <davecheney> alternating times ?! <davecheney> i read that as two meetings <wallyworld_> it was explained to me as alternating and you would make whichever meeting you could <wallyworld_> or both <davecheney> but, alternating every other week ? <wallyworld_> yes <wallyworld_> unless i am mistaken <davecheney> so we can go an entire milestone cycle without meeting or feedback ? <wallyworld_> is each milestone cycle 2 weeks? <wallyworld_> i think for you and me we can make both <davecheney> you can see we run a pretty loose ship on juju-core, but yes, ish <wallyworld_> to be honest, the current one that is 10pm for me i can make most weeks <davecheney> it's 11pm for me <wallyworld_> yeah, DST :-) <davecheney> and i'm getting pressure from home <wallyworld_> which i guess is why they changed it <wallyworld_> timezones suck sometimes <wallyworld_> happy wife, happy life <wallyworld_> i'm not sure if tomorrow's meeting is still at 0pm, will have to check <davecheney> DST is bitch if you need to coordinate with western europe <davecheney> 7pm UK time is 6am AEST <davecheney> (or is that AEDT <wallyworld_> i thought it was 7am AEST <davecheney> nup <davecheney> http://everytimezone.com/ <wallyworld_> not 7pm, 9pm <wallyworld_> 21:00UTC <wallyworld_> so +10 is 7am for me <TheMue> Morning <davecheney> morning <TheMue> davecheney: Had a nice weekend? <rogpeppe2> morning all <TheMue> Hiya rogpeppe <fwereade> rogpeppe, TheMue, heyhey <fwereade> rogpeppe, TheMue: I would appreciate your opinions on the Living interface <TheMue> fwereade: Hi <rogpeppe> fwereade: just jotting down some thoughts i had over the weekend. will have a look in a bit, thanks for the heads up. <fwereade> rogpeppe, TheMue: it only seems to be used for the tests <fwereade> rogpeppe, TheMue: and I have slight concern that it doesn't really fit our needs <rogpeppe> fwereade: what CL are we talking about? <fwereade> rogpeppe, it's a piece that's already in place <rogpeppe> fwereade: ah <fwereade> rogpeppe, state/life.go <fwereade> rogpeppe, it has EnsureDying/EnsureDead <rogpeppe> fwereade: you're objecting to those methods? <fwereade> rogpeppe, we took those methods off Relation because they didn't make sense <TheMue> fwereade: If you say you have concerns that it fits our needs, then please start by definig what you think our needs are. <fwereade> rogpeppe, and while they're ok for unit they don't express enough <rogpeppe> fwereade: do you think Unit needs a richer interface than a simple "EnsureDead"? <rogpeppe> fwereade: or EnsureDying <fwereade> rogpeppe, yeah -- remove-unit --force <fwereade> rogpeppe, we need a way to say, politely, "please kill yourself"; a way to say politely "I'm done, clean me up"; and a way to force dead immediately <rogpeppe> fwereade: ah, so EnsureDying isn't allowed to take account of things that might block the EnsureDying <fwereade> rogpeppe, yeah, nothing should block an EnsureDying <rogpeppe> fwereade: emr <fwereade> rogpeppe, depending on the circumstances, the existence of subordinates may or may not want to block EnsureDead <rogpeppe> erm <rogpeppe> fwereade: istm that EnsureDying *is* a way to say, politely, "please kill yourself" <rogpeppe> fwereade: and that EnsureDead *is* a way to force dead immediately <TheMue> rogpeppe: That's how I understood it too. <fwereade> rogpeppe, yes... we have 3 things to express, and 2 0-arg methods <rogpeppe> fwereade: that doesn't sound like the current interface is wrong, but that some things may need more <fwereade> rogpeppe, maybe <TheMue> fwereade: Please match the current methods to your three sentences above to make sure which one you don't see covered. <fwereade> rogpeppe, bear in mind that it was already dropped from relation because it's useless <rogpeppe> fwereade: how do you remove a relation then? <fwereade> rogpeppe, TheMue: EnsureDying=please kill yourself, EnsureDead=die immediately; no way to politely signal your own death <fwereade> rogpeppe, it depends <fwereade> rogpeppe, I ca dredge up the details if you like <rogpeppe> fwereade: by "signal your own death", do you mean there's no way to tell others when you've died? <fwereade> rogpeppe, there is no convenient way for me to coordinate the unit's death amongst various tasks <TheMue> fwereade: So you want a kind of destructor. But that is something the entitiy has to signal. It isn't signalled from the outside to the entitiy. <rogpeppe> fwereade: i'm not sure what you mean by that <fwereade> rogpeppe, ok, maybe I should shift perspective around and see what sorts of statements we agree on <fwereade> rogpeppe, the only thing that can sanely set a machine to Dead is a machine agent; agreed? <fwereade> rogpeppe, and the only thing that can sanely set it to Dying is the user <TheMue> fwereade: How would you name that third method? And who will call it? <rogpeppe> fwereade: i'm not sure. what if the machine has gone away and isn't coming back? <fwereade> rogpeppe, do you mean the "instance" <rogpeppe> fwereade: yeah <fwereade> rogpeppe, I don't think that's relevant then? <fwereade> rogpeppe, juju will provision a new one and put the machine agent on it <rogpeppe> fwereade: do we have to resurrect the instance and its machine agent in order to kill the Machine? * TheMue still thinks it's a wrong perspective. The first two methods are telling the entity that it has something to to, the missing one is used by the entity itself to signal the outer world that a state is reached and the outer world has something to do. <TheMue> s/to to/to do/ <fwereade> rogpeppe, you're talking like we can *stop* juju from provisioning a new instance for a broken machine? <fwereade> TheMue, ok <fwereade> TheMue, what does service.EnsureDead do? <fwereade> TheMue, btw you are wrong re methods: if an entity is dead, it must *not* do anything, and in the normal case of affairs it doesn't even see itself become dead because it sets and terminates itself <TheMue> fwereade: It sets the entity to dead. <fwereade> TheMue, the notion of EnsureDead from outside the entity is only sane in the case of units, and that's only because we carved out an exception for remove-unit--force <fwereade> TheMue, ok, and what happens when you do that? <fwereade> TheMue, and more to the point, when is it ok to do that? <rogpeppe> fwereade: what if an instance goes down and the user calls terminate-machine before it's been reprovisioned? <TheMue> fwereade: I would have to take a look what's done today. But IMHO after an entity is marked as dead it can be cleaned up. <fwereade> rogpeppe, if we can detect that for sure, then we can remove it; no reason to make it dead <fwereade> TheMue, think through what will happen if we set a service to Dead <fwereade> TheMue, what should happen to the units of that service? <TheMue> fwereade: I'm currently looking from a more abstracte perspective about where methods have to apply. <rogpeppe> fwereade: doesn't it have to be dead before we remove it? <fwereade> rogpeppe, not at all, we don't do that with relations <TheMue> fwereade: IMHO they should be set to dead too, before the service is marked as dead. <fwereade> rogpeppe, I'm not sure the Dead state is even meaningful for relations or services <fwereade> TheMue, how will you craft your request to set 100k units to Dead? <TheMue> fwereade: I have no quick answer. But logically, if a service shall be set to dead, shouldn't the units be dead too? <fwereade> TheMue, using the txn package, remember <fwereade> TheMue, logically, yes <fwereade> TheMue, but there are 2 possibilities here <fwereade> TheMue, we can set services to dead, and get crack, or wee can admit that service.EnsureDead is (probably) crack <rogpeppe> fwereade: anyway, sorry that was a derail. say, for the sake of argument, we accept that only a machine agent can set its machine to dead. what then? <rogpeppe> fwereade: are you saying that if we set something to dead, we may as well just remove it? <fwereade> rogpeppe, ok, so machines are easy: Dying from outside, Dead from inside <TheMue> rogpeppe: Do we mix topics here? <fwereade> rogpeppe, not at all -- I'm saying that relations and services have no use for a Dead state, because we can just remove when appropriate <fwereade> rogpeppe, units and machines *do* have a use for the dead state * TheMue would like to stay on the path what should happen to units, independent of their number, when their service is said that it shall die. <fwereade> rogpeppe, but the agreement thus far has been that the only one we should be setting to dead from outside was unit, and that in a not-especially-high-priority feature <fwereade> TheMue, what we do is set the service to Dying <fwereade> TheMue, every unit is the responsible for setting itself to dying and cleaning itself up <fwereade> TheMue, the units' deployer waits for it to become dead <TheMue> fwereade: Is every unit watching it's services' lifecycle? <fwereade> TheMue, yes <TheMue> fwereade: Ah, ok, missed that, thanks. <rogpeppe> fwereade: it sounds like you're arguing for removing EnsureDead <fwereade> sorry 1 sec <fwereade> rogpeppe, I'm not stronly arguing *for* anything, I'm just trying to raise the topic for discussion in the hope something will be synthesized therefrom <fwereade> TheMue, to continue: then the deployer removes the unit <TheMue> fwereade: And markes it as dead? <fwereade> TheMue, the unit marked itself as dead <fwereade> TheMue, the deployer is then responsible for uninstalling it and removing it from state <TheMue> fwereade: How does it mark itself as dead? <fwereade> TheMue, and removing its service from state if it was the last member of a dying unit <fwereade> TheMue, at the moment it calls EnsureDead <TheMue> fwereade: The unit calls EnsureDead() on itself? Aha. <fwereade> TheMue, but this will not work correctly in the presence of subordinates <fwereade> TheMue, yes <fwereade> TheMue, in general only an entity is meant to declare itself dead <TheMue> fwereade: Could you give me a hint where in the code this happens? <fwereade> TheMue, the EnsureDead? worker/uniter/modes.go <fwereade> TheMue, ModeStop or something? ModeDie? <TheMue> fwereade: So the uniter calls it, not the unit. <fwereade> TheMue, what part of the unit has its own volition if not the uniter? <rogpeppe> back in a mo <TheMue> fwereade: It's only the wording I want to get sure. If I talk about Unit it's state.Unit. Not about the uniter. <fwereade> TheMue, sure, sorry, I didn't mean to be unclear <TheMue> fwereade: I only wanted to clear that to not be confused. <TheMue> fwereade: I never been deep in the uniter. So that's important to me to get a similar context. <fwereade> TheMue, I think of a Unit as a dumb type manipulated by interesting clients -- the clients are the agents and the command line <rogpeppe> back <TheMue> fwereade: OK, now it's more clear, thanks. <fwereade> rogpeppe, actually, how do you expect we will set a machine to dead? thinking about it, that sounds like a job for the machiner <fwereade> rogpeppe, and it sounds like exactly the same problem as the uniter has <rogpeppe> fwereade: is the provisioner right to allocate a new instance for a machine that's set to dying? <fwereade> rogpeppe, if it's still meant to be running a unit, yes, unless you have a better idea <fwereade> rogpeppe, but I forget: did we agree that we couldn't set a machine to dying until it had no assigned units? <rogpeppe> fwereade: i thought we could always set something to *dying* <fwereade> rogpeppe, I haven't thought so much about machine lifecycles, which is probably why I perceive them as a simple problem.. I bet they aren't really ;p <rogpeppe> fwereade: i have a suspicion we have some muddy thinking about the machine/instance/unit relationship <fwereade> rogpeppe, ok, so terminate-machine shouldn't actually terminate the machine? <fwereade> rogpeppe, ok, please explain your view of the situation <fwereade> rogpeppe, is it your position that terminate-machine should also terminate all the units on that machine? <rogpeppe> fwereade: i don't have a strong notion of *how* our thinking is muddy, just that i don't clearly understand things, and i'm not sure anyone else does <fwereade> rogpeppe, beacuse it has hitherto meant "I will never use this unused machine again", and been blocked by the machine's having units deployed <rogpeppe> fwereade: ok, well that sounds reasonable <fwereade> rogpeppe, ok, you know what? this makes me question EnsureDying/EnsureDead still more <rogpeppe> fwereade: so... say a machine is unused but its instance has just died; someone calls terminate-machine before the provisioner has noticed that the instance is dead. <rogpeppe> fwereade: when the provisioner notices that the instance is dead, should it allocate another one? <rogpeppe> fwereade: i don't see any question about EnsureDying <rogpeppe> fwereade: EnsureDead seems to be the problematic part of the interface <fwereade> rogpeppe, ok, what does it do? <fwereade> rogpeppe, please describe the chain of consequences from EnsureDying <rogpeppe> fwereade: the provisioner can see when an instance dies, right? <fwereade> rogpeppe, yeah, I think so, roughly <rogpeppe> fwereade: (i'm not actually sure that it can, tbh, but this we seem to believe it) <rogpeppe> s/this we/we/ <fwereade> rogpeppe, I think it s possible to infer it anyway <rogpeppe> fwereade: i'm not sure it is <rogpeppe> fwereade: in the presence of network failures <rogpeppe> fwereade: anyway, assuming we can <rogpeppe> fwereade: when the provisioner sees an instance go down, it allocates a new one and assigns the old Machine to it, right? <fwereade> rogpeppe, yeah <rogpeppe> fwereade: so if someone has called EnsureDying on that machine, should the provisioner really allocate a new instance for the machine? <rogpeppe> fwereade: (surely not?!) <fwereade> rogpeppe, if a machine is Dying it means "I will set myself to Dead when I have disacharged my responsibilities" <rogpeppe> fwereade: what is a machine without an instance? <fwereade> rogpeppe, I am reluctant to stipulate that the machine agent can die with no responsibilities undischarged <rogpeppe> fwereade: what responsibilities does a machine agent have if it has no units? <fwereade> rogpeppe, I suspect storage handling will come into play mostly at the machine agent level <fwereade> rogpeppe, but, ok -- that's a potential derail <rogpeppe> fwereade: i just think it's weird that we'd allocate a new instance only to shut it down immediately <fwereade> rogpeppe, what you say makes sense, but you're not taking it far enough <rogpeppe> fwereade: i think the provisioner can proxy for the machine agent in this instance <fwereade> rogpeppe, what does Dying actually mean if the unit agent can only go to Dying when it has no responsibilities left to discharge? <fwereade> rogpeppe, it means Dying is not a sane state for a Machine to have <fwereade> rogpeppe, it will become so if/when we change the rules ofc... <rogpeppe> fwereade: sorry, you're mixing up machine dying and unit dying in a way i don't quite understand <fwereade> rogpeppe, shit sorry s/unit/machine/ <fwereade> s/unit agent/machine/ actually <rogpeppe> fwereade: it sounds reasonably sane to me <fwereade> rogpeppe, what is the distinction between Dying and Dead for a machine? <rogpeppe> fwereade: i think it's a pity we haven't actually implemented terminate-machine yet... <fwereade> rogpeppe, although, sorry, I'm derailing a little and I have no particular interest in fighting this one -- I take a similar Dying shortcut in the Deployer, actually <fwereade> rogpeppe, but I'm actually questioning it <rogpeppe> fwereade: because you're saying that a machine can only go to Dying when it has no responsibilities left to discharge, but that's not the case currently <rogpeppe> fwereade: anyone can set a machine to dying at any time <fwereade> rogpeppe, ok, well then that is surely crack <fwereade> rogpeppe, why timebomb your machines like that? <rogpeppe> fwereade: i'm not sure why <rogpeppe> fwereade: you might have a good reason <rogpeppe> fwereade: you might not want a new machine to be allocated <fwereade> rogpeppe, the only thing that does is to say "keep this machine around for an arbitrary length of time, but don't forget the irrevocable kill command that will take effect when its current purpose is fulfilled" <rogpeppe> fwereade: so calling terminate-machine before removing a unit means that we can ensure that's true <rogpeppe> fwereade: that seems quite plausibly useful to me <fwereade> rogpeppe, but you're also suggesting that the unit should get no opportunity to shut down cleanly if the instance goes away <fwereade> rogpeppe, it's a total niche case <fwereade> rogpeppe, defaulting to shutting down machines when unused would be moreuseful <fwereade> rogpeppe, weeks-delayed kill commands are pretty esoteric ;) <rogpeppe> fwereade: if we think that automatically shutting down a machine when it's unused is a good thing, then sure. but i think we've decided that it's not. <fwereade> rogpeppe, I am working on the asusmption that we will have some sort of persistent storage generally available, such that moving from one instance to another can be accomplished almost trivially <fwereade> rogpeppe, my point is that if we're going to get clever about machine termination, delayed-kill is not the top priority <rogpeppe> fwereade: well maybe, but there is overhead in starting an instance, and state in an instance that may not be easily stored. <rogpeppe> fwereade: i think it falls out naturally from our model. <fwereade> rogpeppe, yeah, depends on the provider <rogpeppe> fwereade: and it *is* potentially useful behaviour <fwereade> rogpeppe, it is potentially useful behaviour that we should restrain ourselves from implementing until we understand the use cases <fwereade> rogpeppe, you know what <fwereade> rogpeppe, Unit.EnsureDead, implemented correctly, is just *horribly* complex <rogpeppe> fwereade: because it's hard to check all the unit's dependencies before setting state to dead? <fwereade> rogpeppe, it needs to clean up some or all of: machine assignment, subordinates Dead-setting, relation removal, and service removal <fwereade> rogpeppe, if we do it correctly, we have to just build up some monster transaction <fwereade> rogpeppe, not to mention just cleaning up relation membership <rogpeppe> fwereade: that's not Unit.EnsureDead - that's RemoveUnit <rogpeppe> fwereade: EnsureDead and EnsureDying don't do anything other than set life state <fwereade> rogpeppe, ok, drop service removal <fwereade> rogpeppe, that's the only one <rogpeppe> fwereade: it's up to the caller to make sure that the other invariants are preserved correctly. <fwereade> rogpeppe, ha ha <fwereade> rogpeppe, how is that possible? <rogpeppe> fwereade: well usually we do that be not allowing new things to be added when something is in a dying state. <fwereade> rogpeppe, to make sane transitions in system state we have to do these things as single transactions <Aram> good morning <rogpeppe> Aram: hiya! <fwereade> Aram, heyhey <fwereade> Aram, nice holiday? <Aram> yeah. <fwereade> Aram, cool <rogpeppe> Aram: is your holiday finished now? <Aram> yeah. <fwereade> Aram, I landed your watchers -- we've identified the firewaller bug and TheMue is looking into it this week AIUI <TheMue> fwereade, Aram: +1 <Aram> fwereade, could you please link me to the commit that fixed it? <TheMue> Aram: Hello btw. <Aram> hi. <rogpeppe> fwereade: i'm not sure we do need to do these things as single transactions <rogpeppe> fwereade: and that's why we have the dying/dead distinction <fwereade> rogpeppe, Dying/Dead is about handover of responsibility surely <TheMue> Aram: It's not yet fixed, only better identified. I'm now working on it with two CLs. <fwereade> rogpeppe, but any time we want to make a change to the state, we need to do it as a single transation <fwereade> rogpeppe, or write a CA <rogpeppe> fwereade: CA? <fwereade> rogpeppe, Corrective Agent ;) <rogpeppe> fwereade: ha <TheMue> Aram: And LXC is right now stopped, one CL has gone in, two are open. MAAS has gotten higher priority. <rogpeppe> fwereade: but that's not true. we don't need to make every change to the state as a single transaction <Aram> fwereade, I've seen a proposal to replace int with string, excellent. <fwereade> Aram, (there was a MUW bug that was contributing to that test failing, I'll find that commit, just a mo) <rogpeppe> fwereade: and in this case, after the unit is set to dying, if we use several transactions to find that all its dependencies have gone away and it's in dying state, then we know we can remove it <rogpeppe> fwereade: or set it to dead <Aram> meh, the damn unity panel can't be set in a different position, and auto hide doesn't work in vmware. <rogpeppe> Aram: so run ubuntu natively :-) <Aram> I bought a new mac. <TheMue> Aram: It's possible to auto-hide it, have to find the switch again. Somwhere in the settings. <Aram> TheMue, it's possible to auto hide it, it's impossible to appear again after you've done so. <Aram> that's because compiz is too smart for its own good. <TheMue> *lol <Aram> it calculates how hard you've hit the screen edge. <Aram> since vmware won't deliver mouse events after you've hit the edge, it can't calculate how hard you hit it. <Aram> so it won't show up. <fwereade> so, hey again all <rogpeppe> fwereade: your network connection die? <fwereade> rogpeppe, power cut <rogpeppe> fwereade: ah, not useful <fwereade> rogpeppe, didn't last long :0 <fwereade> rogpeppe, ok, so, the trouble with lifecycle discussions is that there are in the end fewer commonalities than one might think <rogpeppe> fwereade: did you see my last commend ("... or set it to dead")? <rogpeppe> comment <fwereade> rogpeppe, ah, sorry, I didn't <rogpeppe> fwereade: ah <rogpeppe> [09:59:49] <rogpeppe> fwereade: but that's not true. we don't need to make every change to the state as a single transaction <rogpeppe> [10:00:36] <rogpeppe> fwereade: and in this case, after the unit is set to dying, if we use several transactions to find that all its dependencies have gone away and it's in dying state, then we know we can remove it <rogpeppe> [10:00:42] <rogpeppe> fwereade: or set it to dead <fwereade> rogpeppe, ok, so what's the scenario you're considering there? <rogpeppe> fwereade: i was replying to your "but any time we want to make a change to the state, we need to do it as a single transation" comment <fwereade> rogpeppe, yeah, and you're explaining a situation in which we don't have to worry about it, and I'm asking for more details <fwereade> rogpeppe, I think :) <fwereade> rogpeppe, who is setting the unit to dying, and why? <rogpeppe> fwereade: i was talking about setting the unit to dead, not dying <rogpeppe> fwereade: anyone can set the unit to dying <mramm> network here is a bit flakey, but I'm around again for the next few hours today, and then traveling back to the US tomorrow. <rogpeppe> mramm: i guess you'll miss the meeting then <mgz> dimitern: want to catch up, even though it'll only be us two? <mramm> rogpeppe: yea <mramm> rogpeppe: I also need to figure out better meeting times <fwereade> rogpeppe, ok, yes, that is true; it is not hard to set a unit to dead once we know it's done everything <fwereade> rogpeppe, but the tricky case is EnsureDead from the outside <rogpeppe> fwereade: i don't see that's a problem. the only thing that can call EnsureDead is something that knows it can act for the object. <rogpeppe> fwereade: which is usually the agent responsible for the object, but may not be, in some cases. <fwereade> rogpeppe, ok, so when someone uses remove-unit --force... <fwereade> rogpeppe, that client is basically taking on all the responsibilities of the unit agent, right? <rogpeppe> fwereade: i'm not sure <fwereade> rogpeppe, well, the UA won't do anything else after it's dead <rogpeppe> fwereade: i don't think the client can call EnsureDead in that case <dimitern> mgz: sure, i'm starting mumble now <fwereade> rogpeppe, its responsibilities must either be transferred elsewhere, or discharged, in the same transaction that makes it Dead <rogpeppe> fwereade: i think it just means "unit: please remove yourself regardless of the dependencies" <fwereade> rogpeppe, I don't think there's any command that means "please corrupt state" <rogpeppe> fwereade: i don't see why it all needs to happen in the same transaction <fwereade> rogpeppe, ok, so what happens when the first change is made and then the connection is dropped? <rogpeppe> fwereade: all we need to do is set the unit to dying and some flag, say "force" that means that the unit will kill itself even when it has relations etc. <rogpeppe> s/"force"/"force",/ <fwereade> rogpeppe, please expand on what you mean by "kill itself" <fwereade> rogpeppe, it needs to exit the relations even if it isn't running hooks <rogpeppe> fwereade: well, perhaps it might be good if you could explain the sematics of remove-unit --force. <fwereade> rogpeppe, the idea was from possibly the first lisbon <rogpeppe> semantics <fwereade> rogpeppe, make the unit Dead <fwereade> rogpeppe, the trouble is that "make the unit Dead" is not necessarily a simple change to state <rogpeppe> fwereade: at a higher level, why do we want this? <fwereade> rogpeppe, because a broken UA is a hugely awful failure, that otherwise blocks the removal of machines, services, and relations <fwereade> rogpeppe, it's a feature we hope nobody will use <rogpeppe> fwereade: ok. are we talking about a broken unit agent, or a dead instance? <fwereade> rogpeppe, a broken unit agent <fwereade> rogpeppe, that has bad code, and bad upgrading code too, and that we have no other way of removing <rogpeppe> fwereade: so if we know it's broken, then we know we can act for it, right? <fwereade> rogpeppe, sure, I'm 100% comfortable with the idea that we can tak on its responsibilities <fwereade> rogpeppe, I'm just saying that if we do so we must either discharge them all, or transfer responsibility for doing so elsewhere, and that it must be done in the same transaction as that in which we set the unit to dead <rogpeppe> fwereade: ok, so something else must take on its responsibilities for a while and see the unit through its last phases. <fwereade> rogpeppe, so you want two different sorts of Dying state <rogpeppe> fwereade: i still don't see that last point <rogpeppe> fwereade: no, i don't <fwereade> rogpeppe, we are talking about setting the unit to dead <fwereade> rogpeppe, there are a potentially large number of things in state that need to be changed in order for the unit to become dead and state to remain consistent <rogpeppe> fwereade: i thought we were talking about forcing the unit to die even when its agent is broken <rogpeppe> fwereade: that's a somewhat different thing <fwereade> rogpeppe, er, "setting the unit to dead" is what the unit agent would have done were it not broken, right? <rogpeppe> fwereade: i suspect we might want another worker/agent to manage this <fwereade> rogpeppe, the whole point is setting it to dead <fwereade> rogpeppe, that is how we communicate that the unit agent can be safely trashed <rogpeppe> fwereade: well, presumably the unit agent will manage the "potentially large number of things" along the road to deadness, yes? <rogpeppe> fwereade: setting it to dead indicates that the unit agent *is already* trashed <fwereade> rogpeppe, when we set Dying, that is exactly what the uniter does, yes <fwereade> rogpeppe, not even slightly <fwereade> rogpeppe, setting it to dead is what lets its deployer know that it can be trashed <rogpeppe> fwereade: ok, so we need something else to take on the responsibility for the work that the unit agent normally does <fwereade> rogpeppe, <fwereade> rogpeppe, its responsibilities must either be transferred elsewhere, or discharged, in the same transaction that makes it Dead <rogpeppe> fwereade: ok, that's a different level of trashing <fwereade> rogpeppe, ok: AIUI the deployer is responsible for both kinds <fwereade> rogpeppe, once something is Dead, the thing responsible for it releases any underlying resources and removes it from state <rogpeppe> fwereade: AFAICS the deployer is responsible for maintaining the container of the unit <rogpeppe> fwereade: yes <rogpeppe> fwereade: and that's not the problem. the problem is the transition to death <fwereade> rogpeppe, to Dead? <rogpeppe> fwereade: yeah <fwereade> rogpeppe, yes <rogpeppe> fwereade: and we can't solve that by simply charging in and calling EnsureDead <fwereade> rogpeppe, depending on what EnsureDead means, bu yes <rogpeppe> fwereade: but i don't see that it must all be called in the same transaction either <mramm> hotel wifi at 25% packet loss :( <fwereade> rogpeppe, it sounds like yu are advocating for a corrective agent <fwereade> rogpeppe, or task <rogpeppe> fwereade: perhaps you could outline the things that the unit agent does after seeing Dying and before it calls EnsureDead? <fwereade> rogpeppe, depart all its relations, basically <rogpeppe> fwereade: perhaps; i'm not sure yet. <fwereade> rogpeppe, this is not necessarily a simple operation <rogpeppe> fwereade: each relation depart can be done as a single transaction? <fwereade> rogpeppe, because some of those relations might themselves be dying <fwereade> rogpeppe, yes, they could <rogpeppe> fwereade: so esssentially it does: for r in relations {r.Depart()}; u.EnsureDead() ? <fwereade> rogpeppe, you are proposing that we depart them each individually, and assuming that the unit agent is not currently running and will never come back? <fwereade> rogpeppe, remove-unit --force needs to work even if the UA suddenly wakes up <fwereade> rogpeppe, while the unit is not dead, the UA has responsibility for it <rogpeppe> fwereade: if relation departing is idempotent, i think that could work <fwereade> rogpeppe, I am open to discussion re when/how that responsibility is transferred/discharged <mramm2> hotel wifi at 25% packet loss, falling back to cell phone data plan but on edge network, so only a half step above IPoAC <fwereade> rogpeppe, I don't think so -- I can imagine a path by which the UA could rejoin one of those relations in between the depart and the ensuredead <rogpeppe> fwereade: can a UA rejoin a relation when the unit is marked as dying? <fwereade> rogpeppe, why would the unit be marked as dying? <rogpeppe> fwereade: isn't that one of the transaction guards? <fwereade> rogpeppe, we're talking about forcing Dead from any state <rogpeppe> fwereade: because that's the first thing we must do when calling remove-unit --force, surely? <rogpeppe> fwereade: that's how we manage to shut down cleanly in multiple transactions <fwereade> rogpeppe, I guess that's probably not too harmful, although I am strongly -1 on state changes that are not expressed as single transactions <niemeyer> fwereade: How would the deployer even know to kill the unit if it wasn't market as dying <niemeyer> Good mornings, btw <fwereade> niemeyer, heyhey <TheMue> niemeyer: Morning <fwereade> niemeyer, the deployer removes the unit's it's responsible for when they become Dead <fwereade> niemeyer, they shouldn't care that their units are Dying <fwereade> sorry errant ' <niemeyer> fwereade: Ah, right, okay <fwereade> niemeyer, Dying is a signal to the UA to clean itself up and mark itself Dead when it's ready <niemeyer> fwereade: Yeah, you're right.. something else has to mark it as Dead in that edge case being debated <fwereade> niemeyer, yeah, and the requirements for the two cases are rather different, so a single EnsureDead method with no parameters is not enough to express what we need to do <rogpeppe> fwereade: if we want it to be expressed as a single transaction, then we need an agent to do the rest for us <rogpeppe> fwereade: call it the CA if you want <fwereade> niemeyer, in a clean shutdown, the uniter shouldn't set itself to Dead until it has no remaining subordinates <fwereade> niemeyer, in a forced death, we need to make all the subordinates dead as well, I think -- but I guess that is a red herring, it's just where I started the conversation from <rogpeppe> fwereade: i don't think that remove-unit --force should simply call EnsureDead to do the work <fwereade> niemeyer, the relations question is much more interesting <niemeyer> fwereade: It's an interesting case, actually <fwereade> rogpeppe, ok, sorry, I have been talking a lot <fwereade> rogpeppe, would you outline how how would expect the feature to work? <fwereade> s/how how/how you/ <rogpeppe> fwereade: i'd expect the forced shutdown to go through the same phases as a non-forced shutdown <rogpeppe> fwereade: and i would expect it to error out if the agent involved is actually alive. <fwereade> rogpeppe, ok, but when the unit is Dying its agent is still the responsible entity <rogpeppe> fwereade: mebbe <fwereade> rogpeppe, shouldn't remove-unit --force be able to forcibly remove the unit whatever it's doing? <rogpeppe> fwereade: yeah, probably <rogpeppe> fwereade: in which case we need to make the shutdown phases work even when multiple entities are performing them <niemeyer> fwereade: It should, but if that's done when the unit is not even dying, it feels like we should do something to not create unnecessary harm <fwereade> rogpeppe, well, we should have been doing so anyway, right, what with the txn runners <rogpeppe> fwereade: yes, except you were saying you can't do it all in one transaction. which seems a reasonable issue. <fwereade> niemeyer, rogpeppe: yeah, I think it's fine to start remove-unit --force by setting Dying <niemeyer> fwereade: and having a grace period, maybe <fwereade> niemeyer, rogpeppe: I think it's ok to have remove-unit always set the unit to Dying <rogpeppe> niemeyer: that, presumably, would mean that remove-unit --force would need to take as long as that grace period... or we'd need an agent to do it. <niemeyer> rogpeppe: Assuming the unit wasn't already Dying <fwereade> niemeyer, rogpeppe: and then if --force is set, aggressively go around trashing its stuff until it can call EnsureDead (or finds that the unit has done so itself) <rogpeppe> fwereade: that's what i'm thinking <rogpeppe> niemeyer: if it is already dying, we ignore the grace period? <niemeyer> rogpeppe: Yeah, I think that sounds reasonable <fwereade> niemeyer, I rather feel that the grace period is unnecessary -- if we're forcing then we're stating that we don't want or need a clean shutdown, we just want it gone <rogpeppe> niemeyer: it seems slightly odd that. {remove-unit; remove-unit --force} is different from {remove-unit --force} <niemeyer> fwereade: That sounds like unnecessary mess in the system <niemeyer> Alternatively, we could disallow --force to be used without first calling without force <niemeyer> Which would set dying <niemeyer> THis would be a better teaching instrument than the grace period <rogpeppe> niemeyer: yes, that sounds better <fwereade> niemeyer, no remove-unit --force unless already dying SGTM <niemeyer> fwereade: Yeah <rogpeppe> niemeyer: then the user can choose their own grace <niemeyer> rogpeppe: Right, the issue becomes obvious * fwereade is a little worried that the UA *will* be going around re-entering relations <rogpeppe> fwereade: can it reenter a relation if the unit is dying? <fwereade> niemeyer, rogpeppe: and hence that --force might have to grind away at it a bit * fwereade check <niemeyer> fwereade: How can it re-enter a relation if it's dying? <niemeyer> Erm.. what Roger siad <niemeyer> said <fwereade> niemeyer, it shouldn't leave scope until it is completely done with the relation <rogpeppe> fwereade: "leave scope"? <niemeyer> fwereade: I still don't get it <niemeyer> fwereade: How can it *re-enter* while dying? <fwereade> niemeyer, because calling Join() when we're adding a relation we expect to do stuff with works right even if it's done twice <fwereade> niemeyer, let me double-check when we leave <niemeyer> fwereade: I still don't see the reason why it works while Dying there <fwereade> niemeyer, ok, do you agree that a unit should not leave a relation scope until it has run the relation-broken hook? <fwereade> niemeyer, (possibly when UA is down something else may leave scope for it, that is ont the question right now) <niemeyer> fwereade: Okay <niemeyer> fwereade: I still don't see the connection though <fwereade> niemeyer, the purpose of the scope doc is to indicate that a unit still needs the relation to be alive <niemeyer> fwereade: I understand that <fwereade> niemeyer, a Dying unit that has not run all its hooks needs that relation to be alive <niemeyer> fwereade: Yes, and how did we get to re-entering relation <niemeyer> s <fwereade> niemeyer, so when it comes up, it ensures that (if it knows itself to be part of the relation) its scope doc exists <niemeyer> fwereade: Yep <niemeyer> fwereade: So..? <fwereade> niemeyer, with the proposed style of remove-unit, we will be leaving its scope from outside, and it will be erroring out, coming back up, and rejoining scopes we've already exited <fwereade> niemeyer, the 5s break should be enough to make that only happen once <fwereade> niemeyer, maybe not at all <niemeyer> fwereade: Do we allow it to create scope documents even when it is dying? <niemeyer> fwereade: *create* scope documents <fwereade> niemeyer, there's no distinction at the API level -- EnterScope could be EnsureInScope <niemeyer> fwereade: Can you answer the question, for once? :-) <fwereade> niemeyer, yes, we allow it to create scope documents at any time we are certain that it is appropriate for it to have a state document, which includes when the unit is Dying <fwereade> s/state doc/scope doc/ <niemeyer> fwereade: Okay, i don't think we should allow it to create scope documents whne when it is dying <niemeyer> fwereade: It makes no sense to be establishing new participation in relations when the unit is dying <fwereade> niemeyer, I could just as easily say it makes no sense to be messing around with a unit's state while the unit is Dying and still responsible for its own state <niemeyer> fwereade: You could, but I don't get what you mean by that <niemeyer> fwereade: I'm pinpointing a very specific case which I was hoping would be easy to agree or disagree on itself <niemeyer> fwereade: If the unit is not in a scope, and is in a dying state, it doesn't make sense to be joining relation it wasn't in, AFAICS <fwereade> niemeyer, why are we even considering relations it's not already in? <niemeyer> * fwereade is a little worried that the UA *will* be going around re-entering relations <fwereade> niemeyer, "at any time we are certain that it is appropriate for it to have a scope document" means "when we know we're already part of the relation" <fwereade> niemeyer, if the unit agent is running and knows it should be in a relation it will call EnterScope <niemeyer> fwereade: There's someone else that disagreed meanwhile.. sounds like a straightforward race <niemeyer> fwereade: Yes, and then it's put to Dying, and that should fail <fwereade> niemeyer, well, yeah, I am a bit blindsided by all of this <fwereade> niemeyer, meh, no trouble, we can add that to the transaction <niemeyer> fwereade: Exactly <fwereade> niemeyer, induce a few errors while it's going down is not really a big deal I guess <niemeyer> fwereade: We don't have to induce errors.. EnterScope already does verifications of that nature <niemeyer> fwereade: Since we already disallow entereing a dying relation <niemeyer> fwereade: For similar reasons, I suspect <fwereade> niemeyer, we will absolutely be inducing errors by purposefully corrupting the uniter's state <niemeyer> fwereade: How's that "corrupting the uniter's state"? Putting a unit to dying is a normal condition <fwereade> niemeyer, looping through all its relations, leaving scope for each, while the uniter is valiantly trying to run its hooks *is* corrupting the uniter's state <niemeyer> fwereade: I'm talking about the case above still <fwereade> niemeyer, whether or not EnterScope should work when the unit is dying? I have no problem changing that, it's not a big deal, it will make no observable difference to the system AFAICT <niemeyer> <fwereade> niemeyer, meh, no trouble, we can add that to the transaction <niemeyer> <niemeyer> fwereade: Exactly <niemeyer> <fwereade> niemeyer, induce a few errors while it's going down is not really a big deal I guess <fwereade> niemeyer, sensible check, though, definitely a good thing <niemeyer> fwereade: That's the context in which you mentioned corrupting state and which I answered to <fwereade> niemeyer, ok, AIUI you are advocating roger's proposed style of remove-unit --force <fwereade> niemeyer, which is all about corrupting the unit's state as it runs <niemeyer> fwereade: I don't actually know. What was roger suggesting? <fwereade> niemeyer, EnsureDying(); leave scope for each relation unit; EnsureDead <niemeyer> fwereade: Is there an alternative you were suggesting? <fwereade> niemeyer, I had felt that the sane thing to do was to either have an EnsureDead transaction that cleans up the necessary state in one go <fwereade> niemeyer, well, not necessarily sane <fwereade> niemeyer, the alternatives I saw were: <fwereade> niemeyer, 1) a potentially large and unwieldy transaction <fwereade> niemeyer, 2) some complicated corrective agent <fwereade> niemeyer, and I was not very happy with either <niemeyer> fwereade: I think there's another alternative that might be easier to implement <fwereade> niemeyer, rogpeppe has suggested just taking joint responsibility while the UA is running, and trying to shut itself down cleanly, by shutting everything down for it -- surely inducing errors -- and then setting it to dead when that's done <fwereade> niemeyer, I honestly don't like that solution much either <niemeyer> fwereade: Having the deployer being responsible for double-checking that the unit resources have been cleared if death was forced <niemeyer> fwereade: After killing the software, before removing from state <fwereade> niemeyer, that STM like a special case of "complicated corrective agent" <fwereade> niemeyer, but worth exploring, cool <niemeyer> fwereade: That sounds like cleaning resources to me.. <niemeyer> fwereade: Can't get simpler than that <niemeyer> fwereade: We can't clear resources without clearing resources <fwereade> niemeyer, ok, this to me somewhat changes the balance of Dying/Dead <niemeyer> fwereade: How? It doesn't seem to change them at all to me <fwereade> niemeyer, but could probably work very nicely, once we have containerisation in place <niemeyer> fwereade: I don't think waiting is necessary <fwereade> niemeyer, I'm pretty sure we had agreed that eg a principal should not be able to become Dead while its subordinates were still around <fwereade> niemeyer, because otherwise the subordinate would be holding a reference to a dead object it was not responsible for destroying <fwereade> niemeyer, and that would be a Bad thing <niemeyer> fwereade: Hmm.. yeah.. so how does that change the picture? <fwereade> niemeyer, so setting something to Dead, and having its deployer clean up the its various other connections of the system, seems wrong to me <niemeyer> fwereade: Sorry for being slow, but I don't understand the underlying point being made <fwereade> niemeyer, I had been working from a picture in which Dead was as good as removed from the perspective of the single entity responsible for actual removal <niemeyer> fwereade: --force means the thing itself can't clean up its connection to the rest of the system <niemeyer> fwereade: Something has to do that <fwereade> niemeyer, ok, you are saying that the deployer can clean up a unit's relations after the relation is dead? or not at all? <niemeyer> fwereade: If you want to do that at the command line, you'll be "corrupting the state" <niemeyer> fwereade: The only thing that can be sure the thing is actually dead is its runner <fwereade> niemeyer, yes, it is something else's responsibility to do the cleaning up <niemeyer> fwereade: Which is the deployer <niemeyer> fwereade: So that's the place we can clean up without corrupting the state <niemeyer> fwereade: But you don't think that's right, because..? <fwereade> niemeyer, a dead unit that's still participating in relations *is* corrupt state IMO <niemeyer> fwereade: We can ask the deployer to force its death without putting its status to Dead <mramm> BTW, just sent an e-mail with updated meeting times -- and moved them to thursday for this week since I'm traveling tomorrow. <niemeyer> mramm: Cool, cheers <mgz> mramm: saw that. won't be there for thursday (as I'm travelling) but the rest of our lot should be. <mramm> mgz: no problem. We are bound to be missing one or two folks most weeks in december! <fwereade> niemeyer, ok, so: a flag on units, checked by their deployer, that when set (1) causes the uniter to treat its unit as Dead and (2) causes its deployer to clean up its state, setit to dead, and uninstall it? <mramm> mgz: probably more most weeks. <niemeyer> fwereade: The uniter doesn't have to change.. as far as it knows, it's already Dying, so it *should* be aborting its activities <fwereade> niemeyer, and so it will be <niemeyer> fwereade: The force flag action would happen purely on the deployer <fwereade> niemeyer, ok, isn't it better for the uniter to see that someone else has taken responsibility, and just stop doing things? <niemeyer> fwereade: It will stop doing things, because the deployer will shoot it in the head <niemeyer> fwereade: There's no point in sending him a postcard telling its being shot in the head :-) <niemeyer> (and by now I'm probably in a list of dangerous people somewhere) <fwereade> niemeyer, haha <fwereade> niemeyer, this somewhat complicates the UnitsWatcher <fwereade> niemeyer, or the clients I guess, I just need it to send all changes <niemeyer> fwereade: I don't think it should change <fwereade> niemeyer, ok, so the deployer needs to set up kill-switch watches on each of its units itself? <mramm> also, friendly reminder to folks: put your vacation on the Juju Calendar so we all know it. <niemeyer> fwereade: Although, don't we already monitor all changes <niemeyer> fwereade: On units <fwereade> niemeyer, no, that watcher filters it to just lifecycle changes <niemeyer> fwereade: What does that mean in practice? Life is a field in the unit <fwereade> niemeyer, yes, when it gets a unit change it discards it unless Life has changed <niemeyer> fwereade: Right <niemeyer> fwereade: That sounds like a life-related change, and is in the same place as the other settings <niemeyer> fwereade: We'd just be comparing Life+ForcedDeath or whatever, instead of Life <niemeyer> fwereade: (just a strawman for now) <fwereade> niemeyer, sure, it's just a change to the watcher and a change to the deployer to handle the new possibilities <niemeyer> fwereade: Right <fwereade> niemeyer, I'm not suggesting it's the end of the world :) <niemeyer> fwereade: Sure, I'm just testing waters by suggesting it's actually simple
{'channel': 'juju-dev', 'month': 12}