Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
vNext
----------
- [MINOR] Add OTel Benchmarker (#2786)
- [MAJOR] Add KeyStoreBackedSecretKeyProvider (#2674)
- [MINOR] Add Open Id configuration issuer validation reporting in OpenIdProviderConfigurationClient (#2751)
- [MINOR] Add helper method to record elapsed time (#2768)
Expand Down
6 changes: 6 additions & 0 deletions common4j/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ def nativeAuthConfigFilePathParameter = "" // will be blank unless specified by
def nativeAuthConfigStringParameter = "" // will be blank unless specified by developer, used to run e2e tests in CI
def disableAcquireTokenSilentTimeoutParameter = false // will be false unless specified by developer
def allowOneboxAuthorities = false // will be false unless specified by developer
def shouldSkipSilentTokenCommandCacheForStressTest = false // will be false unless specified by developer, required for running concurrent stress test in MSAL/Broker.

if (project.hasProperty("slice")) {
sliceParameter = slice
Expand Down Expand Up @@ -205,6 +206,10 @@ if (project.hasProperty("allowOneboxAuthorities")) {
allowOneboxAuthorities = true
}

if (project.hasProperty("shouldSkipSilentTokenCommandCacheForStressTest")) {
shouldSkipSilentTokenCommandCacheForStressTest = true
}

sourceSets {
main {
java.srcDirs = ['src/main', "$project.buildDir/generated/source/buildConfig/main"]
Expand All @@ -216,6 +221,7 @@ sourceSets {
buildConfigField("String", "NATIVE_AUTH_CONFIG_STRING", "\"$nativeAuthConfigStringParameter\"")
buildConfigField("boolean", "DISABLE_ACQUIRE_TOKEN_SILENT_TIMEOUT", "${disableAcquireTokenSilentTimeoutParameter}")
buildConfigField("boolean", "ALLOW_ONEBOX_AUTHORITIES", "${allowOneboxAuthorities}")
buildConfigField("boolean", "SHOULD_SKIP_SILENT_TOKEN_COMMAND_CACHE_FOR_STRESS_TEST", "${shouldSkipSilentTokenCommandCacheForStressTest}")
}
test {
java.srcDirs = ['src/test']
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
// THE SOFTWARE.
package com.microsoft.identity.common.java.commands;

import com.microsoft.identity.common.java.BuildConfig;
import com.microsoft.identity.common.java.WarningType;
import com.microsoft.identity.common.java.commands.parameters.SilentTokenCommandParameters;
import com.microsoft.identity.common.java.constants.OAuth2ErrorCode;
Expand Down Expand Up @@ -150,6 +151,11 @@ public AcquireTokenResult execute() throws Exception {

@Override
public boolean isEligibleForCaching() {
if (BuildConfig.SHOULD_SKIP_SILENT_TOKEN_COMMAND_CACHE_FOR_STRESS_TEST) {
// by disabling this, MSAL/Broker will allow similar request to be executed
// as opposed to being handled by cache.
return false;
}
return true;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

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

/**
* Returns the approximate number of threads that are actively
* executing tasks in the silent request thread pool.
*/
public static int getSilentRequestActiveCount(){
return ((ThreadPoolExecutor)sSilentExecutor).getActiveCount();
}

/**
* Remove all keys that are the command reference from the executing command map. Since if they key has
* been changed, remove will not work, construct a new map and add all keys that are not identically
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
// Copyright (c) Microsoft Corporation.
// All rights reserved.
//
// This code is licensed under the MIT License.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files(the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions :
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
package com.microsoft.identity.common.java.opentelemetry

import com.microsoft.identity.common.java.controllers.CommandDispatcher
import io.opentelemetry.api.common.AttributeKey
import io.opentelemetry.api.common.Attributes
import io.opentelemetry.api.trace.Span
import io.opentelemetry.api.trace.SpanContext
import io.opentelemetry.api.trace.StatusCode
import java.util.concurrent.TimeUnit

interface IBenchmarkSpan {
/**
* Returns a list of status changes along with their timestamps in nanoseconds.
**/
fun getStatuses(): List<Pair<String, Long>>

/**
* Returns the span name.
**/
fun getSpanName(): String

/**
* The start time of the span in nanoseconds.
**/
fun getStartTimeInNanoSeconds(): Long

/**
* The end time of the span in nanoseconds.
**/
fun getEndTimeInNanoSeconds(): Long

/**
* # of concurrent active silent requests when this span is started.
**/
fun getConcurrentSilentRequestSize(): Int

/**
* The exception recorded on this span, if any.
**/
fun getException(): Throwable?
}

/**
* A span wrapper class for benchmarking purposes.
*
* @param originalSpan The original span to be wrapped.
* @param printer The printer to print the benchmark results.
* @param spanName The name of the span.
**/
class BenchmarkSpan(
val originalSpan: Span,
val printer: IBenchmarkSpanPrinter,
private val spanName: String) : Span, IBenchmarkSpan {

// Pair of (status name, timestamp in nano seconds)
val statuses : ArrayList<Pair<String, Long>> = arrayListOf()
private var exception: Throwable? = null

private var startTimeInNanoSeconds: Long = System.nanoTime()
private var endTimeInNanoSeconds: Long = 0L

// # of concurrent active silent requests when this span is started.
private var concurrentSize = 1

override fun getStatuses(): List<Pair<String, Long>> {
return statuses
}

override fun getSpanName(): String {
return spanName
}

override fun getStartTimeInNanoSeconds(): Long {
return startTimeInNanoSeconds
}

override fun getEndTimeInNanoSeconds(): Long {
return endTimeInNanoSeconds
}

override fun getConcurrentSilentRequestSize(): Int {
return concurrentSize
}

override fun getException(): Throwable? {
return exception
}

fun start(){
startTimeInNanoSeconds = System.nanoTime()
concurrentSize = CommandDispatcher.getSilentRequestActiveCount()
}

override fun end() {
endTimeInNanoSeconds = System.nanoTime()
printer.printAsync(this)
return originalSpan.end()
}

override fun end(timestamp: Long, unit: TimeUnit) {
endTimeInNanoSeconds = System.nanoTime()
printer.printAsync(this)
return originalSpan.end(timestamp, unit)
}

override fun <T : Any?> setAttribute(
key: AttributeKey<T>,
value: T
): Span? {
statuses.add(Pair(key.toString(), System.nanoTime()))
return originalSpan.setAttribute(key, value)
}

override fun addEvent(
name: String,
attributes: Attributes
): Span? {
statuses.add(Pair(name, System.nanoTime()))
return originalSpan.addEvent(name, attributes)
}

override fun addEvent(
name: String,
attributes: Attributes,
timestamp: Long,
unit: TimeUnit
): Span? {
statuses.add(Pair(name, System.nanoTime()))
return originalSpan.addEvent(name, attributes, timestamp, unit)
}

override fun setStatus(
statusCode: StatusCode,
description: String
): Span? {
statuses.add(Pair("SetStatus:$statusCode", System.nanoTime()))
return originalSpan.setStatus(statusCode, description)
}

override fun recordException(
exception: Throwable,
additionalAttributes: Attributes
): Span? {
val timestamp = System.nanoTime()
statuses.add(Pair("recordException", timestamp))
this.exception = exception
return originalSpan.recordException(exception, additionalAttributes)
}

override fun updateName(name: String): Span? {
return originalSpan.updateName(name)
}

override fun getSpanContext(): SpanContext? {
return originalSpan.spanContext
}

override fun isRecording(): Boolean {
return originalSpan.isRecording
}
}
Loading