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

Added SpotInstance Percentage Multiplier with dynamic tags #175

Merged
merged 1 commit into from
Dec 13, 2017

Conversation

kartik894
Copy link
Contributor

@kartik894 kartik894 commented Dec 3, 2017

Issue Type

Summary

Added configurable policy to place bids at a multiple of spot instance price or at on-demand price. Also added dynamic tag to enable this feature for an auto-scaling group.

Code review process:

The issue should be tagged with 'review wanted' label before the checklist below
is satisfied from the perspective of the PR author and the code is ready to be
peer-reviewed.

Code contribution checklist

  1. The contribution fixes a single existing github issue, and it is linked
    to it.
  2. The code is as simple as possible, readable and follows the idiomatic Go
    guidelines.
  3. All new functionality is covered by automated test cases so the overall
    test coverage doesn't decrease.
  4. No issues are reported when running make full-test.
  5. Functionality not applicable to all users should be configurable.
  6. Configurations should be exposed through Lambda function environment
    variables which are also passed as parameters to the
    CloudFormation
    and
    Terraform
    stacks defined as infrastructure code.
  7. Global configurations set from the infrastructure stack level should also
    support per-group overrides using tags.
  8. Tags names and expected values should be similar to the other existing
    configurations.
  9. Both global and tag-based configuration mechanisms should be tested and
    proven to work using log output from various test runs.
  10. The logs should be kept as clean as possible (use log levels as
    appropriate) and formatted consistently to the existing log output.
  11. The documentation is updated to cover the new behavior, as well as the
    new configuration options for both stack parameters and tag overrides.
  12. A code reviewer reproduced the problem and can confirm the code
    contribution actually resolves it.

Copy link
Member

@cristim cristim left a comment

Choose a reason for hiding this comment

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

Thanks for this contribution, this piece of functionality is great to have, I'm really excited about it and looking forward to see it merged.

START.md Outdated
runtime hour before spot termination is free of charge.
(default 1.1)

-spot_price_multiplier_flag bool
Copy link
Member

Choose a reason for hiding this comment

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

I would prefer to have this a multi-option flag instead of a boolean, perhaps named something like 'bidding_policy', and accepting string values such as 'normal' and 'aggressive' and document them in detail. This way we can add more policies later on.

Copy link
Contributor

Choose a reason for hiding this comment

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

The idea would be to have a "policy type" and a tag associated with the configuration of this specific policy, is that correct @cristim ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes

