[00:00] apps should not be rotating their own logs [00:00] twisted tried that and it was miserably shit [00:00] bigjools: was talking about reopening on HUP [00:57] wallyworld_: aren't you off today? [00:57] thumper: yeah, about to go to in [01:00] thumper: if someone "not lgtm"s something, I need that same person to lgtm to go ahead - is that right? [01:01] axw: it depends :) [01:01] axw: what is it? [01:01] just a sec [01:01] thumper: https://codereview.appspot.com/12924043/ [01:01] * thumper looks [01:02] axw: depends, could you take them in a straight fight ? [01:02] heh :) [01:11] axw: if you can address my comments, I can give you an LGTM [01:11] axw: william's opposition was due to no test, since you've added one, I think you'll be fine [01:11] * thumper goes to check on the pizza in the oven [01:12] thumper: thanks [01:16] thumper: that LC_ALL thing is existing. do you want me to update those things as I go? [01:17] :) do you feel like making it more up to date? [01:17] ... yes [01:17] :) [01:22] I think I'll write a tool to check import groups [01:22] prevent myself from submitting crap [01:26] https://bugs.launchpad.net/juju-core/+bug/1212903 [01:26] <_mup_> Bug #1212903: cmd/juju: debug hooks should set a better PS1 [01:34] axw: That bug ^^^ seems pretty trivial to do right? [01:34] * axw looks [01:35] thumper: yes should be pretty simple [01:37] I'll take a look in a moment [01:39] thumper: axw yeah [01:39] it would be super useful to have for our upcoming engagements [01:39] we're always zooming the screen [01:39] then the tmux output and the path become a real issue [01:40] for a charm the standard path consumes almost all the screen width [01:40] davecheney: I fully agree with the bug description [01:41] I think it is sound [01:41] sweet [01:41] i didn't think it would be a technical issue to fix [01:41] more ... philosophical [02:13] axw: you aren't looking at breaking up the agent config are you? [02:13] axw: because if you aren't, I'll take that on [02:13] thumper: not right now [02:13] okey dokey [02:13] I kinda need it for some other work [02:14] thumper: nps. I'll work on bugs and continue with the manual provisioner [02:14] as after talking with william, he convinced me that shoving env vars into the upstart scripts isn't very good [02:14] heh ok :) [02:14] rewriting upstart scripts on upgrade is a little crazy [02:14] but adding stuff to agent config isn't too bad [02:14] right [02:16] * thumper recalls the need to write an email about juju containers [02:48] https://bugs.launchpad.net/juju-core/+bug/1212916 [02:48] <_mup_> Bug #1212916: uniter/worker: unit agent MUST remove stale unix socket [02:56] davecheney: if you put an importance on the bug, please mark it triaged [02:57] thumper: will do [02:57] sorry [02:57] will fix [02:57] davecheney: I've done those two [02:57] but for future reference [02:57] ok, i understand now [03:00] thumper: thanks, I've updated checkers -> jc. will merge [03:33] axw: ack [03:50] axw: if you run the trunk tests, do your ec2 ones pass? [03:50] * thumper wonders if goamz has been updated [03:50] thumper: goamz has indeed been updated [03:50] are you getting a TestInstanceState failure? [03:51] yes [03:51] * thumper pulls goamz and tries again [03:51] I'll send an email now. Sorry, should have done it last week [03:52] I am prepared to buy drinks for whoever makes the ec2 tests run faster (without skipping or producing incorrect results) [03:52] thumper: rogpeppe said he sped it down to 6s or something like that [03:52] but decided not to commit it? [03:52] IIRC it required modifying goamz itself [03:52] I think there's some global variable that would affect the normal operation [03:52] well, if he fixes it, I'll buy him some drinks :) [04:36] https://bugs.launchpad.net/juju-core/+bug/1212936 [04:36] <_mup_> Bug #1212936: cmd/juju: debug-hooks without hook fired should place the user into a simple hook environment [05:39] relation question [05:39] can you run the relation-get hook command during the relation-departed hook ? [06:12] mornin' all [06:22] morning rogpeppe [06:22] axw: hiya === tasdomas_afk is now known as tasdomas [07:24] dimitern: Hi! If you have a few minutes at some point, can you read what I tried RE testing with RegisterControlPoint etc on the decscription of https://codereview.appspot.com/12795045/ , and let me know what I have missed, or whether it's something that needs to be added to goose? [07:37] noodles775: I think an update to goose is in order, so that you can tweak the instance state in the test double [07:37] noodles775: sorry, I was wrong that you can tweak it without code changes [07:40] dimitern: k - do you think I should do that before landing that branch (ie. so it doesn't land without tests), or it's OK as is? === racedo` is now known as racedo [07:42] noodles775: I don't think increasing the timeout fixes anything [07:42] noodles775: it's still fragile [07:43] noodles775: if anything, we should fix the nova server filter to be smarter and return stuff not only in ACTIVE or BUILD states, but also hp-cloud specific BUILD(spawning) [07:44] dimitern: Yeah, I did try that, but it's still fragile (takes about 8-9 seconds to get to BUILD(spawning) from my testing) [07:44] noodles775: and to test it, you'll need to change goose to allow you to change the instance state when you start a server with the test double [07:45] noodles775: ok, let me rephrase - timeout by itself is not enough to fix it, you'll need the above things as well [07:47] noodles775: fixing the filter to use HasPrefix instead of == should be trivial [07:47] dimitern: That doesn't work - I tried that (see the description). [07:48] noodles775: it doesn't work, because it returns BUILD(networking) and at that state the instance is not yet accessible? [07:48] dimitern: It means that it returns as soon as HP sets BUILD(networking), but juju-core then never connects (I didn't dig deeper, but I assume we make some assumptions)... [07:48] Yes. [07:48] well, it seems obvious that the network is not yet up at that point [07:49] Right - but it continues to not connect. Let me paste you output, it may make more sense. === tasdomas is now known as tasdomas_afk === tasdomas_afk is now known as tasdomas [07:58] dimitern: http://paste.ubuntu.com/5991914/ [08:00] noodles775: my point is, instead of HasPrefix, you could use server.Status == nova.StatusBuild || serverStatus == "BUILD(spawning)" (perhaps even define this as const HPStatusBuildSpawning) [08:01] noodles775: then run it 10 times or more to see if it passes every time, if sometimes it doesn't, then increase the timeout [08:01] dimitern: Right, I did do that and it then takes about 8-9 seconds - so it is better than 15s that it takes for ACTIVE. If you think it's better to use that way, I'll do that. [08:02] noodles775: the hp-specific build const should be definied in goose, like other nova.Status* consts I think, and a change to enable setting it in the double is in order, so you can test it [08:03] dimitern: yep, will do. [08:03] noodles775: thanks! [08:03] dimitern: np, thanks for the feedback and direction. [08:03] rogpeppe: hey [08:03] dimitern: hiya [08:03] noodles775: no worries, glad I can help [08:03] rogpeppe: updated https://codereview.appspot.com/12990043/ [08:03] dimitern: looking [08:03] rogpeppe: cheers [08:13] dimitern: reviewed [08:15] rogpeppe: thanks [08:15] fwereade: ping [08:17] blast.. william is out today [08:17] rogpeppe: I have a question [08:17] * rogpeppe is all ears [08:17] rogpeppe: now that I changed Relation() to return only the endpoint of the auth'ed unit, should it return []params.Endpoint at all or just a single one? [08:18] dimitern: hmm, since there can only be one local endpoint, i think it should just return a single one [08:18] rogpeppe: then I shouldn't really use params.RelationInfo, but a copy of it without the Endpoints slice, just a single field [08:21] dimitern: i suppose so, but i can't get too worried about it. [08:21] dimitern: the comment in UniterAPI.Relation should definitely be updated though - i missed that [08:21] rogpeppe: ok [08:23] rogpeppe: did you have time to check the charm store for possible relation names? [08:24] dimitern: no, i'm going to do that this morning [08:24] rogpeppe: great [08:25] dimitern: i've also been trying to make some sort of sense of the config mess this morning. this is as far as i've got: https://docs.google.com/a/canonical.com/spreadsheet/ccc?key=0AghcHASXWzXmdHdOX2htQVJITkdaYVh4VnZ2Z0RrQWc#gid=0 [08:25] rogpeppe: looking [08:25] Anyone up for a tiny review? https://codereview.appspot.com/12850045/ [08:25] rvba: looking [08:27] rvba: LGTM [08:27] rogpeppe: ta [08:34] rogpeppe: for openstack, the username, password and tenant-name are not generated, only the password is secret; auth-url and auth-mode are cloud-specific and not generated, access-key and secret-key are used with auth-keypair only (otherwise username/password are used) - both secret, region is cloud-specific, not generated or secret, both buckets can be generated and are not secret [08:35] dimitern: feel free to change the spreadsheet! [08:35] rogpeppe: ah, I wasn't sure I can [08:35] dimitern: if you're logged into your canonical account, you should be able to [08:42] rogpeppe: yeah, I'm already half way through [08:42] dimitern: yeah, i saw that - very helpful, thanks! [08:44] rvba: if you have a moment at some point, could you perhaps fill out some of the azure-specific entries in this spreadsheet? https://docs.google.com/a/canonical.com/spreadsheet/ccc?key=0AghcHASXWzXmdHdOX2htQVJITkdaYVh4VnZ2Z0RrQWc#gid=0 [08:44] rvba: i'm trying to start rationalising our config attribute twistiness [08:46] rogpeppe: sure, I'll have a look. [08:51] does anyone know how to use the new charm browser to list all available charms? [08:51] the old one was easy enough to screen scrape, but this one isn't so easy. [08:51] rogpeppe: not sure about the last 4 columns though [08:52] dimitern: "needed for sync tools" is whether an attribute is needed when pushing to the private storage [08:53] dimitern: "needed for bootstrap" is whether the attribute needs to be passed into the bootstrap node [08:53] dimitern: "needed for client operations after bootstrap" is whether you need to use the attribute when connecting to the state later. [08:54] rogpeppe: I have to think about all of these - all in all they seem to be the same as for ec2, but have to check [08:54] dimitern: i would expect them to be similar === tasdomas is now known as tasdomas_afk === tasdomas_afk is now known as tasdomas [09:19] rogpeppe: what does "OK to specify in environments.yaml?" mean exactly? [09:19] rvba: do you want a user to be able to specify that attribute in the environments.yaml file? [09:19] rvba: in your case, the answer is likely to be yes for all [09:20] Okay. [09:20] rvba: we have some weird attributes like agent-version, which don't really make sense for a user to be able to specify there [09:20] rvba: that's part of the twistiness [09:20] rogpeppe: I see. [09:36] rogpeppe: I'm a bit confused by "Cloud account information?". I interpret this as "The value part of one's cloud account information" but I see ec2's 'region' is checked (with a question mark though). [09:37] rvba: it's whether the attribute should be considered part of the cloud account information (we're planning to move towards specifying cloud accounts as a separate entry in the juju conf file) [09:37] rvba: just leave it blank if you're not sure [09:38] rogpeppe: 'region' does not seem to be part of the cloud account information then… it's more linked to the deployment isn't it? [09:39] rvba: yes, you're probably right. [09:39] rvba: i'm not quite sure of fwereade's conception of accounts though [09:39] fwereade: you should be interested in this too BTW: https://docs.google.com/a/canonical.com/spreadsheet/ccc?key=0AghcHASXWzXmdHdOX2htQVJITkdaYVh4VnZ2Z0RrQWc#gid=0 [09:40] rogpeppe: okay, ta. I'll put X? then. [09:40] rvba: sgtm [09:40] rogpeppe, rvba: really briefly, because I'm out today: an account is essentially + + [09:41] rogpeppe, oh, nice spreadsheet [09:41] fwereade: so would the ec2 region be part of an account? [09:42] rogpeppe, might be, if someone almost always wants to use (say) eu-west-1 [09:43] rogpeppe, that's one of those ugly things that really needs to be available for override at env and account level [09:43] fwereade: is the idea to be able to specify *any* attribute at account level? [09:44] rogpeppe, I don't think we can have a coherent model without allowing that, upsetting though it may be [09:44] rogpeppe, but I don't think it's too painful to just pass unknown keys up levels until they're found to be comprehensible [09:44] fwereade: hmm, so it's not really "account" - it's more like "environment superclass" [09:45] rogpeppe, I don't personally see that as a particularly useful conception of it, but I expect mileage legitimately varies [09:45] fwereade: or, i guess "environment template" [09:46] rogpeppe, no, I don't think so... [09:46] fwereade: i don't really see anything particularly account-like about it, that's all [09:46] rogpeppe, the env may contribute attrs to the account, not vice versa [09:46] rogpeppe, the auth info is the critical bit [09:46] rogpeppe, that's the stuff that has to be included at that level [09:46] fwereade: erm, surely we're saying that the account can contribute attrs to the env too, no? [09:47] rogpeppe, how is region anything to do with the environment? [09:47] rogpeppe, it's all about the cloud [09:47] rogpeppe, but it can be specified differently at more specific levels [09:47] fwereade: ah [09:47] fwereade: so we've really got three levels, "account", "cloud" and "environment" [09:48] rogpeppe, it's definitely a surprising model from our existing perspective but it really holds together pretty well when you think about it enough, I think [09:48] fwereade: but... are you conflating "account" and "cloud"? [09:49] fwereade: i find it surprising that i can specify a region in something called an "account", which sounds like it's just got user credentials in it [09:50] fwereade: it feels a bit like the responsibilities are mixed up there [09:51] rogpeppe, the names indicate what must be specified, the other bits that *can* be specified are about convenience for users (and, yeah, some degree of irritating conceptual bleed for us developers) [09:52] fwereade: i'm no longer sure how many levels of inheritance we're aiming for here [09:54] fwereade: i'm not sure it's just a conceptual difficulty for developers - i'd find it awkward to explain to a user too. [09:54] rogpeppe, it's not the environment inheriting from the account inheriting from the cloud -- it's the environment overwriting values on the account overwriting values on the cloud [09:54] fwereade: i'm not sure i see the difference there [09:56] rogpeppe, inheritance implies an env is an account is a cloud [09:56] rogpeppe, it's rather more like embedding, I think [09:56] fwereade: if i can specify all the env attrs on an account or a cloud, i'm not sure i see there is a difference [09:56] * fwereade is neglecting his family [09:56] fwereade: right, go away! :-) [09:57] fwereade: sorry for distracting you [09:57] rogpeppe, (when did I say you could specify an env attr on an account? I don't *think* it needs to flow that way too) [09:58] fwereade: hmm, i'd thought the env ended up with all the attributes from the account [09:59] rogpeppe, I think that the env ends up with an account (which may have had overrides applied to it), and the *env* only has a few juju-level things set -- one of which is the account [09:59] fwereade: but are we allowing users to specify a "cloud" thing as well as an environment and an account? [09:59] rogpeppe, because of private clouds essentially [09:59] rogpeppe, most of the time all that tedious cloud stuff comes from simplestreams, so account specification is trivial [10:00] fwereade: an account specifies a cloud too? [10:00] rogpeppe, sometimes it needs to be hand-specified, and we (1) don't want to dirty up the account specification and (2) may well have a couple of accounts on the same cloud for say testing/staging/production [10:01] rogpeppe, yes, account = + + [10:01] rogpeppe, env = + + [10:02] * fwereade is really off now, and hopes the model makes some sort of sense [10:02] * rogpeppe thinks about it [10:02] fwereade: thanks [10:33] hmm, that's a first for me - laptop just spontaneously rebooted. i wasn't doing anything significant at the time [10:33] * rogpeppe tries to recollect all the threads [10:34] rogpeppe: it happens to me sometimes since I upgraded to raring - all due to video drivers [10:35] dimitern: yeah, i reckon that's likely to be the problem [10:35] rogpeppe: btw, should we have an EnvironConfig() call in the uniter API? the uniter uses it, but I remember there were some concerns about it [10:35] dimitern: i also get a weird thing occasionally where everything goes ultra slowly [10:35] dimitern: what is the uniter using it for? [10:35] * dimitern checks [10:36] rogpeppe: to get the provider type and from then the private address [10:36] rogpeppe: and public [10:37] dimitern: i think you should just have a ProviderType API call [10:37] rogpeppe: ok [11:13] morning all [11:13] dimitern: here's the list of all the relation names used in the 193 charms i could find in the charm store, most frequent first: http://paste.ubuntu.com/5992327/ [11:13] natefinch: hiya [11:14] dimitern: looks like we can be quite restrictive without it being a problem [11:15] rogpeppe: so it looks like ([a-z-]+)+ should be enough? [11:16] rogpeppe: or more like ([a-z]+-?)+, except it shouldn't end with - [11:16] mgz: I was looking at maas and azure for addressability. I have some code for azure, but it's pretty ugly... I couldn't find where maas was exposing IPs, though [11:16] dimitern: i think i'd go for [a-z][a-z0-9]*(-[a-z0-9]+)* [11:17] rogpeppe: sgtm [11:18] dimitern: in case you're interested, i downloaded all the charms with this: http://paste.ubuntu.com/5992345/ (unfortunately obtaining the list of charms was a bit more hassle) [11:18] dimitern: and i got it to spew out the relation names with this: http://paste.ubuntu.com/5992349/ [11:20] dimitern: (i ran it with this command line: http://paste.ubuntu.com/5992351/) [11:20] dimitern: so that command is also potentially useful for other charm metadata analysis we might want to do [11:20] rogpeppe: cool! so you didn't miss anything [11:20] natefinch: for maas, you might want to ask one of the red squad guys [11:20] dimitern: well, i might well have missed some charms [11:21] we need a different mechanism for the local provider currently, we might need one for maas too, but I think when I looked at it there seemed to be a reasonable way [11:33] standup guys? [11:59] juju-core 1.13.1 won't bootstrap on private openstack. /var/log/cloud-init-output.log reports : ERROR juju open.go:89 state: connection failed, will retry: dial tcp 127.0.0.1:37017: connection refused. There's nothing running on this port.mongod is running on 27017 and 28017 though [12:07] geme_: you need to ssh into the state server machine and look at the juju logs [12:08] I had a similar issue a while back, but I'm failing to find or recall what the root cause was [12:08] which log ? [12:08] look at things under /var/log/juju [12:10] there's also a mongo log that I remember looking at... [12:13] gah, I remember talking to jam about it, but can't find it in the chat logs anywhere [12:18] geme_: any luck? the first few lines of /var/log/juju/machine-0.log should be about mongo state connection [12:20] mgz, there is no machine-0.log - just an empty all-machines.log [12:20] okay, so can you pastebin your whole cloud-init please? seems the agent didn't get started at all [12:21] cloud-init.log and cloud-init-output.log ? [12:22] -output please [12:26] mgz, jusr seen : 2013-08-16 10:36:05,970 - cc_apt_update_upgrade.py[WARNING]: Source Error: ppa:juju/experimental:add-apt-repository failed [12:27] what do you get if you sudo run thant command from the box now? [12:33] mgz, http://paste.ubuntu.com/5992536/ [12:34] oh god, that statecert logging [12:35] geme_: okay, so it installs the wrong version of mongodb (2.0.4-1ubuntu2.1) because the repo add failed [12:36] adding the repo works from the cmdline [12:37] though it needs a return response [12:42] meh, and it's a bare except clause in cloudinit/config/cc_apt_update_upgrade.py that throws away the actual error when logging [12:42] mgz, any idea why adding the repo failed ? [12:43] my best guess is somehow the networking isn't up yet so it fails trying to talk to launchpad [12:43] but could be anything, and after all the install works fine after that [12:45] one thing you could try is reproducing the error with `nova boot --user-data CLOUDCONFIGFILE` [12:46] mgz, when cloud-init runs can it access envars like HTTP_PROXY ? [12:47] where cloud config is just something like a file containing "#cloud-config\napt-sources:\n - source: ppa:juju/experimental" (with real newlines etc) [12:47] geme_: set where? if you have some funky proxy setup for external net access, that is likely the issue [12:48] mgz, /etc/bash.bashrc [12:49] doubt very much cloud-init will pick up that, except perhaps as a side-effect to some subcommands [12:51] mgz, Is there another way to make these proxy setting available to cloud-init ? [12:51] I'm not even sure that's sufficient, not everything respects HTTP_PROXY [12:54] mgz, so firewalls are a blocker then - sorry for the pun :) [12:54] not really, we've got a bunch of firewalled clouds that work fine [12:54] I don't think any try to do it using HTTP_PROXY though [12:55] which is an application level setting, if you're trying to inject that into your vms it doesn't seem a reliable way of doing things [12:55] you need to configure openstack to understand your network setup [12:57] mgr, the proxy settings work in the shell [12:57] sure, because it's a shell [12:57] lots of things in a vm will not be spun up from bash [12:59] I think what you probably want is just a local apt proxy, but I'm still not sure how apt-add-reposistory interacts with that [13:00] you might want to try asking in #juju how people have set up juju on openstack clouds without outgoing access to the wider internet [13:00] because a bunch of guys in there have done that [13:01] mgr, ok I'll ask. Thanks [13:02] dimitern: i realised that i'd missed most charms in my download, but rick_h in juju-gui pointed me to the charmworld api, so here's a program that downloads *all* the charms: http://paste.ubuntu.com/5992628/ [13:02] dimitern: 663 successfully acquired at last count [13:02] rogpeppe: nice [13:02] dimitern: and our relation regexp is still fine [13:02] rogpeppe: great! [13:03] rogpeppe: we should have some repo for such useful tools [13:03] dimitern: yeah, they might well be useful to others [13:05] rogpeppe: so if you have a peer relation, are the remote and local units of it the same? [13:05] dimitern: no [13:06] rogpeppe: why did you resolve all my comments on that document? [13:07] dimitern: i turned then all into notes [13:07] dimitern: which seemed a much more appropriate way of doing things [13:07] dimitern: (i didn't know about notes when i started adding comments) [13:07] rogpeppe: ha! I didn't know there is such a feature [13:07] dimitern: thank rvba [13:07] rvba: thanks :) [13:08] rogpeppe: so I'm pondering how to get the remote unit from a relationunit [13:08] rogpeppe: I can get the relation of the RU, call RelatedEndpoints on it, passing the local unit's service name [13:09] rogpeppe: I'll a slice with length 1 (peer) or 2 (provider-requirer) with the endpoints [13:09] rogpeppe: and I can get the remote service name from eps[0].ServiceName or eps[1].ServiceName respectively [13:10] rogpeppe: but how to get which unit is on the other end? [13:10] rogpeppe: /I'll a/I'll get a/ [13:11] dimitern: an endpoint doesn't imply a unit [13:11] rogpeppe: so there's no way to know which is the remote unit [13:11] dimitern: the client will need to name the unit, i think [13:12] rogpeppe: ok, so ReadSettings cannot be made to enforce "use remote unit only" [13:12] rogpeppe: in fact I can see in test that it gets called for the local unit as well [13:12] dimitern: well, there are many possible remote units [13:12] rogpeppe: right.. [13:12] dimitern: and we need to be able to read our own settings too [13:13] rogpeppe: for that, you have another call [13:13] rogpeppe: Settings() [13:13] dimitern: what's the difference between the two? [13:14] rogpeppe: Settings() returns a mutable *Settings, whereas ReadSettings() returns a plain map[string]interface{} [13:14] dimitern: but they'll use the same underlying API call, right? [13:18] rogpeppe: no [13:18] rogpeppe: but perhaps they should actually [13:18] dimitern: ah, what's the difference? [13:18] rogpeppe: ReadSettings only [13:18] dimitern: i think they're both doing the same thing [13:18] dimitern: the difference is only at the client side [13:19] rogpeppe: and it to simulate Settings() you need to call it with "relation-x", "unit-x", "unit-x" [13:19] rogpeppe: I have defined a RelationUnitPair with Relation, LocalUnit and RemoteUnit string [13:20] rogpeppe: I can make RemoteUnit to be optional [13:20] dimitern: hmm, can't you just omit LocalUnit? [13:21] dimitern: because it's implied by the client [13:21] rogpeppe: no, let's not do that [13:21] rogpeppe: if we're going to start omitting stuff because it's implied by the authenticated entity, that's a whole new can of worms [13:22] dimitern: ok, fair enough [13:22] rogpeppe: possibly making bulk calls a moot in general [13:22] dimitern: ahem, yes [13:22] dimitern: [don't get me started :-)] [13:22] rogpeppe: so, until and if we decide to do that, I'll prefer to be explicit [13:22] dimitern: so LocalUnit must always be the authenticated unit, right? [13:22] rogpeppe: yes [13:23] dimitern: so i think there's no need to make RemoteUnit optional [13:23] dimitern: if you want to get settings of the remote unit, just have RemoteUnit==LocalUnit [13:23] rogpeppe: so pass both and make them the same to request local settings [13:23] rogpeppe: ok [13:23] rogpeppe: no, it's exactly the opposite :) [13:23] dimitern: then RemoteUnit is always the tag of the unit you're asking for settings of [13:23] rogpeppe: Local!=Remote => remote settings [13:24] dimitern: ha, yes, typo [13:24] dimitern: if you want to get settings of the *local* unit... ! [13:24] rogpeppe: ok then, almost done - will propose soon [13:24] dimitern: cool [13:28] rogpeppe: remind me again why you removed the unitTagToName conversion from names? [13:28] dimitern: you've got ParseTag now [13:28] rogpeppe: ah, right [13:38] dimitern: when you've a moment, try running "go get launchpad.net/juju-utils/cmd/getallcharms" [13:39] dimitern: there's also launchpad.net/juju-utils/cmd/charmmeta [13:39] mgz, added repo to the image. Now bootstraps successfully and juju status respond. Likely the problem will come back and bite me. [13:39] rogpeppe: done both [13:40] dimitern: you could try and see if it works [13:40] dimitern: e.g. getallcharms ~/allcharms [13:40] rogpeppe: trying now [13:40] dimitern: it should fetch all the charms in the charm store [13:40] rogpeppe: is there a -v option? [13:40] rogpeppe: ah, it dumps them [13:41] dimitern: yeah. it's actually verbose by default [13:41] rogpeppe: for some it reports 2013/08/16 15:40:58 get "cs:~philipballew/precise/dyndns-0" failed: charm "noip2" relation "dynamic-ip-reporting" using a reserved interface: "juju-info" [13:41] dimitern: yup, that's the most common issue [13:42] rogpeppe: done, 361M fetched [13:42] dimitern: cool [13:42] rogpeppe: and charmmeta ? [13:43] rogpeppe: it's not recursive [13:43] dimitern: now you can run charmmeta over all of them by doing something like: charmmeta sometemplatetext $(cat $HOME/allcharms/urls.txt) [13:44] dimitern: no - it probably should recurse looking for metadata.yaml files [13:45] dimitern: but i've spent too much time on it already :-) [13:45] rogpeppe: np, with some tweaking it works [13:45] dimitern: what tweaking? [13:46] rogpeppe: charmmeta '..template..' `cat ~/allcharms/urls.txt` fails [13:46] rogpeppe: it reports Dir==nil on each line [13:47] dimitern: ah! [13:47] dimitern: that's because you need to be inside the allcharms directory [13:47] rogpeppe: yeah, I figured :) [13:49] man.. testing Enter and LeaveScope is a bitch [13:49] I have to use a scope watcher.. [14:04] dimitern: charmmeta is now recursive. couldn't quite resist doing it now :-) [14:07] rogpeppe: cool, will test it [14:11] * rogpeppe must get some lunch now === tasdomas is now known as tasdomas_afk [15:07] trivial review, anyone? https://codereview.appspot.com/13053043 [15:08] just some package renaming, mechanical changes [15:15] rogpeppe: I can review it [15:15] natefinch: thanks [15:23] rogpeppe: reviewed. LGTM, bonus points for fixing a few of the import sorts [15:23] natefinch: good catch [15:30] dimitern, mgz, rvba, jtv, fwereade: another review appreciated please. trying to avoid a prereq. https://codereview.appspot.com/13053043 [15:33] rogpeppe: looking [15:33] dimitern: ta [15:33] dimitern: i okayed this with fwereade previously, BTW [15:34] rogpeppe: while I'm at it, would you look at https://codereview.appspot.com/13055043 [15:34] dimitern: looking [15:36] rogpeppe: thanks [15:36] rogpeppe: didn't we discuss having the providers at top-level? [15:36] dimitern: they're very much to do with environs [15:36] dimitern: (they implement an interface defined in environs) [15:37] dimitern: so i strongly feel they below below environs [15:37] dimitern: (as does instance, but that's another story) [15:37] rogpeppe: that shouldn't stops us - there are cases where other packages implement interfaces defined outside their parent package [15:37] dimitern: yes, but really environs is all about providers [15:38] dimitern: hmm, well, i can see your p.o.v. too [15:38] rogpeppe: providers, like charms are things that need an environment [15:39] dimitern: not really: providers *provide* an environment [15:39] rogpeppe: but really the environment needs both of them [15:39] dimitern: well, they provide an Environ anyway [15:39] rogpeppe: the environment is much more that what a provider provides [15:39] rogpeppe: it provides means to access the environment, but not the only means [15:40] dimitern: a provider provides an Environ [15:40] rogpeppe: state and API are other means to access it [15:40] dimitern: and that's what environs is all about [15:40] rogpeppe: well, Environ is hardly the best name for it [15:40] dimitern: state and API don't really have anything to do with Environ [15:40] dimitern: that may be true [15:41] rogpeppe: i think we're conflating the Environ type and what a juju environment is all about [15:41] rogpeppe: the Environ type is *not* a juju environment [15:41] dimitern: i didn't say it was [15:41] dimitern: well, [15:41] dimitern: it depends what you mean by a juju environment [15:42] rogpeppe: I mean both local and remote stuff [15:42] dimitern: but regardless, the provider types are there entirely to implement the interface defined in environs/interface.go [15:42] rogpeppe: settings, instances, cloud account info [15:42] dimitern: they aren't accessed in any way accept through environs [15:43] dimitern: so i think it's reasonable to put them under that directory [15:43] rogpeppe: they are - through the CLI and the API [15:43] dimitern: not really. whenever the CLI gets an Environ, it gets it via the environs package [15:43] rogpeppe: should we put the API in environs as well, because all it does is to provide access to a running environment's parts? [15:44] dimitern: no, an Environ (whatever it should really be called) is all about encapsulating provider-specific functionality [15:44] rogpeppe: I really liked the way pyjuju has providers at top level [15:44] s/whatever/or whatever/ [15:45] rogpeppe: and the argument that providers need to implement an interface in environs/interface.go is not a strong point to keep them together IMO [15:46] rogpeppe: we have plenty of cases where that's not true [15:46] dimitern: if they provided independent functionality too, i might agree [15:47] dimitern: but given that nothing else in the system imports them directly (except for the side effect of having them there, and excepting dummy which is special), i think it's reasonable to hide them under environs [15:47] rogpeppe: how about the null provider? [15:48] rogpeppe: it's not a complete provider - also possibly the ssh one [15:48] dimitern: i don't know what the null provider is. have we got one of those? [15:48] rogpeppe: we will soon [15:48] rogpeppe: read the specs [15:49] rogpeppe: it's an example of a "partial" provider [15:49] rogpeppe: you cannot start or stop instances for example [15:49] rogpeppe: or xenv rels? [15:49] dimitern: it will still need to implement all the methods defined in environs.EnvironProvider [15:49] dimitern: even if it returns errors for some of them [15:50] rogpeppe: I'm saying this might change [15:50] dimitern: i'm not sure it needs to [15:50] rogpeppe: we might need to separate that huge interface into smaller ones [15:50] dimitern: and we can change things again when that happens [15:50] dimitern: i plan to do just that [15:50] rogpeppe: well.. [15:51] dimitern: but i'm still happy to have them all under environs [15:51] rogpeppe: ok, at least havingthem in environs/provider/ will clean up some of the mess already in there [15:51] rogpeppe: the same should apply to cloudinit imo [15:51] dimitern: where should environs/cloudinit go? [15:51] rogpeppe: in cloudinit/ [15:52] dimitern: cloudinit/cloudinit ? [15:52] dimitern: (there's already a juju-core/cloudinit package) [15:52] rogpeppe: I know [15:52] rogpeppe: why shouldn't it be in the same top-level package? [15:52] rogpeppe: why the separation? [15:53] dimitern: because the top level package is entirely juju-agnostic [15:53] dimitern: whereas environs/cloudinit is intimately bound up with juju environment-related stuff [15:54] dimitern: and that's kinda where my reasoning for when we should have a top level package or namespace comes from - it should be largely independent functionality. [15:54] rogpeppe: anyway, that's entirely another topic in its own right [15:55] dimitern: the cloudinit package works well as something standalone. environs/cloudinit builds neatly on top of it. [15:55] rogpeppe: if it's independent it shouldn't be in juju-core - it should be an outside package [15:55] dimitern: perhaps. but if we don't care about other people using them, it's more convenient to keep it in juju-core [15:56] dimitern: we can still have decent modularity, even inside juju-core [15:56] rogpeppe: if it's useful independently, it shouldn't be inside [15:56] dimitern: there are lots of pieces that are useful independently. having them outside is a burden we don't want to have to bear for all of them [15:57] dimitern: schema and rpc are two examples [15:57] dimitern: anyway, anyone outside can still use these things [15:57] dimitern: they don't need to be in an independent package to enable external reuse [15:58] rogpeppe: I don't quite agree the burden is that big [15:59] rogpeppe: most of this things are stable and change rarely [15:59] dimitern: i don't think there's that much in the way of advantage from having them in separate projects [15:59] rogpeppe: actually that could be a reason to move it out [15:59] rogpeppe: of course there is - smaller code base, easier to maintain [16:00] dimitern: i don't think the maintenance burden is any smaller if the code is separated across several projects [16:00] dimitern: the opposite if anything [16:00] rogpeppe: there's only a burden if we have to update them often - the same thing as with gwacl, goose, etc. [16:01] dimitern: if we don't need to update them, where's the burden from having them in the same code base? [16:01] dimitern: i think it's great to build a project from many small indpendent parts [16:02] dimitern: and if those parts were principally built for the main project, i don't really see there's an advantage to moving them out [16:02] dimitern: the main advantage of doing that is if we want to promote external users of those packages [16:03] rogpeppe: it's not a burden as such to have them in the same codebase, it just inflates it, and if they rarely change, it's actually better and cleaner to have them outside [16:03] dimitern: i guess i don't see that. the more external dependencies, the more hassle. [16:04] dimitern: otherwise we'd have one project for each package [16:05] rogpeppe: what's the hassle exactly? [16:05] dimitern: moving the code base to different versions doesn't automatically update the dependencies [16:06] dimitern: i can see the history of those dependencies in with the main log [16:06] dimitern: if they're inside the code base [16:06] rogpeppe: aren't we moving towards handling dependencies better in general? [16:06] dimitern: yeah, but it's still not cost-free [16:07] rogpeppe: it depends on how easy we make it [16:08] rogpeppe: nothing is cost free, there are hidden costs you have to consider [16:08] dimitern: back to the environs/provider thing: for me, it's about strict encapsulation boundaries. the environs package should entirely encapsulate the different providers. [16:08] dimitern: hidden costs like? [16:09] rogpeppe: reviewed [16:10] dimitern: i don't want to do any import renaming in that branch if that's ok. i can go through in another branch doing all the gocheck renames if you'd like [16:11] rogpeppe: like having a bunch of code which is not directly juju-related, only slightly and could be reused by external projects [16:11] dimitern: external projects can still reuse that code [16:12] rogpeppe: Ok, at least fix the imports separation then - they were just a few [16:12] dimitern: (and does - for instance that charmmeta command) [16:12] dimitern: will do, thanks [16:13] rogpeppe: i don't want to argue anymore about this - it deserves a separate discussion in good time :) [16:13] dimitern: indeed :-) [16:13] dimitern, rogpeppe: FWIW I agree with both of you... both ways have advantages and disadvantages. It's a tale as old as time (or at least as old as modern programming) [16:15] natefinch: nah.. c'mon you can't agree like that :) [16:16] dimitern: what, I have to take a side? :) [16:16] natefinch: hehe [16:16] natefinch: you can have a look at this perhaps? https://codereview.appspot.com/13055043/ [16:18] dimitern: sure [16:19] natefinch: ta [16:23] dimitern: just curious, why do we do "type RelationUnits struct { RelationUnits []RelationUnit}" rather than "type RelationUnits []RelationUnit" ? [16:23] natefinch: because the rpc layer of the API needs a struct as args and results [16:24] dimitern: ahh, ok [16:25] natefinch: take a look at doc/api.txt and rpc/server.go -> Serve()'s doc comment you're interested in the internals [16:25] fwereade_: hey [16:25] fwereade_: just in time for a review? :) [16:51] natefinch: thanks [16:52] natefinch: btw - I'd like to leave the checkers.IsTrue etc. for a follow-up, if you don't mind. The diff is already big enough [16:56] rogpeppe: does it look roughly ok? [16:56] dimitern: i'm working on doing all the gc.etc stuff in about 4 giant branches [16:57] dimitern: i think i can do most of the changes automatically [16:57] rogpeppe: good to hear [16:57] rogpeppe: but please don't forget my branch as well ;) [16:58] dimitern: i'm looking now [16:59] rogpeppe: cheers [17:03] rogpeppe: I was looking at how to do the . to gc change automatically... did you find a good way to do it? [17:03] natefinch: i've got a command that may help - we'll see if it's up to the task [17:04] natefinch: it's pretty much a failed experiment, but still sometimes useful [17:04] rogpeppe: yeah, I looked around for something that might already do it, gofix or something, but nothing seemed to be able to do it, so I was looking into writting a command to do it by parsing the AST... hopefully yours will work so I don't have to do that :) [17:04] natefinch:code.google.com/p/rog-go/exp/cmd/gosym [17:05] natefinch: gofmt isn't far off being able to do it [17:06] rogpeppe: yeah, I looked at gofmt too... wasn't sure how to get it to figure out what is getting imported by . and fix it automatically.... at least in the general case [17:06] natefinch: it can't do that [17:06] natefinch: it needs to resolve type symbols [17:07] natefinch: gosym resolves symbols but it doesn't cope with import to . well [17:09] rogpeppe: I'm honestly surprised import . even exists in Go. Seems like the kind of "almost always a bad idea" thing that it would not support [17:09] natefinch: it's only there for a particular edge case [17:09] natefinch: but it's been abused [17:14] dimitern: can settings attributes be deleted? [17:15] rogpeppe: they are replaced [17:15] rogpeppe: so yeah [17:15] dimitern: hmm, it looks like relation-set calls settings.Delete [17:15] rogpeppe: perhaps an additional test is needed [17:16] dimitern: but it looks as your API doesn't cater for deletions [17:16] rogpeppe: settings.Delete is only local [17:16] dimitern: it's possible that's not a problem though [17:16] rogpeppe: any changes are always written with Settings.Write() [17:17] dimitern: but if you've called Delete, Settings.Write will actually delete the attribute [17:17] dimitern: it doesn't just overwrite it with nil [17:18] rogpeppe: the settings are one blob, right? [17:18] dimitern: no [17:18] dimitern: the settings are held as mongo fields [17:18] dimitern: each settings gets its own field [17:18] s/settings/setting/ [17:18] dimitern: look in state/settings.go [17:18] rogpeppe: so what happens when you overwrite it with some different map? [17:19] dimitern: it changes only the ones that have locally changed since you last read the settings [17:19] dimitern: it unsets the ones that have been deleted and sets the others [17:20] dimitern: it might be reasonable to say that setting a value to nil will be treated as a deletion [17:21] rogpeppe: ok, will add more tests to make sure what I can read from the unit after calling WriteSettings is exactly as passed in the args [17:21] dimitern: how will attributes get deleted? [17:21] rogpeppe: by calling Update before Write? [17:21] dimitern: Update never deletes an attribute [17:22] rogpeppe: so you're saying the operation has to be more sophisticated [17:23] dimitern: i think so... assuming we care about deleting attributes [17:23] rogpeppe: reading old settings, finding what to delete, updating the rest, finally writing the result [17:23] dimitern: no [17:23] rogpeppe: no? [17:23] dimitern: the API call has to be able to specify which settings are to be deleted [17:24] rogpeppe: delete just does delete(c.core, key) [17:24] rogpeppe: it doesn't seem it keeps the deleted ones separately [17:24] dimitern: it infers which ones are deleted by looking at the difference between c.core and c.disk [17:24] rogpeppe: assuming we start with an empty map, update with the new settings, then write should take care of deletions as well [17:25] dimitern: what happens if we do: Update({"x": "y"}) then Update({}) ? [17:25] rogpeppe: will delete everything [17:25] dimitern: nope [17:25] dimitern: "x" will still be around [17:25] rogpeppe: no, I mean we can't do that really [17:26] rogpeppe: Update is client-side only [17:26] rogpeppe: on the API level we have read and write only [17:26] dimitern: it would be possible to make Update overwrite all attributes [17:26] dimitern: but i don't think that's a great idea [17:27] rogpeppe: how? [17:27] dimitern: because it means potentially quite a bit more network traffic [17:27] rogpeppe: sorry, I don't get you [17:27] rogpeppe: we have old settings, we want to write new ones we got over the wire [17:27] dimitern: if a hook just does a single "relation-set x=y", we don't really want to send all the other settings too [17:28] rogpeppe: calling Read() will reset the disk cache, then we reset all core values, update the new ones, then write [17:28] dimitern: are you talking client- or server-side here? [17:28] rogpeppe: server-side [17:29] rogpeppe: client-side we implement a Settings proxy object with a subset of the interface that state.Settings has, but only Write calls the API [17:29] rogpeppe: the overhead is not significant [17:29] dimitern: if we replace all core values, the client must send all those values up with the Write call [17:29] rogpeppe: yes it will [17:29] rogpeppe: the client calls Write eventually [17:30] rogpeppe: in context, if I'm not mistaken, after the hook is comitted [17:30] committed [17:30] rogpeppe: yep, context.go:290 [17:30] dimitern: are you happy that at the end of the hook, all settings will be sent across the network, even if they have not changed? [17:30] rogpeppe: I see no problem with that [17:31] rogpeppe: the settings are usually just a few [17:31] dimitern: i don't know - have you looked at how charms use settings? [17:31] rogpeppe: a few yes [17:31] dimitern: i think it's quite possible that people have some large values in there sometimes [17:31] rogpeppe: haven't look through all of them [17:31] dimitern: we're not talking about config settings [17:32] rogpeppe: can we check that reliably? [17:32] rogpeppe: find all relation-set calls in all charms? [17:32] dimitern: no - i just think it's not hard to be a little more efficient [17:32] rogpeppe: like how? [17:32] rogpeppe: have UpdateSettings and DeleteSettings calls? [17:33] rogpeppe: pass every client-side call over the wire? [17:33] dimitern: we could treat a nil value as a delete [17:33] dimitern: so to delete an attribute you'd do: WriteSettings({"x": nil}) for example [17:33] rogpeppe: deletes are not a problem [17:33] rogpeppe: you're talking about something different [17:34] rogpeppe: having incremental updates [17:34] dimitern: afaics, deletes are the only problem with the current scheme [17:34] dimitern: the current scheme will work fine to do incremental updates [17:34] dimitern: the only exception being that you won't be able to delete an attribute [17:34] rogpeppe: the current scheme will change as described and deletes won't be a problem anymore - it'll overwrite all stuff on write [17:35] rogpeppe: not really [17:35] rogpeppe: we're removed from the source when using the API [17:35] rogpeppe: we can't rely on Update to do things smartly [17:35] rogpeppe: we read the settings every time when we're about to write them [17:36] rogpeppe: so there's no cache, etc. [17:36] dimitern: i thought the idea was to replicate some similar Settings logic client side [17:36] rogpeppe: yeah, except for Write [17:36] rogpeppe: but Write will still overwrite what's currently in state [17:36] dimitern: so that the client would read settings, make some modifications, then call Write, which could intelligently decide which have changed and issue an appropriate Update API call [17:37] rogpeppe: I'm not keen on the idea of having an Update API call [17:37] dimitern: why not? [17:37] rogpeppe: it seems too much complexity for very marginal gain [17:38] dimitern: in that case, there's no point in duplicating any of the Settings logic in the client side. [17:38] dimitern: the uniter may as well just use map[string]interface{} directly [17:38] rogpeppe: it being "in the odd case some charm writes huge settings keys once and changes a few smaller ones often" [17:38] rogpeppe: well, it doesn't and changing the uniter is not necessary [17:39] rogpeppe: the client-side will replicate the same logic with a proxy Settings [17:39] rogpeppe: but on Write it'll overwrite current settings [17:39] rogpeppe: and it will still work the same [17:39] rogpeppe: update and delete will operate on the proxy object's internal map [17:39] rogpeppe: write will send the final, merged map for saving [17:40] dimitern: you'd only need one map, of course. [17:40] rogpeppe: yeah [17:41] rogpeppe: I can add a TODO in WriteSettings saying something like "if this proves to be inefficient we can change it to allow incremental updates" ? [17:42] dimitern: how are you going to reset all attributes, BTW? [17:42] rogpeppe: in fact, having huge values in settings keys is a possible DoS attack on mongo from a malicious charm [17:43] rogpeppe: get the old ones, delete everything, write, update, write again is the easiest way that comes to mind [17:43] rogpeppe: but perhaps there's a more efficient way, have to experiment a bit, to do it with a single write [17:43] dimitern: i'm not sure there's a "delete everything" mongo operation [17:44] rogpeppe: there is $unset deletions [17:44] rogpeppe: that's what Write does [17:44] dimitern: yes, but you have to know what attributes to unset [17:45] dimitern: and the way you're using Settings, unset will never happen [17:45] rogpeppe: all of them, which do not conflict with the new ones, which are simply updated [17:45] dimitern: how do you know the "all"? [17:45] rogpeppe: yes, we already discussed that, I change what's being done on WriteSettings [17:46] rogpeppe: there's Keys() [17:46] rogpeppe: or, perhaps simpler - get and delete each key not in the new map [17:46] rogpeppe: just delete actually should do it [17:47] dimitern: ISTM like the state.Settings object is actually getting in the way here. [17:47] rogpeppe: after doing Read to populate the disk cache [17:47] rogpeppe: how so? [17:47] dimitern: because you'll be duplicating the same kind of logic that's already inside Settings [17:48] rogpeppe: not much - just a for loop for deletions, a read before it, and update + write after it [17:48] dimitern: part of the reason i'm slightly concerned here is that i suspect that these settings writes may be a good proportion of the ongoing mongo traffic in a large juju installation [17:49] rogpeppe: yeah, and how is my approach different from what's already happening? [17:50] rogpeppe: we always read them to create a settings object, do some ops on it, and call write finally - only the first and last operations are actually mongo ops [17:50] dimitern: currently for each hook execution, we only have two mongo ops: read then write [17:50] dimitern: we're going to change that so that it reads twice [17:51] dimitern: and also currently, we read the settings, but we only write the data for the ones that have changed [17:51] rogpeppe: not really, we can change the settings object to provide ClearCache method [17:51] dimitern: how will that work? [17:51] rogpeppe: that way we'll still have 1 read and 1 write [17:52] dimitern: i don't know how if there's a mongo operation to delete all attributes on a doc without knowing what those attributes are. [17:52] rogpeppe: it doesn't matter how many keys we set or unset - it's still a single update operation [17:52] rogpeppe: I'm not talking about a mongo operation [17:52] rogpeppe: I'm talking about ClearCache method which clears the settings object's internal disk cache [17:53] rogpeppe: the same one write uses to determine which keys to unset [17:53] dimitern: uniter.go:725 reads all the settings; uniter.go:728 writes them [17:53] dimitern: every hook execution will call ReadSettings (which reads all the settings), then WriteSettings (which reads all the settings then writes them) [17:54] rogpeppe: can we please stop discussing what's there now and focus on what we want to have there? [17:54] rogpeppe: that's what I'm trying to explain [17:54] dimitern: ok, so i think we still want to have ReadSettings followed by WriteSettings, right? [17:54] rogpeppe: yes [17:54] rogpeppe: it's the same as now [17:54] dimitern: so the question is: how does WriteSettings delete any attributes which aren't in the map we're writing? [17:55] rogpeppe: RU.Settings() does readSettings, Settings.Write() does the write [17:55] dimitern: right - so we'll still have exactly the situation i described above, no? [17:55] rogpeppe: by deleting them from the map, unless they are to be overwritten by keys in the new map [17:56] dimitern: i.e. we'll read all the settings twice, then write them [17:56] rogpeppe: ah, right, but in 2 separate API calls [17:56] dimitern: yes [17:57] dimitern: but the increase in mongo traffic is real [17:57] rogpeppe: read does read, write does read+delete+update+write [17:57] rogpeppe: how can you measure that? [17:57] dimitern: write *should* just do a single mongo update transaction, i think [17:57] dimitern: and i don't think it's at all hard to arrange that [17:58] rogpeppe: we can't avoid the read in WriteSettings [17:58] dimitern: yeah, i think we can [17:58] rogpeppe: we don't have the orinal settings object around anymore [17:58] dimitern: we'll just have to bypass the Settings object entirely. [17:59] dimitern: it's not really appropriate for the server side [17:59] dimitern: however.... [17:59] rogpeppe: ah, so you're saying implment state.WriteSettings that does all that in one go? [17:59] dimitern: yeah. or probably RU.WriteSettings [17:59] rogpeppe: ok, that sounds reasonable [18:00] dimitern: however... perhaps we could leave this as a TODO [18:00] rogpeppe: and how will that work internally, without knowing what keys are already there? [18:00] dimitern: we treat nil elements in the map passed to WriteSettings as deletions [18:01] dimitern: the client side keeps track of deletions and passes them through as nils [18:01] rogpeppe: that seems worse traffic-wise [18:01] dimitern: only if deletions are common [18:02] rogpeppe: now, instead of sending only stuff we actually want to store, we'll send also deleted things [18:02] dimitern: we'll be sending deltas [18:02] dimitern: if there are more than a few attributes that aren't changed, then it'll definitely be a win [18:02] rogpeppe: yeah, that's one of those questions with no easy answers without having to go through every charm hook [18:02] dimitern: yeah [18:03] dimitern: it would be great to have some operational data to inspect [18:03] dimitern: i really have to go, i'm afraid [18:03] rogpeppe: ok, so: 1) treat key->nil as deletions, 2) update the rest [18:03] dimitern: for the time being, i think you could leave it as it is [18:03] rogpeppe: 3) add a todo to possibly replace all that with a RU.WriteSettings call [18:04] dimitern: we won't be able to delete anything, but i don't think that's a big problem for the time being [18:04] dimitern: actually, yes, that will work better [18:04] rogpeppe: ok, let's continue on monday then [18:04] dimitern: then deletions will actually work, and we can be more efficient later [18:04] dimitern: cool, thanks for going along with me [18:05] rogpeppe: no worries, it was useful [18:05] rogpeppe: have a good weekend! :) [18:05] dimitern: i will - going to a folk festival, yay! [18:05] dimitern: and you [18:05] dimitern: ttfn [18:06] g'night all [18:06] yay :) [18:06] fiddles then [18:06] oooh yes [18:10] * dimitern @ eod as well [18:11] see y'all on monday!