Skip to content

Commit ded76d0

Browse files
committed
OTel Benchmarker
1 parent afbc7b2 commit ded76d0

File tree

12 files changed

+1053
-143
lines changed

12 files changed

+1053
-143
lines changed

changelog.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
vNext
22
----------
3+
- [MINOR] Add OTel Benchmarker (#2786)
34
- [MAJOR] Add KeyStoreBackedSecretKeyProvider (#2674)
45
- [MINOR] Add Open Id configuration issuer validation reporting in OpenIdProviderConfigurationClient (#2751)
56
- [MINOR] Add helper method to record elapsed time (#2768)

common4j/build.gradle

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ def nativeAuthConfigFilePathParameter = "" // will be blank unless specified by
166166
def nativeAuthConfigStringParameter = "" // will be blank unless specified by developer, used to run e2e tests in CI
167167
def disableAcquireTokenSilentTimeoutParameter = false // will be false unless specified by developer
168168
def allowOneboxAuthorities = false // will be false unless specified by developer
169+
def shouldSkipSilentTokenCommandCacheForStressTest = false // will be false unless specified by developer, required for running concurrent stress test in MSAL/Broker.
169170

170171
if (project.hasProperty("slice")) {
171172
sliceParameter = slice
@@ -205,6 +206,10 @@ if (project.hasProperty("allowOneboxAuthorities")) {
205206
allowOneboxAuthorities = true
206207
}
207208

209+
if (project.hasProperty("shouldSkipSilentTokenCommandCacheForStressTest")) {
210+
shouldSkipSilentTokenCommandCacheForStressTest = true
211+
}
212+
208213
sourceSets {
209214
main {
210215
java.srcDirs = ['src/main', "$project.buildDir/generated/source/buildConfig/main"]
@@ -216,6 +221,7 @@ sourceSets {
216221
buildConfigField("String", "NATIVE_AUTH_CONFIG_STRING", "\"$nativeAuthConfigStringParameter\"")
217222
buildConfigField("boolean", "DISABLE_ACQUIRE_TOKEN_SILENT_TIMEOUT", "${disableAcquireTokenSilentTimeoutParameter}")
218223
buildConfigField("boolean", "ALLOW_ONEBOX_AUTHORITIES", "${allowOneboxAuthorities}")
224+
buildConfigField("boolean", "SHOULD_SKIP_SILENT_TOKEN_COMMAND_CACHE_FOR_STRESS_TEST", "${shouldSkipSilentTokenCommandCacheForStressTest}")
219225
}
220226
test {
221227
java.srcDirs = ['src/test']

common4j/src/main/com/microsoft/identity/common/java/commands/SilentTokenCommand.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
// THE SOFTWARE.
2323
package com.microsoft.identity.common.java.commands;
2424

25+
import com.microsoft.identity.common.java.BuildConfig;
2526
import com.microsoft.identity.common.java.WarningType;
2627
import com.microsoft.identity.common.java.commands.parameters.SilentTokenCommandParameters;
2728
import com.microsoft.identity.common.java.constants.OAuth2ErrorCode;
@@ -150,6 +151,11 @@ public AcquireTokenResult execute() throws Exception {
150151

151152
@Override
152153
public boolean isEligibleForCaching() {
154+
if (BuildConfig.SHOULD_SKIP_SILENT_TOKEN_COMMAND_CACHE_FOR_STRESS_TEST) {
155+
// by disabling this, MSAL/Broker will allow similar request to be executed
156+
// as opposed to being handled by cache.
157+
return false;
158+
}
153159
return true;
154160
}
155161

common4j/src/main/com/microsoft/identity/common/java/controllers/CommandDispatcher.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@
8989
import java.util.concurrent.ExecutionException;
9090
import java.util.concurrent.ExecutorService;
9191
import java.util.concurrent.Executors;
92+
import java.util.concurrent.ThreadPoolExecutor;
9293
import java.util.concurrent.TimeUnit;
9394
import java.util.concurrent.TimeoutException;
9495

@@ -114,6 +115,14 @@ public class CommandDispatcher {
114115
@SuppressWarnings(WarningType.rawtype_warning)
115116
private static ConcurrentMap<BaseCommand, FinalizableResultFuture<CommandResult>> sExecutingCommandMap = new ConcurrentHashMap<>();
116117

118+
/**
119+
* Returns the approximate number of threads that are actively
120+
* executing tasks in the silent request thread pool.
121+
*/
122+
public static int getSilentRequestActiveCount(){
123+
return ((ThreadPoolExecutor)sSilentExecutor).getActiveCount();
124+
}
125+
117126
/**
118127
* Remove all keys that are the command reference from the executing command map. Since if they key has
119128
* been changed, remove will not work, construct a new map and add all keys that are not identically
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// All rights reserved.
3+
//
4+
// This code is licensed under the MIT License.
5+
//
6+
// Permission is hereby granted, free of charge, to any person obtaining a copy
7+
// of this software and associated documentation files(the "Software"), to deal
8+
// in the Software without restriction, including without limitation the rights
9+
// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
10+
// copies of the Software, and to permit persons to whom the Software is
11+
// furnished to do so, subject to the following conditions :
12+
//
13+
// The above copyright notice and this permission notice shall be included in
14+
// all copies or substantial portions of the Software.
15+
//
16+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22+
// THE SOFTWARE.
23+
package com.microsoft.identity.common.java.opentelemetry
24+
25+
import com.microsoft.identity.common.java.controllers.CommandDispatcher
26+
import io.opentelemetry.api.common.AttributeKey
27+
import io.opentelemetry.api.common.Attributes
28+
import io.opentelemetry.api.trace.Span
29+
import io.opentelemetry.api.trace.SpanContext
30+
import io.opentelemetry.api.trace.StatusCode
31+
import java.util.concurrent.TimeUnit
32+
33+
interface IBenchmarkSpan {
34+
/**
35+
* Returns a list of status changes along with their timestamps in nanoseconds.
36+
**/
37+
fun getStatuses(): List<Pair<String, Long>>
38+
39+
/**
40+
* Returns the span name.
41+
**/
42+
fun getSpanName(): String
43+
44+
/**
45+
* The start time of the span in nanoseconds.
46+
**/
47+
fun getStartTimeInNanoSeconds(): Long
48+
49+
/**
50+
* The end time of the span in nanoseconds.
51+
**/
52+
fun getEndTimeInNanoSeconds(): Long
53+
54+
/**
55+
* # of concurrent active silent requests when this span is started.
56+
**/
57+
fun getConcurrentSilentRequestSize(): Int
58+
59+
/**
60+
* The exception recorded on this span, if any.
61+
**/
62+
fun getException(): Throwable?
63+
}
64+
65+
/**
66+
* A span wrapper class for benchmarking purposes.
67+
*
68+
* @param originalSpan The original span to be wrapped.
69+
* @param printer The printer to print the benchmark results.
70+
* @param spanName The name of the span.
71+
**/
72+
class BenchmarkSpan(
73+
val originalSpan: Span,
74+
val printer: IBenchmarkSpanPrinter,
75+
private val spanName: String) : Span, IBenchmarkSpan {
76+
77+
// Pair of (status name, timestamp in nano seconds)
78+
val statuses : ArrayList<Pair<String, Long>> = arrayListOf()
79+
private var exception: Throwable? = null
80+
81+
private var startTimeInNanoSeconds: Long = System.nanoTime()
82+
private var endTimeInNanoSeconds: Long = 0L
83+
84+
// # of concurrent active silent requests when this span is started.
85+
private var concurrentSize = 1
86+
87+
override fun getStatuses(): List<Pair<String, Long>> {
88+
return statuses
89+
}
90+
91+
override fun getSpanName(): String {
92+
return spanName
93+
}
94+
95+
override fun getStartTimeInNanoSeconds(): Long {
96+
return startTimeInNanoSeconds
97+
}
98+
99+
override fun getEndTimeInNanoSeconds(): Long {
100+
return endTimeInNanoSeconds
101+
}
102+
103+
override fun getConcurrentSilentRequestSize(): Int {
104+
return concurrentSize
105+
}
106+
107+
override fun getException(): Throwable? {
108+
return exception
109+
}
110+
111+
fun start(){
112+
startTimeInNanoSeconds = System.nanoTime()
113+
concurrentSize = CommandDispatcher.getSilentRequestActiveCount()
114+
}
115+
116+
override fun end() {
117+
endTimeInNanoSeconds = System.nanoTime()
118+
printer.printAsync(this)
119+
return originalSpan.end()
120+
}
121+
122+
override fun end(timestamp: Long, unit: TimeUnit) {
123+
endTimeInNanoSeconds = System.nanoTime()
124+
printer.printAsync(this)
125+
return originalSpan.end(timestamp, unit)
126+
}
127+
128+
override fun <T : Any?> setAttribute(
129+
key: AttributeKey<T>,
130+
value: T
131+
): Span? {
132+
statuses.add(Pair(key.toString(), System.nanoTime()))
133+
return originalSpan.setAttribute(key, value)
134+
}
135+
136+
override fun addEvent(
137+
name: String,
138+
attributes: Attributes
139+
): Span? {
140+
statuses.add(Pair(name, System.nanoTime()))
141+
return originalSpan.addEvent(name, attributes)
142+
}
143+
144+
override fun addEvent(
145+
name: String,
146+
attributes: Attributes,
147+
timestamp: Long,
148+
unit: TimeUnit
149+
): Span? {
150+
statuses.add(Pair(name, System.nanoTime()))
151+
return originalSpan.addEvent(name, attributes, timestamp, unit)
152+
}
153+
154+
override fun setStatus(
155+
statusCode: StatusCode,
156+
description: String
157+
): Span? {
158+
statuses.add(Pair("SetStatus:$statusCode", System.nanoTime()))
159+
return originalSpan.setStatus(statusCode, description)
160+
}
161+
162+
override fun recordException(
163+
exception: Throwable,
164+
additionalAttributes: Attributes
165+
): Span? {
166+
val timestamp = System.nanoTime()
167+
statuses.add(Pair("recordException", timestamp))
168+
this.exception = exception
169+
return originalSpan.recordException(exception, additionalAttributes)
170+
}
171+
172+
override fun updateName(name: String): Span? {
173+
return originalSpan.updateName(name)
174+
}
175+
176+
override fun getSpanContext(): SpanContext? {
177+
return originalSpan.spanContext
178+
}
179+
180+
override fun isRecording(): Boolean {
181+
return originalSpan.isRecording
182+
}
183+
}

0 commit comments

Comments
 (0)