autospotting.go Outdated
@@ -30,7 +30,8 @@ func main() {
func run() {
log.Println("Starting autospotting agent, build ", Version, "expiring on", ExpirationDate)

if isExpired(ExpirationDate) {
if false {
Copy link
Member

Choose a reason for hiding this comment

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

Please don't change the expiration logic.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure if the PR is done or not, but please do not leave commented code

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Will change this.

Copy link
Contributor

@xlr-8 xlr-8 left a comment

Choose a reason for hiding this comment

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

Thanks for the contribution! Always appreciate to see new contributors! :)

I've made one comment which has big impacts, basically the flag option seems unnecessary to me, by defaulting the multiplier to 1.0, then it removes many code pieces which are made more complicated than they could be.

I had the same question/issue when implementing the flags for the algorithm https://github.com/cristim/autospotting/pull/54; and I realized I could make it easier by removing such cases.

If there is anything that seems unclear to you, or you disagree with, please let me know - I'd be happy to clarify/discuss.

START.md Outdated
Multiplier for the spot price. Placing bids a fraction of a cent over the
current price would aggressively optimize for cost savings since the last
runtime hour before spot termination is free of charge.
(default 1.1)
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd rather put 1.0, so that it doesn't change people's cost optimization/bidding strategy.
That could however be a note/advise in the documentation and/or be updated later on if we realize many people are updating this value to something slightly higher.

Copy link
Member

Choose a reason for hiding this comment

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

The 1.0 will place spot instance bids at exactly the current market price, the 1.1 is there to offer a 10% buffer above the current price.

But I also find it a bit confusing, perhaps we should switch to percentages and also support absolute value increments, something like spot_price_buffer_percentage and spot_price_buffer_amount, what do you guys think?

Copy link
Member

Choose a reason for hiding this comment

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

@kartik894, @xlr-8 what do you think about my proposal to use percentage or absolut values for the spot price buffer?

I think something like spot_price_buffer_percentage=10(and make it a default when the aggressive policy is used) is more intuitive than the current spot_price_multiplier=1.1 for setting the bid to 10% over the current spot price. Also, negative percentage numbers don't make sense in this context, but the multiplier gives the users the chance to mess it up by giving 0.9, which will fail to launch any instances.

This is also quite consistent to the existing percentage/value concept which we use for keeping on-demand capacity, where we can have an absolute value as well as a percentage. This would bid X+current_price, where X may be given as a small fractional number, such as spot_price_buffer=0.001.

Copy link
Contributor

@xlr-8 xlr-8 Dec 11, 2017

Choose a reason for hiding this comment

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

Your various comments made me think of: https://github.com/cristim/autospotting/pull/175/files#r156044213

We need to check the value anyway, just like it's done for the percentage/raw-value for on-demand instances. Also this value can't be set at an autoscaling-level if I'm not mistaken. Don't know if that's a goal, but if so some code need to be adapted.

But I agree that percentage might make more sense here, as it's a multiplier rather a raw number.

autospotting.go Outdated
@@ -30,7 +30,8 @@ func main() {
func run() {
log.Println("Starting autospotting agent, build ", Version, "expiring on", ExpirationDate)

if isExpired(ExpirationDate) {
if false {
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure if the PR is done or not, but please do not leave commented code

a.SpotPriceMultiplierFlag = newValue
return done
}
}
Copy link
Contributor

@xlr-8 xlr-8 Dec 3, 2017

Choose a reason for hiding this comment

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

I like the idea of loading the value dynamically, I guess it should be adapted to have a map of string/func based on the tag's value.

@@ -39,6 +47,8 @@ type autoScalingGroup struct {
// spot instance requests generated for the current group
spotInstanceRequests []*spotInstanceRequest
minOnDemand int64

SpotPriceMultiplierFlag bool
Copy link
Contributor

Choose a reason for hiding this comment

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

I believe this variable doesn't need to be exported


// DefaultSpotPriceMultiplierFlag stores the default flag value for the choice
// of bid on a per-group level
DefaultSpotPriceMultiplierFlag = false
Copy link
Contributor

@xlr-8 xlr-8 Dec 3, 2017

Choose a reason for hiding this comment

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

Regarding @cristim comments I assume that should be the policy type, rather than a flag

@@ -182,6 +239,8 @@ func (a *autoScalingGroup) process() {
a.scanInstances()
a.loadDefaultConfig()
a.loadConfigFromTags()
a.loadDefaultSpotConfig()
a.loadSpotConfigFromTags()
Copy link
Contributor

@xlr-8 xlr-8 Dec 4, 2017

Choose a reason for hiding this comment

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

I think those 2 methods could be put directly in the previous 2 methods (loadDefaultConfig & loadConfigFromTags) that were created for that; in case extra elements were to be added.

While re-reading the code I also realised it could be slightly re-arranged in order to load the default configuration and ASG configuration earlier than during the processing, see https://github.com/cristim/autospotting/issues/180.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

loadDefaultConfig has dedicated tests and merging mine with that will be a bit messy. What do you think @cristim ?

Copy link
Member

Choose a reason for hiding this comment

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

I agree with @xlr-8, the purpose of tests is for making such refactoring easier, let's merge these because they do more or less the same thing.

}

func (a *autoScalingGroup) loadConfSpot() bool {
tagList := [1]string{SpotBidChoice}
Copy link
Contributor

Choose a reason for hiding this comment

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

Why declaring an array of string of 1 and not directly a string tagKey?

logger.Println("Launching spot instance with a bid =", baseOnDemandPrice)
return baseOnDemandPrice
}

Copy link
Contributor

@xlr-8 xlr-8 Dec 4, 2017

Choose a reason for hiding this comment

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

I suppose the multiplier could be used here: https://github.com/cristim/autospotting/blob/master/core/region.go#L236 or here: https://github.com/cristim/autospotting/blob/master/core/instance.go#L160-L162 - that would avoid using this method.

Just like this new parameter actually: https://github.com/cristim/autospotting/pull/163/files#diff-17a0faaa6296d309bb099b250ff6ead1R162.

I'd tend to prefer putting them in the instance.go file as it seems more logical, but the main method would need to take new parameters such as biding_policies or multipliers or something like this.

@cristim what are your thoughts on this?

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 can be done, but at the repo level - requestSpotPrices() might be used for another bidding policy in the future and I wouldn't want to touch that. isPriceCompatible() is used to determine the choice of spot instances to be spawned for the current on-demand instance - Also wouldn't want to change that. I did think of these scenarios before I begun the implementation, but I figured it would be best placed in autoscaling.go since that is where you call for the bidPrice to be placed.

tt.multiplier, tt.flag, actualPrice, tt.want, currentSpotPrice)
}
}
}
Copy link
Contributor

@xlr-8 xlr-8 Dec 4, 2017

Choose a reason for hiding this comment

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

Rest of the tests seem good to me!

@cristim
Copy link
Member

cristim commented Dec 4, 2017

@xlr-8 we can't entirely get rid of the boolean or the string flag because this is meant to introduce an additional bidding policy, so we basically need a switch to choose between the old and this one and a second piece of configuration (or maybe later more) for tweaking this additional bidding strategy.

In theory we could swing to the new bidding strategy only if its configuration tweaking option is different from a default value, but I would prefer to use an explicit, dedicated flag for this switch.

@xlr-8
Copy link
Contributor

xlr-8 commented Dec 5, 2017

Alright, thx @cristim I'll update my comments accordingly, sorry for the delay @kartik894

Copy link
Contributor

@xlr-8 xlr-8 left a comment

Choose a reason for hiding this comment

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

I try to update all previous comments, if anything's missing please let me know. As the strategy is to go with policy name then some code has to be updated

}

}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Would need to be updated to fetch various decided policy names

