1// Copyright 2018 Istio Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package perftests
16
17import (
18	"testing"
19
20	istio_mixer_v1 "istio.io/api/mixer/v1"
21	"istio.io/istio/mixer/pkg/perf"
22	spyadapter "istio.io/istio/mixer/test/spyAdapter"
23)
24
25// Tests single quota call into Mixer that dispatches instances to multiple noop inproc adapters.
26func Benchmark_Quota_1Client_1Call(b *testing.B) {
27	settings, spyAdapter := settingsWithAdapterAndTmpls()
28	settings.RunMode = perf.InProcess
29
30	setup := perf.Setup{
31		Config: perf.Config{
32			Global:  mixerGlobalCfg,
33			Service: quotaInstToSpyAdapter + attrGenToSpyAdapter,
34		},
35
36		Loads: []perf.Load{{
37			Multiplier: 1,
38			Requests: []perf.Request{
39				perf.BuildBasicCheck(
40					baseAttr,
41					map[string]istio_mixer_v1.CheckRequest_QuotaParams{
42						"requestcount": {Amount: 1},
43					}),
44			},
45		}},
46	}
47
48	perf.Run(b, &setup, settings)
49	validateQuotaBehavior(spyAdapter, b)
50}
51
52// Tests 5 synchronous identical quota call into Mixer that dispatches instances to multiple noop inproc adapters.
53func Benchmark_Quota_1Client_5SameCalls(b *testing.B) {
54	settings, spyAdapter := settingsWithAdapterAndTmpls()
55	settings.RunMode = perf.InProcess
56
57	setup := perf.Setup{
58		Config: perf.Config{
59			Global:  mixerGlobalCfg,
60			Service: quotaInstToSpyAdapter + attrGenToSpyAdapter,
61		},
62
63		Loads: []perf.Load{{
64			Multiplier: 5,
65			Requests: []perf.Request{
66				perf.BuildBasicCheck(
67					baseAttr,
68					map[string]istio_mixer_v1.CheckRequest_QuotaParams{
69						"requestcount": {Amount: 1},
70					}),
71			},
72		}},
73	}
74
75	perf.Run(b, &setup, settings)
76	validateQuotaBehavior(spyAdapter, b)
77}
78
79// Tests 5 synchronous different quota call into Mixer that dispatches instances to multiple noop inproc adapters.
80func Benchmark_Quota_1Client_5DifferentCalls(b *testing.B) {
81	settings, spyAdapter := settingsWithAdapterAndTmpls()
82	settings.RunMode = perf.InProcess
83
84	setup := perf.Setup{
85		Config: perf.Config{
86			Global:  mixerGlobalCfg,
87			Service: quotaInstToSpyAdapter + attrGenToSpyAdapter,
88		},
89
90		Loads: []perf.Load{
91			{
92				Multiplier: 1,
93				Requests: []perf.Request{
94					perf.BuildBasicCheck(
95						attr1,
96						map[string]istio_mixer_v1.CheckRequest_QuotaParams{
97							"requestcount": {Amount: 1},
98						}),
99					perf.BuildBasicCheck(
100						attr2,
101						map[string]istio_mixer_v1.CheckRequest_QuotaParams{
102							"requestcount": {Amount: 1},
103						}),
104					perf.BuildBasicCheck(
105						attr3,
106						map[string]istio_mixer_v1.CheckRequest_QuotaParams{
107							"requestcount": {Amount: 1},
108						}),
109					perf.BuildBasicCheck(
110						attr4,
111						map[string]istio_mixer_v1.CheckRequest_QuotaParams{
112							"requestcount": {Amount: 1},
113						}),
114					perf.BuildBasicCheck(
115						attr5,
116						map[string]istio_mixer_v1.CheckRequest_QuotaParams{
117							"requestcount": {Amount: 1},
118						}),
119				},
120			},
121		},
122	}
123
124	perf.Run(b, &setup, settings)
125	validateQuotaBehavior(spyAdapter, b)
126}
127
128// Tests 4 async client, each sending 5 identical quota call into Mixer that dispatches instances to
129// multiple noop inproc adapters.
130func Benchmark_Quota_4Clients_5SameCallsEach(b *testing.B) {
131	settings, spyAdapter := settingsWithAdapterAndTmpls()
132	settings.RunMode = perf.InProcess
133	setup := perf.Setup{
134		Config: perf.Config{
135			Global:  mixerGlobalCfg,
136			Service: quotaInstToSpyAdapter + attrGenToSpyAdapter,
137		},
138
139		Loads: []perf.Load{
140			{
141				Multiplier: 5,
142				Requests: []perf.Request{
143					perf.BuildBasicCheck(
144						baseAttr,
145						map[string]istio_mixer_v1.CheckRequest_QuotaParams{
146							"requestcount": {Amount: 1},
147						}),
148				},
149			},
150			{
151				Multiplier: 5,
152				Requests: []perf.Request{
153					perf.BuildBasicCheck(
154						baseAttr,
155						map[string]istio_mixer_v1.CheckRequest_QuotaParams{
156							"requestcount": {Amount: 1},
157						}),
158				},
159			},
160			{
161				Multiplier: 5,
162				Requests: []perf.Request{
163					perf.BuildBasicCheck(
164						baseAttr,
165						map[string]istio_mixer_v1.CheckRequest_QuotaParams{
166							"requestcount": {Amount: 1},
167						}),
168				},
169			},
170			{
171				Multiplier: 5,
172				Requests: []perf.Request{
173					perf.BuildBasicCheck(
174						baseAttr,
175						map[string]istio_mixer_v1.CheckRequest_QuotaParams{
176							"requestcount": {Amount: 1},
177						}),
178				},
179			},
180		},
181	}
182
183	perf.Run(b, &setup, settings)
184	validateQuotaBehavior(spyAdapter, b)
185}
186
187// Tests 4 async client, each sending 5 different quota call into Mixer that dispatches instances to
188// multiple noop inproc adapters.
189func Benchmark_Quota_4Clients_5DifferentCallsEach(b *testing.B) {
190	settings, spyAdapter := settingsWithAdapterAndTmpls()
191	settings.RunMode = perf.InProcess
192	setup := perf.Setup{
193		Config: perf.Config{
194			Global:  mixerGlobalCfg,
195			Service: quotaInstToSpyAdapter + attrGenToSpyAdapter,
196		},
197
198		Loads: []perf.Load{
199			{
200				Multiplier: 1,
201				Requests: []perf.Request{
202					perf.BuildBasicCheck(
203						attr1,
204						map[string]istio_mixer_v1.CheckRequest_QuotaParams{
205							"requestcount": {Amount: 1},
206						}),
207					perf.BuildBasicCheck(
208						attr2,
209						map[string]istio_mixer_v1.CheckRequest_QuotaParams{
210							"requestcount": {Amount: 1},
211						}),
212					perf.BuildBasicCheck(
213						attr3,
214						map[string]istio_mixer_v1.CheckRequest_QuotaParams{
215							"requestcount": {Amount: 1},
216						}),
217					perf.BuildBasicCheck(
218						attr4,
219						map[string]istio_mixer_v1.CheckRequest_QuotaParams{
220							"requestcount": {Amount: 1},
221						}),
222					perf.BuildBasicCheck(
223						attr5,
224						map[string]istio_mixer_v1.CheckRequest_QuotaParams{
225							"requestcount": {Amount: 1},
226						}),
227				},
228			},
229			{
230				Multiplier: 1,
231				Requests: []perf.Request{
232					perf.BuildBasicCheck(
233						attr1,
234						map[string]istio_mixer_v1.CheckRequest_QuotaParams{
235							"requestcount": {Amount: 1},
236						}),
237					perf.BuildBasicCheck(
238						attr2,
239						map[string]istio_mixer_v1.CheckRequest_QuotaParams{
240							"requestcount": {Amount: 1},
241						}),
242					perf.BuildBasicCheck(
243						attr3,
244						map[string]istio_mixer_v1.CheckRequest_QuotaParams{
245							"requestcount": {Amount: 1},
246						}),
247					perf.BuildBasicCheck(
248						attr4,
249						map[string]istio_mixer_v1.CheckRequest_QuotaParams{
250							"requestcount": {Amount: 1},
251						}),
252					perf.BuildBasicCheck(
253						attr5,
254						map[string]istio_mixer_v1.CheckRequest_QuotaParams{
255							"requestcount": {Amount: 1},
256						}),
257				},
258			},
259			{
260				Multiplier: 1,
261				Requests: []perf.Request{
262					perf.BuildBasicCheck(
263						attr1,
264						map[string]istio_mixer_v1.CheckRequest_QuotaParams{
265							"requestcount": {Amount: 1},
266						}),
267					perf.BuildBasicCheck(
268						attr2,
269						map[string]istio_mixer_v1.CheckRequest_QuotaParams{
270							"requestcount": {Amount: 1},
271						}),
272					perf.BuildBasicCheck(
273						attr3,
274						map[string]istio_mixer_v1.CheckRequest_QuotaParams{
275							"requestcount": {Amount: 1},
276						}),
277					perf.BuildBasicCheck(
278						attr4,
279						map[string]istio_mixer_v1.CheckRequest_QuotaParams{
280							"requestcount": {Amount: 1},
281						}),
282					perf.BuildBasicCheck(
283						attr5,
284						map[string]istio_mixer_v1.CheckRequest_QuotaParams{
285							"requestcount": {Amount: 1},
286						}),
287				},
288			},
289			{
290				Multiplier: 1,
291				Requests: []perf.Request{
292					perf.BuildBasicCheck(
293						attr1,
294						map[string]istio_mixer_v1.CheckRequest_QuotaParams{
295							"requestcount": {Amount: 1},
296						}),
297					perf.BuildBasicCheck(
298						attr2,
299						map[string]istio_mixer_v1.CheckRequest_QuotaParams{
300							"requestcount": {Amount: 1},
301						}),
302					perf.BuildBasicCheck(
303						attr3,
304						map[string]istio_mixer_v1.CheckRequest_QuotaParams{
305							"requestcount": {Amount: 1},
306						}),
307					perf.BuildBasicCheck(
308						attr4,
309						map[string]istio_mixer_v1.CheckRequest_QuotaParams{
310							"requestcount": {Amount: 1},
311						}),
312					perf.BuildBasicCheck(
313						attr5,
314						map[string]istio_mixer_v1.CheckRequest_QuotaParams{
315							"requestcount": {Amount: 1},
316						}),
317				},
318			},
319		},
320	}
321
322	perf.Run(b, &setup, settings)
323	validateQuotaBehavior(spyAdapter, b)
324}
325
326// Tests 4 async client, each sending 5 identical quota call into Mixer that dispatches instances to
327// multiple noop inproc adapters. The APA in this case is a slow by 1ms.
328func Benchmark_Quota_4Clients_5SameCallsEach_1MilliSecSlowApa(b *testing.B) {
329	settings, spyAdapter := settingsWith1milliSecApaAdapterAndTmpls()
330	settings.RunMode = perf.InProcess
331	setup := perf.Setup{
332		Config: perf.Config{
333			Global:  mixerGlobalCfg,
334			Service: quotaInstToSpyAdapter + attrGenToSpyAdapter,
335		},
336
337		Loads: []perf.Load{
338			{
339				Multiplier: 5,
340				Requests: []perf.Request{
341					perf.BuildBasicCheck(
342						baseAttr,
343						map[string]istio_mixer_v1.CheckRequest_QuotaParams{
344							"requestcount": {Amount: 1},
345						}),
346				},
347			},
348			{
349				Multiplier: 5,
350				Requests: []perf.Request{
351					perf.BuildBasicCheck(
352						baseAttr,
353						map[string]istio_mixer_v1.CheckRequest_QuotaParams{
354							"requestcount": {Amount: 1},
355						}),
356				},
357			},
358			{
359				Multiplier: 5,
360				Requests: []perf.Request{
361					perf.BuildBasicCheck(
362						baseAttr,
363						map[string]istio_mixer_v1.CheckRequest_QuotaParams{
364							"requestcount": {Amount: 1},
365						}),
366				},
367			},
368			{
369				Multiplier: 5,
370				Requests: []perf.Request{
371					perf.BuildBasicCheck(
372						baseAttr,
373						map[string]istio_mixer_v1.CheckRequest_QuotaParams{
374							"requestcount": {Amount: 1},
375						}),
376				},
377			},
378		},
379	}
380
381	perf.Run(b, &setup, settings)
382	validateQuotaBehavior(spyAdapter, b)
383}
384
385func validateQuotaBehavior(spyAdapter *spyadapter.Adapter, b *testing.B) {
386	// validate all went as expected.
387	//
388	// based on the config, there must be, for each quota check call from client,
389	// * single attribute generation call
390	// * single quota check call
391	foundAttrGenCall := false
392	foundQuotaCall := false
393	for _, cc := range spyAdapter.HandlerData.CapturedCalls {
394		if cc.Name == "HandleSampleApaAttributes" && len(cc.Instances) == 1 {
395			foundAttrGenCall = true
396		}
397		if cc.Name == "HandleSampleQuota" && len(cc.Instances) == 1 {
398			foundQuotaCall = true
399		}
400	}
401
402	if !foundAttrGenCall || !foundQuotaCall {
403		b.Errorf("got spy adapter calls %v; want calls  with HandleSampleApaAttributes:1 & HandleSampleQuota:1",
404			spyAdapter.HandlerData.CapturedCalls)
405	}
406}
407
408const (
409	// contains 1 rules that pass 1 instance to a quota adapter
410	quotaInstToSpyAdapter = `
411apiVersion: "config.istio.io/v1alpha2"
412kind: spyadapter
413metadata:
414  name: spyadapterHandler
415  namespace: istio-system
416spec:
417---
418apiVersion: "config.istio.io/v1alpha2"
419kind: samplequota
420metadata:
421  name: requestcount
422  namespace: istio-system
423spec:
424  dimensions:
425    source: source.labels["app"] | source.service | "unknown"
426    sourceVersion: source.labels["version"] | "unknown"
427    destination: destination.labels["app"] | destination.service | "unknown"
428    destinationVersion: destination.labels["version"] | "unknown"
429---
430apiVersion: "config.istio.io/v1alpha2"
431kind: rule
432metadata:
433  name: quota
434  namespace: istio-system
435spec:
436  actions:
437  - handler: spyadapterHandler.spyadapter
438    instances:
439    - requestcount.samplequota
440---
441`
442)
443