Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Perf + Resource Utilization: Ensure proper dispose of disposable members #4832

Merged
merged 1 commit into from
Oct 21, 2020
Merged
Show file tree
Hide file tree
Changes from all 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
72 changes: 44 additions & 28 deletions libraries/Microsoft.Bot.Builder/BotFrameworkAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -370,10 +370,17 @@ public override async Task ContinueConversationAsync(ClaimsIdentity claimsIdenti
}
}

var connectorClient = await CreateConnectorClientAsync(reference.ServiceUrl, claimsIdentity, audience).ConfigureAwait(false);
context.TurnState.Add(connectorClient);

await RunPipelineAsync(context, callback, cancellationToken).ConfigureAwait(false);
using (var connectorClient = await CreateConnectorClientAsync(reference.ServiceUrl, claimsIdentity, audience).ConfigureAwait(false))
{
// Make the connector client available in turn state
context.TurnState.Add(connectorClient);

// Run bot pipeline
await RunPipelineAsync(context, callback, cancellationToken).ConfigureAwait(false);

// Cleanup disposable resources in case other code kept a reference to it.
context.TurnState.Set<IConnectorClient>(null);
}
}
}

Expand Down Expand Up @@ -448,12 +455,16 @@ public override async Task<InvokeResponse> ProcessActivityAsync(ClaimsIdentity c
// The OAuthScope is also stored on the TurnState to get the correct AppCredentials if fetching a token is required.
var scope = SkillValidation.IsSkillClaim(claimsIdentity.Claims) ? JwtTokenValidation.GetAppIdFromClaims(claimsIdentity.Claims) : GetBotFrameworkOAuthScope();
context.TurnState.Add(OAuthScopeKey, scope);
var connectorClient = await CreateConnectorClientAsync(activity.ServiceUrl, claimsIdentity, scope).ConfigureAwait(false);
context.TurnState.Add(connectorClient);
using (var connectorClient = await CreateConnectorClientAsync(activity.ServiceUrl, claimsIdentity, scope).ConfigureAwait(false))
{
context.TurnState.Add(connectorClient);
context.TurnState.Add(callback);

context.TurnState.Add(callback);
await RunPipelineAsync(context, callback, cancellationToken).ConfigureAwait(false);

await RunPipelineAsync(context, callback, cancellationToken).ConfigureAwait(false);
// Cleanup disposable resources in case other code kept a reference to it.
context.TurnState.Set<IConnectorClient>(null);
}

// Handle ExpectedReplies scenarios where the all the activities have been buffered and sent back at once
// in an invoke response.
Expand Down Expand Up @@ -1274,30 +1285,35 @@ public virtual Task CreateConversationAsync(string channelId, string serviceUrl,
/// </remarks>
public virtual async Task CreateConversationAsync(string channelId, string serviceUrl, AppCredentials credentials, ConversationParameters conversationParameters, BotCallbackHandler callback, CancellationToken cancellationToken)
{
var connectorClient = CreateConnectorClient(serviceUrl, credentials);
using (var connectorClient = CreateConnectorClient(serviceUrl, credentials))
{
var result = await connectorClient.Conversations.CreateConversationAsync(conversationParameters, cancellationToken).ConfigureAwait(false);

var result = await connectorClient.Conversations.CreateConversationAsync(conversationParameters, cancellationToken).ConfigureAwait(false);
// Create a conversation update activity to represent the result.
var eventActivity = Activity.CreateEventActivity();
eventActivity.Name = ActivityEventNames.CreateConversation;
eventActivity.ChannelId = channelId;
eventActivity.ServiceUrl = serviceUrl;
eventActivity.Id = result.ActivityId ?? Guid.NewGuid().ToString("n");
eventActivity.Conversation = new ConversationAccount(id: result.Id, tenantId: conversationParameters.TenantId);
eventActivity.ChannelData = conversationParameters.ChannelData;
eventActivity.Recipient = conversationParameters.Bot;

// Create a conversation update activity to represent the result.
var eventActivity = Activity.CreateEventActivity();
eventActivity.Name = ActivityEventNames.CreateConversation;
eventActivity.ChannelId = channelId;
eventActivity.ServiceUrl = serviceUrl;
eventActivity.Id = result.ActivityId ?? Guid.NewGuid().ToString("n");
eventActivity.Conversation = new ConversationAccount(id: result.Id, tenantId: conversationParameters.TenantId);
eventActivity.ChannelData = conversationParameters.ChannelData;
eventActivity.Recipient = conversationParameters.Bot;
using (var context = new TurnContext(this, (Activity)eventActivity))
{
var claimsIdentity = new ClaimsIdentity();
claimsIdentity.AddClaim(new Claim(AuthenticationConstants.AudienceClaim, credentials.MicrosoftAppId));
claimsIdentity.AddClaim(new Claim(AuthenticationConstants.AppIdClaim, credentials.MicrosoftAppId));
claimsIdentity.AddClaim(new Claim(AuthenticationConstants.ServiceUrlClaim, serviceUrl));

using (var context = new TurnContext(this, (Activity)eventActivity))
{
var claimsIdentity = new ClaimsIdentity();
claimsIdentity.AddClaim(new Claim(AuthenticationConstants.AudienceClaim, credentials.MicrosoftAppId));
claimsIdentity.AddClaim(new Claim(AuthenticationConstants.AppIdClaim, credentials.MicrosoftAppId));
claimsIdentity.AddClaim(new Claim(AuthenticationConstants.ServiceUrlClaim, serviceUrl));
context.TurnState.Add<IIdentity>(BotIdentityKey, claimsIdentity);
context.TurnState.Add(connectorClient);

context.TurnState.Add<IIdentity>(BotIdentityKey, claimsIdentity);
context.TurnState.Add(connectorClient);
await RunPipelineAsync(context, callback, cancellationToken).ConfigureAwait(false);
await RunPipelineAsync(context, callback, cancellationToken).ConfigureAwait(false);

// Cleanup disposable resources in case other code kept a reference to it.
context.TurnState.Set<IConnectorClient>(null);
}
}
}

Expand Down
20 changes: 18 additions & 2 deletions libraries/Microsoft.Bot.Builder/Inspection/InspectionSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ internal class InspectionSession : IDisposable
private readonly ConversationReference _conversationReference;
private readonly ILogger _logger;

// To detect redundant calls to Dispose()
private bool _disposed = false;

public InspectionSession(ConversationReference conversationReference, MicrosoftAppCredentials credentials, HttpClient httpClient, ILogger logger)
{
_conversationReference = conversationReference;
Expand All @@ -44,9 +47,22 @@ public async Task<bool> SendAsync(Activity activity, CancellationToken cancellat
return true;
}

public void Dispose()
public void Dispose() => Dispose(true);

protected virtual void Dispose(bool disposing)
{
_connectorClient?.Dispose();
if (_disposed)
{
return;
}

if (disposing)
{
// Dispose managed state (managed objects).
_connectorClient?.Dispose();
}

_disposed = true;
}
}
}