START.md Outdated
Multiplier for the spot price. Placing bids a fraction of a cent over the
current price would aggressively optimize for cost savings since the last
runtime hour before spot termination is free of charge.
(default 1.1)
Copy link
Member

Choose a reason for hiding this comment

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

@kartik894, @xlr-8 what do you think about my proposal to use percentage or absolut values for the spot price buffer?

I think something like spot_price_buffer_percentage=10(and make it a default when the aggressive policy is used) is more intuitive than the current spot_price_multiplier=1.1 for setting the bid to 10% over the current spot price. Also, negative percentage numbers don't make sense in this context, but the multiplier gives the users the chance to mess it up by giving 0.9, which will fail to launch any instances.

This is also quite consistent to the existing percentage/value concept which we use for keeping on-demand capacity, where we can have an absolute value as well as a percentage. This would bid X+current_price, where X may be given as a small fractional number, such as spot_price_buffer=0.001.

autospotting.go Outdated
flag.Float64Var(&c.SpotPriceMultiplier, "spot_price_multiplier", 1.1,
"Multiplier for the spot price. Placing bids a fraction of a cent over the current price\n"+
"\twould aggressively optimize for cost savings since the last runtime hour before spot termination\n"+
"\tis free of charge.")
Copy link
Member

Choose a reason for hiding this comment

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

It turns out after the per-second billing the last hour is no longer free of charge.

As of now the main benefit is that it protects the group from running spot instances that got significantly more expensive than when they were initially launched, but still somewhat less than the on-demand price.

For example:

  • on-demand price: 1
  • spot-price: 0.2 but increased to 0.9 over time
  • this instance would be terminated by this new policy as soon as the price was exceeding 0.22 (using the vehiculated 10% price buffer we want to use as default the bid price would be 0.22, instead of 1).

With the default policy the spot instance in the above example may run forever after this significant price increase unless the price goes over 1.

This allows us to find other spot instance types that may be priced better as of the moment when the price increased.

