Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

If a change is no-op, then don't create a transaction. #7180

Merged
merged 8 commits into from Apr 3, 2017

Conversation

jameinel
Copy link
Member

@jameinel jameinel commented Mar 30, 2017

Description of change

Change SetLinkLayerDevices and SetDevicesAddresses so that both of them check if there are actually any changes to be done, and only if we'll actually change something do we create a transaction.

QA steps

  $ juju bootstrap lxd --debug
  $ juju add-machine
  $ juju ssh 0
  $$ sudo service jujud-machine-0 restart
  $ juju ssh -m controller 0
  $ dialmgo
  $ db.txns.find({"o.c": {"$in": ["linklayerdevices", "ip.addresses"]}}).sort({"_id": -1}).limit(5).pretty()
  $ juju debug-log -m controller --include machine-0 -include-module juju.state

monitor the transaction log, it should not be filling up with transactions that are ultimately just asserting that a bunch of devices still exist. The debug-log should also include lines like:

  DEBUG juju.state no changes to LinkLayerDevices for machine "0"
  DEBUG juju.state no changes to DevicesAddresses for machine "0"
  db.txns.find({"o.c": {$exists: 0}}).pretty()

should return no documents. Before the last commit we were creating transactions that affected no collections, which was a bit silly.

Documentation changes

None.

Bug reference

lp:1677619
lp:1676427

Change SetLinkLayerDevices and SetDevicesAddresses so that both of them check if there are actually any changes to be done, and only if we'll actually change something do we create a transaction.
…ssumed we could set things in multiple arguments.

It wasn't a valid check, because it happened to work because it always set the values,
because it wasn't properly seeing if there was a change.
But if we do it sequentially we find the right values.
@jameinel jameinel changed the title WIP: If a change is no-op, then don't create a transaction. If a change is no-op, then don't create a transaction. Mar 30, 2017
@jameinel
Copy link
Member Author

This currently does create no-op transactions that look like:

{
        "_id" : ObjectId("58dd1fe3ab41ca0c3219d73d"),
        "s" : 6,
        "o" : [ ],
        "n" : "89fc8647",
        "r" : [ ]
}

would it be better to raise an exception that there is nothing to do so that these don't get created?

@jameinel
Copy link
Member Author

This has been fixed in the last version.

Copy link
Contributor

@axw axw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks fine, but I have some concern about dropping the model life assertion. I don't think it needs to be there in the first place, in which case we should remove both parts. But if we do care and must check in memory, we should be mirroring that in an assert.

if err := checkModelActive(m.st); err != nil {
return nil, errors.Trace(err)
}
if err := m.isStillAlive(); err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it would be a little more efficient to use the m.doc.Life value here, and only reload the machine doc on attempt>0. I suspect that's what was intended originally, except the initial check was left out

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@@ -206,18 +206,27 @@ func (m *Machine) SetLinkLayerDevices(devicesArgs ...LinkLayerDeviceArgs) (err e
}
}

// We've checked the model is alive directly, and we assert the machine is alive, we don't need to also
// assert the model is alive, because then the machine would be dying as well.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This statement is not true. A model can be dying, but still have alive machines. They will eventually be destroyed on account of cleanups.

If we don't really care about the model life here, should we remove the one above too? The doc comment for the method does not explain why we check the model life. Do you know?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it was back in the "I'm updating something, make sure my model is alive". These aren't actually creating new resources, so I don't think it is critical.

I'm personally ok with a "don't start work that is about to be dropped" check of model, but certainly I don't believe we need the TXN assert.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you're right, and we should only care about checking model life when creating new resources.

IMO, the in-memory life check should be dropped too. It's of dubious value when there's no accompanying txn op, since it's no longer ensuring integrity - so it's really just an optimisation. This makes things more difficult to understand, because it's not clear why we would do it in only memory from the context (hence this question).

FWIW, I've taken to the pattern of:

// checkSomething checks something in memory, and then return txn.Ops that assert the same
func checkSomething() ([]txn.Op, error)

That way it's clear that the ops will be ignored or not, more likely with comments to justify.

if len(setDevicesOps) == 0 {
// No need to assert only that the machine is alive
logger.Debugf("no changes to LinkLayerDevices for machine %q", m.Id())
return nil, errNoChanges
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return nil, jujutxn.ErrNoOperations

then you can drop the check below

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks, I expected something like that, but there wasn't anything like that around this code.

Check directly if the machine is still alive, instead of hitting the db immediately.
We also don't need our own 'noChanges' as jujutxn.ErrNoOperations already exists.
@@ -206,18 +206,27 @@ func (m *Machine) SetLinkLayerDevices(devicesArgs ...LinkLayerDeviceArgs) (err e
}
}

// We've checked the model is alive directly, and we assert the machine is alive, we don't need to also
// assert the model is alive, because then the machine would be dying as well.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you're right, and we should only care about checking model life when creating new resources.

IMO, the in-memory life check should be dropped too. It's of dubious value when there's no accompanying txn op, since it's no longer ensuring integrity - so it's really just an optimisation. This makes things more difficult to understand, because it's not clear why we would do it in only memory from the context (hence this question).

FWIW, I've taken to the pattern of:

// checkSomething checks something in memory, and then return txn.Ops that assert the same
func checkSomething() ([]txn.Op, error)

That way it's clear that the ops will be ignored or not, more likely with comments to justify.

@@ -600,13 +615,13 @@ func (m *Machine) SetDevicesAddresses(devicesAddresses ...LinkLayerDeviceAddress
return nil, errors.Trace(err)
}

if err := checkModelActive(m.st); err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as above

@jameinel
Copy link
Member Author

jameinel commented Apr 3, 2017

$$merge$$

@jujubot
Copy link
Collaborator

jujubot commented Apr 3, 2017

Status: merge request accepted. Url: http://juju-ci.vapour.ws:8080/job/github-merge-juju

@jujubot
Copy link
Collaborator

jujubot commented Apr 3, 2017

Build failed: Generating tarball failed
build url: http://juju-ci.vapour.ws:8080/job/github-merge-juju/10611

@jameinel
Copy link
Member Author

jameinel commented Apr 3, 2017

$$merge$$

@jujubot
Copy link
Collaborator

jujubot commented Apr 3, 2017

Status: merge request accepted. Url: http://juju-ci.vapour.ws:8080/job/github-merge-juju

@jujubot jujubot merged commit ab4ee25 into juju:develop Apr 3, 2017
@jameinel jameinel deleted the 2.1-set-devices-1677619 branch April 22, 2017 16:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
3 participants