Skip to content

Commit 959c4d6

Browse files
adrianrioboanjannath
authored andcommitted
fix: azure spot metrics by region is causing rate limit issues invoking azure api
this commit will use a single query to get the information and the split it across each regions calculation. This is a similar behavior as we had before the spot module refactor and should fix the rate limit issues. Fixes #573. Signed-off-by: Adrian Riobo <[email protected]>
1 parent 209569e commit 959c4d6

File tree

1 file changed

+67
-38
lines changed

1 file changed

+67
-38
lines changed

pkg/provider/azure/data/spot.go

Lines changed: 67 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,14 @@ import (
2323
const (
2424
querySpotPrice = "SpotResources | where type =~ 'microsoft.compute/skuspotpricehistory/ostype/location' " +
2525
"and sku.name in~ ({{range $index, $v := .ComputeSizes}}{{if $index}},{{end}}'{{$v}}'{{end}}) and properties.osType =~ '{{.OSType}}'" +
26-
"and location =~ '{{.Location}}' " +
26+
"and location in~ ({{range $index, $l := .Locations}}{{if $index}},{{end}}'{{$l}}'{{end}}) " +
2727
"| project skuName=tostring(sku.name),osType=tostring(properties.osType)," +
2828
"location,latestSpotPriceUSD=todouble(properties.spotPrices[0].priceUSD)" +
2929
"| order by latestSpotPriceUSD asc"
3030

3131
queryEvictionRate = "SpotResources | where type =~ 'microsoft.compute/skuspotevictionrate/location' " +
3232
"and sku.name in~ ({{range $index, $v := .ComputeSizes}}{{if $index}},{{end}}'{{$v}}'{{end}})" +
33-
"and location =~ '{{.Location}}' " +
33+
"and location in~ ({{range $index, $l := .Locations}}{{if $index}},{{end}}'{{$l}}'{{end}}) " +
3434
"and tostring(properties.evictionRate) in~ ({{range $index, $e := .AllowedER}}{{if $index}},{{end}}'{{$e}}'{{end}}) " +
3535
"| project skuName=tostring(sku.name),location,spotEvictionRate=tostring(properties.evictionRate) "
3636
)
@@ -94,11 +94,18 @@ func SpotInfo(mCtx *mc.Context, args *SpotInfoArgs) (*spot.SpotResults, error) {
9494
if err != nil {
9595
return nil, err
9696
}
97-
evictionRates, err := hostingPlaces.RunOnHostingPlaces(locations,
98-
evictionRatesArgs{
97+
allEvictionRates, err := checkEvictionRates(locations,
98+
checkEvictionRatesArgs{
9999
computeSizes: args.ComputeSizes,
100100
clientFactory: clientFactory,
101101
allowedER: allowedER(*args.SpotTolerance),
102+
})
103+
if err != nil {
104+
return nil, err
105+
}
106+
evictionRates, err := hostingPlaces.RunOnHostingPlaces(locations,
107+
evictionRatesArgs{
108+
evr: allEvictionRates,
102109
},
103110
evictionRatesAsync)
104111
if err != nil {
@@ -110,11 +117,17 @@ func SpotInfo(mCtx *mc.Context, args *SpotInfoArgs) (*spot.SpotResults, error) {
110117
locations = utilMaps.Keys(evictionRates)
111118
}
112119
// prices
120+
allSpotPricings, err := checkSpotPricing(locations, checkSpotPricingArgs{
121+
computeSizes: args.ComputeSizes,
122+
clientFactory: clientFactory,
123+
osType: args.OSType,
124+
})
125+
if err != nil {
126+
return nil, err
127+
}
113128
spotPricings, err := hostingPlaces.RunOnHostingPlaces(locations,
114129
spotPricingArgs{
115-
computeSizes: args.ComputeSizes,
116-
clientFactory: clientFactory,
117-
osType: args.OSType,
130+
sp: allSpotPricings,
118131
},
119132
spotPricingAsync)
120133
if err != nil {
@@ -205,13 +218,17 @@ func allowedER(spotTolerance spot.Tolerance) []string {
205218
})
206219
}
207220

208-
type evictionRatesArgs struct {
221+
type checkEvictionRatesArgs struct {
209222
clientFactory *armresourcegraph.ClientFactory
210223
computeSizes []string
211224
allowedER []string
212225
// capacity int32
213226
}
214227

228+
type evictionRatesArgs struct {
229+
evr map[string][]evictionRateResult
230+
}
231+
215232
type evictionRateResult struct {
216233
ComputeSize string `json:"skuName"`
217234
Location string `json:"location"`
@@ -220,27 +237,23 @@ type evictionRateResult struct {
220237

221238
type queryERData struct {
222239
ComputeSizes []string
223-
Location string
240+
Locations []string
224241
AllowedER []string
225242
}
226243

227-
// This will get evictionrates grouped on map per region
228-
// only scores over tolerance will be added
229-
func evictionRatesAsync(location string, args evictionRatesArgs, c chan hostingPlaces.HostingPlaceData[[]evictionRateResult]) {
244+
func checkEvictionRates(locations []string, args checkEvictionRatesArgs) (map[string][]evictionRateResult, error) {
230245
tmpl, err := template.New("graphQuery").Parse(queryEvictionRate)
231246
if err != nil {
232-
hostingPlaces.SendAsyncErr(c, err)
233-
return
247+
return nil, err
234248
}
235249
buffer := new(bytes.Buffer)
236250
err = tmpl.Execute(buffer, queryERData{
237251
ComputeSizes: args.computeSizes,
238-
Location: location,
252+
Locations: locations,
239253
AllowedER: args.allowedER,
240254
})
241255
if err != nil {
242-
hostingPlaces.SendAsyncErr(c, err)
243-
return
256+
return nil, err
244257
}
245258
evrr := buffer.String()
246259
qr, err := args.clientFactory.NewClient().Resources(context.Background(),
@@ -249,23 +262,31 @@ func evictionRatesAsync(location string, args evictionRatesArgs, c chan hostingP
249262
},
250263
nil)
251264
if err != nil {
252-
hostingPlaces.SendAsyncErr(c, err)
253-
return
265+
return nil, err
254266
}
255267
var results []evictionRateResult
256268
for _, r := range qr.Data.([]interface{}) {
257269
rJSON, err := json.Marshal(r)
258270
if err != nil {
259-
hostingPlaces.SendAsyncErr(c, err)
260-
return
271+
return nil, err
261272
}
262273
rStruct := evictionRateResult{}
263274
if err := json.Unmarshal(rJSON, &rStruct); err != nil {
264-
hostingPlaces.SendAsyncErr(c, err)
265-
return
275+
return nil, err
266276
}
267277
results = append(results, rStruct)
268278
}
279+
return utilSlices.Split(
280+
results,
281+
func(e evictionRateResult) string {
282+
return e.Location
283+
}), nil
284+
}
285+
286+
// This will get evictionrates grouped on map per region
287+
// only scores over tolerance will be added
288+
func evictionRatesAsync(location string, args evictionRatesArgs, c chan hostingPlaces.HostingPlaceData[[]evictionRateResult]) {
289+
results := args.evr[location]
269290
// Order by eviction rate
270291
slices.SortFunc(results,
271292
func(a, b evictionRateResult) int {
@@ -277,13 +298,17 @@ func evictionRatesAsync(location string, args evictionRatesArgs, c chan hostingP
277298
Value: results}
278299
}
279300

280-
type spotPricingArgs struct {
301+
type checkSpotPricingArgs struct {
281302
clientFactory *armresourcegraph.ClientFactory
282303
computeSizes []string
283304
osType string
284305
// capacity int32
285306
}
286307

308+
type spotPricingArgs struct {
309+
sp map[string][]spotPricingResult
310+
}
311+
287312
type spotPricingResult struct {
288313
ComputeSize string `json:"skuName"`
289314
OSType string `json:"osType"`
@@ -293,26 +318,24 @@ type spotPricingResult struct {
293318

294319
type querySpotPriceData struct {
295320
ComputeSizes []string
296-
Location string
321+
Locations []string
297322
OSType string
298323
}
299324

300325
// This function will return a slice of values with price ordered from minor prices to major
301-
func spotPricingAsync(location string, args spotPricingArgs, c chan hostingPlaces.HostingPlaceData[[]spotPricingResult]) {
326+
func checkSpotPricing(locations []string, args checkSpotPricingArgs) (map[string][]spotPricingResult, error) {
302327
tmpl, err := template.New("graphQuery").Parse(querySpotPrice)
303328
if err != nil {
304-
hostingPlaces.SendAsyncErr(c, err)
305-
return
329+
return nil, err
306330
}
307331
buffer := new(bytes.Buffer)
308332
err = tmpl.Execute(buffer, querySpotPriceData{
309333
ComputeSizes: args.computeSizes,
310-
Location: location,
334+
Locations: locations,
311335
OSType: args.osType,
312336
})
313337
if err != nil {
314-
hostingPlaces.SendAsyncErr(c, err)
315-
return
338+
return nil, err
316339
}
317340
spr := buffer.String()
318341
qr, err := args.clientFactory.NewClient().Resources(context.Background(),
@@ -321,25 +344,32 @@ func spotPricingAsync(location string, args spotPricingArgs, c chan hostingPlace
321344
},
322345
nil)
323346
if err != nil {
324-
hostingPlaces.SendAsyncErr(c, err)
325-
return
347+
return nil, err
326348
}
327349
var results []spotPricingResult
328350
for _, r := range qr.Data.([]interface{}) {
329351
rJSON, err := json.Marshal(r)
330352
if err != nil {
331-
hostingPlaces.SendAsyncErr(c, err)
332-
return
353+
return nil, err
333354
}
334355
rStruct := spotPricingResult{}
335356
if err := json.Unmarshal(rJSON, &rStruct); err != nil {
336-
hostingPlaces.SendAsyncErr(c, err)
337-
return
357+
return nil, err
338358
}
339359
logging.Debugf("Found ComputeSize %s at Location %s with spot price %.2f",
340360
string(rStruct.ComputeSize), rStruct.Location, rStruct.Price)
341361
results = append(results, rStruct)
342362
}
363+
return utilSlices.Split(
364+
results,
365+
func(s spotPricingResult) string {
366+
return s.Location
367+
}), nil
368+
}
369+
370+
// This function will return a slice of values with price ordered from minor prices to major
371+
func spotPricingAsync(location string, args spotPricingArgs, c chan hostingPlaces.HostingPlaceData[[]spotPricingResult]) {
372+
results := args.sp[location]
343373
// Order by price
344374
if len(results) > 0 {
345375
utilSlices.SortbyFloat(results,
@@ -350,7 +380,6 @@ func spotPricingAsync(location string, args spotPricingArgs, c chan hostingPlace
350380
c <- hostingPlaces.HostingPlaceData[[]spotPricingResult]{
351381
Region: location,
352382
Value: results}
353-
354383
}
355384

356385
type spotChoiceArgs struct {

0 commit comments

Comments
 (0)