Copy link
Contributor Author

@kartik894 kartik894 Dec 11, 2017

Choose a reason for hiding this comment

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

I'm going ahead with percentage value as it seems more intuitive. Is that cool @xlr-8 @cristim ?

logger.Println("a.BiddingPolicy: ", a.BiddingPolicy)
if a.BiddingPolicy == "aggressive" {
logger.Println("Launching spot instance with a bid =", currentSpotPrice*a.region.conf.SpotPriceMultiplier)
return currentSpotPrice * a.region.conf.SpotPriceMultiplier
Copy link
Contributor

Choose a reason for hiding this comment

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

I realised that there might be a potential issue here, if the spot price is close to the on-demand price for some reasons (or the multiplier very high), then having the SpotPriceMultiplier might allow the bidding to go above that value. e.g.:

spotPrice: 0.5
onDemandPrice: 1
spotPriceMultiplier: 3.0
spotBid: => 1.5

# Other case
spotPrice: 0.9 (couldn't find better)
onDemandPrice: 1
spotPriceMultiplier: 1.5
spotBid: => 1.35

I think we might want to check that this potential maximum price isn't higher than the on-demand price, just for the sake of avoiding it; or we might end with spot instances running costing more than on-demand.

Copy link
Member

Choose a reason for hiding this comment

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

Good catch!

We can just take the minimum between ondemand price and the calculated spot price

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Cool, will do.

Copy link
Member

@cristim cristim left a comment

Choose a reason for hiding this comment

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

I approve what I saw so far, although I can't try it out these days.

@xlr-8 do you have any concerns about this or can we merge it?

Copy link
Contributor

@xlr-8 xlr-8 left a comment

Choose a reason for hiding this comment

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

Couple questions/changes that seem worth checking Also, I haven't yet tested it.
However the overall logic seems good to me, did it react as you expected?

@@ -14,6 +14,16 @@ variable "asg_on_demand_price_multiplier" {
default = "1.0"
}

variable "asg_spot_price_buffer_percentage" {
description = "Percentage above the current spot price to place the bid"
default = "1.1"
Copy link
Contributor

Choose a reason for hiding this comment

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

Isn't it supposed to be 10.0?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah yes, my bad. Will change and ping you.

@@ -4,6 +4,8 @@ module "autospotting" {
autospotting_min_on_demand_number = "${var.asg_min_on_demand_number}"
autospotting_min_on_demand_percentage = "${var.asg_min_on_demand_percentage}"
autospotting_on_demand_price_multiplier = "${var.asg_on_demand_price_multiplier}"
autospotting_spot_price_buffer_percentage = "${var.asg_spot_price_buffer_percentage}"
autospotting_bidding_policy = "${var.asg_bidding_policy}"
Copy link
Contributor

Choose a reason for hiding this comment

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

Could run terraform fmt -write=true to format it properly

description = "Choice of bidding policy for the spot instance"
default = "normal"
}

Copy link
Contributor

Choose a reason for hiding this comment

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

Could run terraform fmt -write=true on those as well; I shall update the doc on this point

currentOnDemandPrice: 0.0464,
policy: "aggressive",
want: 0.02376,
},
Copy link
Contributor

Choose a reason for hiding this comment

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

Good testing of all cases! :)

logger.Printf("Ignoring out of range value : %f\n", spotPriceBufferPercentage)
}
return DefaultSpotPriceBufferPercentage, false
}
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 don't mind, I'd rather have the function rewritten as such:

func (a *autoScalingGroup) loadSpotPriceBufferPercentage(tagValue *string) (float64, bool) {
	spotPriceBufferPercentage, err := strconv.ParseFloat(*tagValue, 64)
	
	if err != nil {
		logger.Printf("Error with ParseFloat: %s\n", err.Error())
		return DefaultSpotPriceBufferPercentage, false
	} else if spotPriceBufferPercentage <= 0 {
		logger.Printf("Ignoring out of range value : %f\n", spotPriceBufferPercentage)
		return DefaultSpotPriceBufferPercentage, false
	}
	
	logger.Printf("Loaded SpotPriceBufferPercentage value to %f from tag %s\n", spotPriceBufferPercentage, SpotPriceBufferPercentageTag)
	return spotPriceBufferPercentage, true
}

