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

feat(rollback): avoid scaling down during rollback #3185

Merged
merged 3 commits into from
Sep 23, 2019

Conversation

dreynaud
Copy link
Contributor

Should partly address spinnaker/spinnaker#4895 by avoiding resizing the restore server group down.
This does not address concurrent actions issues, so it is probably still possible to have race conditions result in bad things.

So fundamentally this changes:

  • computes the max of the current restore server group size and the rollback server group size
  • only generates a resize stage if that computed size is bigger than the current size

This also opens the possibility to skip the snapshot/restoreMin tasks if we are not going to resize (which would pin the min of the restore server group).

Should partly address spinnaker/spinnaker#4895 by avoiding resizing the restore server group down.
This does not address concurrent actions issues, so it is probably still possible to have race conditions result in bad things.

So fundamentally this changes:
* computes the max of the current restore server group size and the rollback server group size
* only generates a resize stage if that computed size is bigger than the current size

This also opens the possibility to skip the snapshot/restoreMin tasks if we are not going to resize (which would pin the min of the restore server group).
@dreynaud
Copy link
Contributor Author

cc @luispollo

@@ -56,6 +56,13 @@ interface ResizeStrategy {
Integer max
Integer desired
Integer min

// from https://codereview.stackexchange.com/questions/8801/returning-groovy-class-fields-as-a-map
public Map asMap() {
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 can just call .properties() to get the value as a map?

Copy link
Contributor

Choose a reason for hiding this comment

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

or I guess that has some groovy magic-junk in it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I get this though

MissingMethodException: No signature of method: Capacity.properties() is applicable for argument types: () values: []

Copy link
Contributor

Choose a reason for hiding this comment

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

If you're still interested, it's a property, not a method:

> import com.netflix.spinnaker.orca.kato.pipeline.support.ResizeStrategy.Capacity
> 
> Capacity c = new Capacity(3,2,1)
> print(c.properties)
[class:class com.netflix.spinnaker.orca.kato.pipeline.support.ResizeStrategy$Capacity, max:3, min:1, desired:2]

@dreynaud
Copy link
Contributor Author

cc @gal-yardeni

targetLocation : restoreServerGroup.getLocation(),
account : restoreServerGroup.credentials,
cloudProvider : restoreServerGroup.cloudProvider,
pinMinimumCapacity : true,
Copy link
Contributor

Choose a reason for hiding this comment

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

wy do you need this if you already set min=desired

Copy link
Contributor Author

Choose a reason for hiding this comment

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

it's not needed, I thought I would just leave it like that to express the intent anyway

Map resizeServerGroupContext = new HashMap(parentStage.context) + [
action : ResizeStrategy.ResizeAction.scale_exact.toString(),
capacity : newRestoreCapacity.asMap(),
asgName : restoreServerGroupName,
Copy link
Contributor

Choose a reason for hiding this comment

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

add serverGroupName?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't like it, but I will do it

]

return newStage(parentStage.execution, resizeServerGroupStage.type,
"Resize Restore Server Group to (min: ${newRestoreCapacity.min}, max: ${newRestoreCapacity.max}, desired: ${newRestoreCapacity.desired})",
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: Resize Restore is pretty confusing wording.. maybe Resize {serverGroupaName}...?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

good point!

fromContext.location
).orElse(null)
} catch(Exception e) {
log.error('Skipping resize stage because there was an error looking up {}', serverGroupName, e)
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 this error would be better in line 157/162.

that way you can differentiate between failure in asg lookup vs there legitimately not being an ASG

Copy link
Contributor Author

Choose a reason for hiding this comment

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

chatted offline, will change the way we log when we get an empty optional

Copy link
Contributor

@marchello2000 marchello2000 left a comment

Choose a reason for hiding this comment

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

i think i understand it... but i definitely love it

newRestoreCapacity.min = newRestoreCapacity.desired

ResizeStrategy.Capacity currentCapacity = restoreServerGroup.capacity
if (currentCapacity == newRestoreCapacity) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't this be comparing with .equals() instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

it is Groovy so it does some magic behind the scenes that ultimately does call .equals()

Copy link
Contributor

@luispollo luispollo Sep 23, 2019

Choose a reason for hiding this comment

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

Actually I think it's because of the way that @Canonical annotation is implemented (they use property values for hashCode() so .equals() ends up being the same as ==). But that's TMI. 😄

return stages
}

@Nullable TargetServerGroup lookupServerGroup(Stage parentStage, String serverGroupName) {
Copy link
Member

Choose a reason for hiding this comment

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

nit: Wouldn't mind seeing @Nonnull etc added to the method args, too.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

do we have an explicit convention around this or tooling that actually takes advantage of that?

Otherwise it seems overkill, for me the implicit contract is that things that are not explicitly @Nullable must be @Nonnull

@luispollo
Copy link
Contributor

@dreynaud Thanks a lot for tagging me for review. Still very early days for me so as you could see, not a lot of insightful comments, but for whatever it's worth, your changes made sense to me.

@dreynaud
Copy link
Contributor Author

dreynaud commented Sep 23, 2019 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants