Skip to content

Commit

Permalink
Added spot premium (LeanerCloud#423)
Browse files Browse the repository at this point in the history
Co-authored-by: Andrew Partis <[email protected]>
  • Loading branch information
tapirs and Andrew Partis authored Mar 24, 2020
1 parent 3313298 commit ecf31a5
Show file tree
Hide file tree
Showing 7 changed files with 62 additions and 5 deletions.
4 changes: 4 additions & 0 deletions START.md
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,10 @@ Usage of ./AutoSpotting:
-spot_product_description="Linux/UNIX (Amazon VPC)":
The Spot Product or operating system to use when looking up spot price history in the market.
Valid choices: Linux/UNIX | SUSE Linux | Windows | Linux/UNIX (Amazon VPC) | SUSE Linux (Amazon VPC) | Windows (Amazon VPC)
-spot_product_premium=0:
The Product Premium to apply to the on demand price to improve spot
selection and savings calculations when using a premium instance type
such as RHEL.
-tag_filters=[{spot-enabled true}]: Set of tags to filter the ASGs on. Default is -tag_filters 'spot-enabled=true'
Example: ./AutoSpotting -tag_filters 'spot-enabled=true,Environment=dev,Team=vision'
Expand Down
20 changes: 18 additions & 2 deletions cloudformation/stacks/AutoSpotting/template.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -219,13 +219,21 @@
- "Linux/UNIX (Amazon VPC)"
- "SUSE Linux (Amazon VPC)"
- "Windows (Amazon VPC)"
- "Red Hat Enterprise Linux"
Default: "Linux/UNIX (Amazon VPC)"
Description: >
"The Spot Product or operating system to use when looking up spot price
history in the market. Valid choices: 'Linux/UNIX | SUSE Linux | Windows
| Linux/UNIX (Amazon VPC) | SUSE Linux (Amazon VPC) | Windows (Amazon
VPC)'"
VPC) | Red Hat Enterprise Linux'"
Type: "String"
SpotProductPremium:
Default: 0.0
Description: >
"The Product Premium to apply to the on demand price to improve spot
selection and savings calculations when using a premium instance type
such as RHEL."
Type: "Number"
StackSetsMainRegion:
Default: "us-east-1"
Description: >
Expand Down Expand Up @@ -304,6 +312,12 @@
Fn::GetAtt:
- "LambdaRegionalExecutionRole"
- "Arn"
LambdaExecutionRoleARN:
Condition: "StackSetsIsMain"
Value:
Fn::GetAtt:
- "LambdaExecutionRole"
- "Arn"
Resources:
AutoSpotTeminationEventRule:
Condition: "StackSetsTrue"
Expand Down Expand Up @@ -419,6 +433,8 @@
Ref: "SpotPricePercentageBuffer"
SPOT_PRODUCT_DESCRIPTION:
Ref: "SpotProductDescription"
SPOT_PRODUCT_PREMIUM:
Ref: "SpotProductPremium"
TAG_FILTERING_MODE:
Ref: "TagFilteringMode"
TAG_FILTERS:
Expand Down Expand Up @@ -763,4 +779,4 @@
- "cloudformation:RegisterListener"
- "cloudformation:GetListenerCredentials"
Effect: "Allow"
Resource: "*"
Resource: "*"
4 changes: 2 additions & 2 deletions core/autoscaling.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ func (a *autoScalingGroup) allInstancesRunning() bool {
func (a *autoScalingGroup) calculateHourlySavings() float64 {
var savings float64
for i := range a.instances.instances() {
savings += i.typeInfo.pricing.onDemand - i.price
savings += (i.typeInfo.pricing.onDemand + i.typeInfo.pricing.premium) - i.price
}
return savings
}
Expand Down Expand Up @@ -212,7 +212,7 @@ func (a *autoScalingGroup) scanInstances() instances {
if i.isSpot() {
i.price = i.typeInfo.pricing.spot[*i.Placement.AvailabilityZone]
} else {
i.price = i.typeInfo.pricing.onDemand
i.price = i.typeInfo.pricing.onDemand + i.typeInfo.pricing.premium
}

a.instances.add(i)
Expand Down
5 changes: 5 additions & 0 deletions core/autoscaling_configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ const (
// to use when looking up spot price history in the market.
DefaultSpotProductDescription = "Linux/UNIX (Amazon VPC)"

// DefaultSpotProductPremium stores the default value to add to the
// on demand price for premium instance types.
DefaultSpotProductPremium = 0.0

// DefaultMinOnDemandValue stores the default on-demand capacity to be kept
// running in a group managed by autospotting.
DefaultMinOnDemandValue = 0
Expand Down Expand Up @@ -93,6 +97,7 @@ type AutoScalingConfig struct {
SpotPriceBufferPercentage float64

SpotProductDescription string
SpotProductPremium float64

BiddingPolicy string

Expand Down
27 changes: 27 additions & 0 deletions core/autoscaling_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -929,6 +929,7 @@ func TestScanInstances(t *testing.T) {
typeInfo: instanceTypeInformation{
pricing: prices{
onDemand: 0.5,
premium: 0.0,
spot: map[string]float64{
"az-1": 0.1,
"az-2": 0.2,
Expand All @@ -947,6 +948,7 @@ func TestScanInstances(t *testing.T) {
typeInfo: instanceTypeInformation{
pricing: prices{
onDemand: 0.8,
premium: 0.0,
spot: map[string]float64{
"az-1": 0.4,
"az-2": 0.5,
Expand Down Expand Up @@ -3234,6 +3236,31 @@ func Test_autoScalingGroup_calculateHourlySavings(t *testing.T) {
}),
want: 0.9,
},
{
name: "premium-instance",
instances: makeInstancesWithCatalog(
instanceMap{
"ondemand-1": {
price: 1.6,
typeInfo: instanceTypeInformation{
pricing: prices{
onDemand: 1.0,
premium: 0.6,
},
},
},
"spot-1": {
price: 0.1,
typeInfo: instanceTypeInformation{
pricing: prices{
onDemand: 1.0,
premium: 0.6,
},
},
},
}),
want: 1.5,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down
5 changes: 4 additions & 1 deletion core/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,10 @@ func ParseConfig(conf *Config) {
flagSet.StringVar(&conf.SpotProductDescription, "spot_product_description", DefaultSpotProductDescription,
"\n\tThe Spot Product to use when looking up spot price history in the market.\n"+
"\tValid choices: Linux/UNIX | SUSE Linux | Windows | Linux/UNIX (Amazon VPC) | \n"+
"\tSUSE Linux (Amazon VPC) | Windows (Amazon VPC)\n\tDefault value: "+DefaultSpotProductDescription+"\n")
"\tSUSE Linux (Amazon VPC) | Windows (Amazon VPC) | Red Hat Enterprise Linux\n\tDefault value: "+DefaultSpotProductDescription+"\n")
flagSet.Float64Var(&conf.SpotProductPremium, "spot_product_premium", DefaultSpotProductPremium,
"\n\tThe Product Premium to apply to the on demand price to improve spot selection and savings calculations\n"+
"\twhen using a premium instance type such as RHEL.")
flagSet.StringVar(&conf.TagFilteringMode, "tag_filtering_mode", "opt-in", "\n\tControls the behavior of the tag_filters option.\n"+
"\tValid choices: opt-in | opt-out\n\tDefault value: 'opt-in'\n\tExample: ./AutoSpotting --tag_filtering_mode opt-out\n")
flagSet.StringVar(&conf.FilterByTags, "tag_filters", "", "\n\tSet of tags to filter the ASGs on.\n"+
Expand Down
2 changes: 2 additions & 0 deletions core/region.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ type prices struct {
onDemand float64
spot spotPriceMap
ebsSurcharge float64
premium float64
}

// The key in this map is the availavility zone
Expand Down Expand Up @@ -209,6 +210,7 @@ func (r *region) determineInstanceTypeInformation(cfg *Config) {
price.onDemand = it.Pricing[r.name].Linux.OnDemand * cfg.OnDemandPriceMultiplier
price.spot = make(spotPriceMap)
price.ebsSurcharge = it.Pricing[r.name].EBSSurcharge
price.premium = r.conf.SpotProductPremium

// if at this point the instance price is still zero, then that
// particular instance type doesn't even exist in the current
Expand Down

0 comments on commit ecf31a5

Please sign in to comment.