I believe it works the same way as yours, but just seems more clear to me


// DefaultBiddingPolicy stores the default bidding policy for
// the spot bid on a per-group level
DefaultBiddingPolicy = "normal"
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 those new variables (except SpotPriceBufferPercentageTag) don't need to be exported.
It's most likely true for other older one as well, would need to be corrected. (Opened https://github.com/cristim/autospotting/issues/186)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Should I leave it as is in this pull request?

Copy link
Contributor

Choose a reason for hiding this comment

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

I believe it's fine like this, I'll pass on those later on


return baseOnDemandPrice
}

Copy link
Contributor

Choose a reason for hiding this comment

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

I would also rewrite that as:

func (a *autoScalingGroup) getPricetoBid(
	baseOnDemandPrice float64, currentSpotPrice float64) float64 {

	logger.Println("BiddingPolicy: ", a.region.conf.BiddingPolicy)

	if a.region.conf.BiddingPolicy == DefaultBiddingPolicy {
		logger.Println("Launching spot instance with a bid =", baseOnDemandPrice)
		return baseOnDemandPrice
	}

	logger.Println("Launching spot instance with a bid =", math.Min(baseOnDemandPrice, currentSpotPrice*(1.0+a.region.conf.SpotPriceBufferPercentage/100.0)))
	return math.Min(baseOnDemandPrice, currentSpotPrice*(1.0+a.region.conf.SpotPriceBufferPercentage/100.0))
}

Please note I removed the check of the value, as it seemed already done here: https://github.com/cristim/autospotting/pull/175/files#diff-de828bac595094756ea2a28fa415583bR219

My logic is always to avoid imbricated if, if there is an error check or direct return, then do it. I find that it usually simplifies reading/understanding of the code.

@kartik894
Copy link
Contributor Author

kartik894 commented Dec 13, 2017

@xlr-8 @cristim Apart from running unit tests, I tested it on AWS too. Works fine. Ran the terraform fmt command to clean the formatting too.

@kartik894 kartik894 changed the title Added SpotInstance Multipler with dynamic tags Added SpotInstance Percentage Multiplier with dynamic tags Dec 13, 2017
@xlr-8
Copy link
Contributor

xlr-8 commented Dec 13, 2017

Cool, code looks good to me, please:

  • fix the code climate issues
  • rebase the PR for git log history purposes

On my side, I'll give it a quick shot tonight on AWS and I'll merge if everything worked as expected.

@kartik894
Copy link
Contributor Author

Will do the rebasing. The code climate points to cognitive complexity in two sections of the code which I'm not sure how to fix.

debug.Println("Couldn't find tag", SpotPriceBufferPercentageTag)
}
return false
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I believe this could be rewritten as:

func (a *autoScalingGroup) loadConfSpotPrice() bool {

	tagValue := a.getTagValue(SpotPriceBufferPercentageTag)
	if tagValue == nil {
		return false
	}

	newValue, done := a.loadSpotPriceBufferPercentage(tagValue)
	if !done {
		debug.Println("Couldn't find tag", SpotPriceBufferPercentageTag)
		return false
	}

	a.region.conf.SpotPriceBufferPercentage = newValue
	return done
}

Which would also solve code climate issue I believe, as the comment about imbricated if/else is something that adds complexity to the code and that code climate dislike.

logger.Println("BiddingPolicy =", a.region.conf.BiddingPolicy)
return done
}
}
} else {
debug.Println("Couldn't find tag", tagKey)
}
}
return false
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Applying the same principle as before, it could be re-written as:

