Files
sqltoolsservice/src/Microsoft.SqlTools.ServiceLayer/Connection/ReliableConnection/CachedServerInfo.cs
Benjamin Russell 41198e9357 Adding sr.strings file and removing hard-coded strings (#52)
* Strings sweep for connection service

* String sweep for credentials service

* String sweep for hosting

* String sweep for query execution service

* String sweep for Workspace service

* Renaming utility namespace to match standards

Renaming Microsoft.SqlTools.EditorServices.Utility to
Microsoft.SqlTools.ServiceLayer.Utility to match the naming changes done a
while back. Also renaming them on the files that use them

* Namespace change on reliable connection

* Adding the new resx and designer files

* Final bug fixes for srgen

Fixing flakey moq package name

* Removing todo as per @kevcunnane

* Adding using statements as per @llali's comment

* Fixing issues from broken unit tests

Note: This feature contains changes that will break the contract for
saving as CSV and JSON. On success, null is returned as a message instead
of "Success". Changes will be made to the vscode component to handle this
change.
2016-09-16 16:18:25 -07:00

138 lines
4.6 KiB
C#

//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System;
using System.Collections.Concurrent;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using Microsoft.SqlTools.ServiceLayer.Utility;
namespace Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection
{
/// <summary>
/// This class caches server information for subsequent use
/// </summary>
internal static class CachedServerInfo
{
private struct CachedInfo
{
public bool IsAzure;
public DateTime LastUpdate;
}
private static ConcurrentDictionary<string, CachedInfo> _cache;
private static object _cacheLock;
private const int _maxCacheSize = 1024;
private const int _deleteBatchSize = 512;
private const int MinimalQueryTimeoutSecondsForAzure = 300;
static CachedServerInfo()
{
_cache = new ConcurrentDictionary<string, CachedInfo>(StringComparer.OrdinalIgnoreCase);
_cacheLock = new object();
}
public static int GetQueryTimeoutSeconds(IDbConnection connection)
{
string dataSource = SafeGetDataSourceFromConnection(connection);
return GetQueryTimeoutSeconds(dataSource);
}
public static int GetQueryTimeoutSeconds(string dataSource)
{
//keep existing behavior and return the default ambient settings
//if the provided data source is null or whitespace, or the original
//setting is already 0 which means no limit.
int originalValue = AmbientSettings.QueryTimeoutSeconds;
if (string.IsNullOrWhiteSpace(dataSource)
|| (originalValue == 0))
{
return originalValue;
}
CachedInfo info;
bool hasFound = _cache.TryGetValue(dataSource, out info);
if (hasFound && info.IsAzure
&& originalValue < MinimalQueryTimeoutSecondsForAzure)
{
return MinimalQueryTimeoutSecondsForAzure;
}
else
{
return originalValue;
}
}
public static void AddOrUpdateIsAzure(IDbConnection connection, bool isAzure)
{
Validate.IsNotNull(nameof(connection), connection);
SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(connection.ConnectionString);
AddOrUpdateIsAzure(builder.DataSource, isAzure);
}
public static void AddOrUpdateIsAzure(string dataSource, bool isAzure)
{
Validate.IsNotNullOrWhitespaceString(nameof(dataSource), dataSource);
CachedInfo info;
bool hasFound = _cache.TryGetValue(dataSource, out info);
if (hasFound && info.IsAzure == isAzure)
{
return;
}
else
{
lock (_cacheLock)
{
if (! _cache.ContainsKey(dataSource))
{
//delete a batch of old elements when we try to add a new one and
//the capacity limitation is hit
if (_cache.Keys.Count > _maxCacheSize - 1)
{
var keysToDelete = _cache
.OrderBy(x => x.Value.LastUpdate)
.Take(_deleteBatchSize)
.Select(pair => pair.Key);
foreach (string key in keysToDelete)
{
_cache.TryRemove(key, out info);
}
}
}
info.IsAzure = isAzure;
info.LastUpdate = DateTime.UtcNow;
_cache.AddOrUpdate(dataSource, info, (key, oldValue) => info);
}
}
}
private static string SafeGetDataSourceFromConnection(IDbConnection connection)
{
if (connection == null)
{
return null;
}
try
{
SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(connection.ConnectionString);
return builder.DataSource;
}
catch
{
Logger.Write(LogLevel.Error, String.Format(Resources.FailedToParseConnectionString, connection.ConnectionString));
return null;
}
}
}
}