func (a *autoScalingGroup) loadConfSpot() bool {
	tagList := []string{BiddingPolicyTag}
	loadDyn := map[string]func(*string) (string, bool){
		BiddingPolicyTag: a.loadBiddingPolicy,
	}

	for _, tagKey := range tagList {
		tagValue := a.getTagValue(tagKey)
		if tagValue == nil {
			debug.Println("Couldn't find tag", tagKey)
			continue
		}
		if _, ok := loadDyn[tagKey]; !ok {
			debug.Println("No method associated with tag", tagKey)
			continue
		}
		if newValue, done := loadDyn[tagKey](tagValue); done {
			a.region.conf.BiddingPolicy = newValue
			logger.Println("BiddingPolicy =", a.region.conf.BiddingPolicy)
			return done
		}
	}
	return false
}

Or considering we have only one policy, even as simple as:

func (a *autoScalingGroup) loadConfSpot() bool {
	tagValue := a.getTagValue(BiddingPolicyTag)
	if tagValue == nil {
		debug.Println("Couldn't find tag", BiddingPolicyTag)
		return false
	}
	if newValue, done := a.loadBiddingPolicy(tagValue); done {
		a.region.conf.BiddingPolicy = newValue
		logger.Println("BiddingPolicy =", a.region.conf.BiddingPolicy)
		return done
	}
	return false
}

I presume both could help/should solve the codeClimate issue

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The first one still failed the CodeClimate run.... So, I used the second one.

@xlr-8
Copy link
Contributor

xlr-8 commented Dec 13, 2017

Tests on AWS worked as expected! Waiting for the rebase before giving it a final look/merge. Thanks again!

@p-kar p-kar force-pushed the master branch 3 times, most recently from e8e87e0 to a1cac1e Compare December 13, 2017 22:49
It is now possible to bid at a value based on the spot instance price
('aggressive' policy) rather than on-demand price ('normal' policy). Thus
allowing to bid for a price slightly higher than the spot price. This is
particularly useful to avoid having spot instances that cost too much.

To enable this, the policy has to be set as 'aggressive' ('normal' default) and
a 'buffer percentage' needs to be defined (10% higher default) to know how much
more users are willing to pay above the spot price found. Details about the
tags are provided in START.md.

Those parameters are configurable from Cloudfront/Terraform and the ASGs.
@LeanerCloud LeanerCloud deleted a comment from coveralls Dec 13, 2017
@LeanerCloud LeanerCloud deleted a comment from coveralls Dec 13, 2017
@LeanerCloud LeanerCloud deleted a comment from coveralls Dec 13, 2017
@LeanerCloud LeanerCloud deleted a comment from coveralls Dec 13, 2017
@LeanerCloud LeanerCloud deleted a comment from coveralls Dec 13, 2017
@LeanerCloud LeanerCloud deleted a comment from coveralls Dec 13, 2017
@LeanerCloud LeanerCloud deleted a comment from coveralls Dec 13, 2017
@LeanerCloud LeanerCloud deleted a comment from coveralls Dec 13, 2017
@LeanerCloud LeanerCloud deleted a comment from coveralls Dec 13, 2017
@LeanerCloud LeanerCloud deleted a comment from coveralls Dec 13, 2017
@LeanerCloud LeanerCloud deleted a comment from coveralls Dec 13, 2017
@coveralls
Copy link

Coverage Status

Coverage increased (+1.2%) to 73.455% when pulling 17a39d1 on kartik894:master into 0898534 on cristim:master.

@LeanerCloud LeanerCloud deleted a comment from coveralls Dec 13, 2017
@LeanerCloud LeanerCloud deleted a comment from coveralls Dec 13, 2017
@LeanerCloud LeanerCloud deleted a comment from coveralls Dec 13, 2017
@LeanerCloud LeanerCloud deleted a comment from coveralls Dec 13, 2017
@LeanerCloud LeanerCloud deleted a comment from coveralls Dec 13, 2017
@xlr-8
Copy link
Contributor

xlr-8 commented Dec 13, 2017

All good! 👍 🎉

@xlr-8 xlr-8 merged commit 053135e into LeanerCloud:master Dec 13, 2017
Copy link
Member

@cristim cristim left a comment

Choose a reason for hiding this comment

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

@kartik894 Huge thanks for this contribution, this is a great feature and I'm really glad to finally see it land.

@kartik894
Copy link
Contributor Author

My pleasure 👍

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.

4 participants