Unverified Commit b40a47e9 authored by Nick Craver's avatar Nick Craver Committed by GitHub

Build: add Ubuntu (#914)

* Build: let's try and get Ubuntu working
* Eliminate LibraryTargetFrameworks
* Tests: only build net462 on Windows
* Oh Linux, you're gonna be fun
* More line ending love
* Move perf counter and thread bits to PerfCounterHelper
parent e167b092
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
<Copyright>2017 Stack Exchange, Inc.</Copyright> <Copyright>2017 Stack Exchange, Inc.</Copyright>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors> <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
<AssemblyOriginatorKeyFile>../StackExchange.Redis.snk</AssemblyOriginatorKeyFile> <AssemblyOriginatorKeyFile>$(MSBuildThisFileDirectory)StackExchange.Redis.snk</AssemblyOriginatorKeyFile>
<PackageId>$(AssemblyName)</PackageId> <PackageId>$(AssemblyName)</PackageId>
<Features>strict</Features> <Features>strict</Features>
<Authors>Stack Exchange, Inc.; marc.gravell</Authors> <Authors>Stack Exchange, Inc.; marc.gravell</Authors>
...@@ -24,7 +24,6 @@ ...@@ -24,7 +24,6 @@
<DefaultLanguage>en-US</DefaultLanguage> <DefaultLanguage>en-US</DefaultLanguage>
<IncludeSymbols>false</IncludeSymbols> <IncludeSymbols>false</IncludeSymbols>
<LibraryTargetFrameworks>netstandard2.0</LibraryTargetFrameworks>
<xUnitVersion>2.4.0-beta.2.build3981</xUnitVersion> <xUnitVersion>2.4.0-beta.2.build3981</xUnitVersion>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
......
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>$(LibraryTargetFrameworks)</TargetFrameworks> <TargetFrameworks>netstandard2.0</TargetFrameworks>
<GenerateDocumentationFile>false</GenerateDocumentationFile> <GenerateDocumentationFile>false</GenerateDocumentationFile>
<PackageTags>Redis;Search;Modules;RediSearch</PackageTags> <PackageTags>Redis;Search;Modules;RediSearch</PackageTags>
<SignAssembly>true</SignAssembly> <SignAssembly>true</SignAssembly>
......
echo Starting Redis servers for testing... INDENT=' '
echo "Starting Redis servers for testing..."
#Basic Servers #Basic Servers
echo Starting Basic: 6379-6382 echo "Starting Basic: 6379-6382"
pushd Basic > /dev/null pushd Basic > /dev/null
echo Master: 6379 echo "${INDENT}Master: 6379"
redis-server master-6379.conf &>/dev/null & redis-server master-6379.conf &>/dev/null &
echo Slave: 6380 echo "${INDENT}Slave: 6380"
redis-server slave-6380.conf &>/dev/null & redis-server slave-6380.conf &>/dev/null &
echo Secure: 6381 echo "${INDENT}Secure: 6381"
redis-server secure-6381.conf &>/dev/null & redis-server secure-6381.conf &>/dev/null &
popd > /dev/null popd > /dev/null
#Failover Servers #Failover Servers
echo Starting Failover: 6382-6383 echo Starting Failover: 6382-6383
pushd Failover > /dev/null pushd Failover > /dev/null
echo Master: 6382 echo "${INDENT}Master: 6382"
redis-server master-6382.conf &>/dev/null & redis-server master-6382.conf &>/dev/null &
echo Slave: 6383 echo "${INDENT}Slave: 6383"
redis-server slave-6383.conf &>/dev/null & redis-server slave-6383.conf &>/dev/null &
popd > /dev/null popd > /dev/null
...@@ -34,10 +35,10 @@ popd > /dev/null ...@@ -34,10 +35,10 @@ popd > /dev/null
#Sentinel Servers #Sentinel Servers
echo Starting Sentinel: 7010-7011,26379-26380 echo Starting Sentinel: 7010-7011,26379-26380
pushd Sentinel > /dev/null pushd Sentinel > /dev/null
echo Targets: 7010-7011 echo "${INDENT}Targets: 7010-7011"
redis-server redis-7010.conf &>/dev/null & redis-server redis-7010.conf &>/dev/null &
redis-server redis-7011.conf &>/dev/null & redis-server redis-7011.conf &>/dev/null &
echo Monitors: 26379-26380 echo "${INDENT}Monitors: 26379-26380"
redis-server sentinel-26379.conf --sentinel &>/dev/null & redis-server sentinel-26379.conf --sentinel &>/dev/null &
redis-server sentinel-26380.conf --sentinel &>/dev/null & redis-server sentinel-26380.conf --sentinel &>/dev/null &
popd > /dev/null popd > /dev/null
......
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>$(LibraryTargetFrameworks)</TargetFrameworks> <TargetFrameworks>netstandard2.0</TargetFrameworks>
<VersionPrefix>0.1</VersionPrefix> <VersionPrefix>0.1</VersionPrefix>
<GenerateDocumentationFile>false</GenerateDocumentationFile> <GenerateDocumentationFile>false</GenerateDocumentationFile>
<PackageTags>Redis;Search;Modules;RediSearch</PackageTags> <PackageTags>Redis;Search;Modules;RediSearch</PackageTags>
......
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>$(LibraryTargetFrameworks)</TargetFrameworks> <TargetFrameworks>netstandard2.0</TargetFrameworks>
<Description>Basic redis server based on StackExchange.Redis</Description> <Description>Basic redis server based on StackExchange.Redis</Description>
<AssemblyTitle>StackExchange.Redis</AssemblyTitle> <AssemblyTitle>StackExchange.Redis</AssemblyTitle>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
......
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>$(LibraryTargetFrameworks)</TargetFrameworks> <TargetFrameworks>netstandard2.0</TargetFrameworks>
<Description>High performance Redis client, incorporating both synchronous and asynchronous usage.</Description> <Description>High performance Redis client, incorporating both synchronous and asynchronous usage.</Description>
<AssemblyTitle>StackExchange.Redis.StrongName</AssemblyTitle> <AssemblyTitle>StackExchange.Redis.StrongName</AssemblyTitle>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
......
...@@ -189,9 +189,9 @@ public async Task GeoRadiusOverloads() ...@@ -189,9 +189,9 @@ public async Task GeoRadiusOverloads()
// Invalid overload // Invalid overload
// Since this would throw ERR could not decode requested zset member, we catch and return something more useful to the user earlier. // Since this would throw ERR could not decode requested zset member, we catch and return something more useful to the user earlier.
var ex = Assert.Throws<ArgumentException>(() => db.GeoRadius(key, -1.759925, 52.19493, GeoUnit.Miles, 500, Order.Ascending, GeoRadiusOptions.WithDistance)); var ex = Assert.Throws<ArgumentException>(() => db.GeoRadius(key, -1.759925, 52.19493, GeoUnit.Miles, 500, Order.Ascending, GeoRadiusOptions.WithDistance));
Assert.Equal("Member should not be a double, you likely want the GeoRadius(RedisKey, double, double, ...) overload.\r\nParameter name: member", ex.Message); Assert.Equal("Member should not be a double, you likely want the GeoRadius(RedisKey, double, double, ...) overload." + Environment.NewLine + "Parameter name: member", ex.Message);
ex = await Assert.ThrowsAsync<ArgumentException>(() => db.GeoRadiusAsync(key, -1.759925, 52.19493, GeoUnit.Miles, 500, Order.Ascending, GeoRadiusOptions.WithDistance)).ForAwait(); ex = await Assert.ThrowsAsync<ArgumentException>(() => db.GeoRadiusAsync(key, -1.759925, 52.19493, GeoUnit.Miles, 500, Order.Ascending, GeoRadiusOptions.WithDistance)).ForAwait();
Assert.Equal("Member should not be a double, you likely want the GeoRadius(RedisKey, double, double, ...) overload.\r\nParameter name: member", ex.Message); Assert.Equal("Member should not be a double, you likely want the GeoRadius(RedisKey, double, double, ...) overload." + Environment.NewLine + "Parameter name: member", ex.Message);
// The good stuff // The good stuff
GeoRadiusResult[] result = db.GeoRadius(key, -1.759925, 52.19493, 500, unit: GeoUnit.Miles, order: Order.Ascending, options: GeoRadiusOptions.WithDistance); GeoRadiusResult[] result = db.GeoRadius(key, -1.759925, 52.19493, 500, unit: GeoUnit.Miles, order: Order.Ascending, options: GeoRadiusOptions.WithDistance);
......
...@@ -11,20 +11,20 @@ public class RedisFeaturesTests ...@@ -11,20 +11,20 @@ public class RedisFeaturesTests
var features = new RedisFeatures(new Version(2, 9)); var features = new RedisFeatures(new Version(2, 9));
var s = features.ToString(); var s = features.ToString();
Assert.True(features.ExecAbort); Assert.True(features.ExecAbort);
Assert.StartsWith("Features in 2.9\r\n", s); Assert.StartsWith("Features in 2.9" + Environment.NewLine, s);
Assert.Contains("ExecAbort: True\r\n", s); Assert.Contains("ExecAbort: True" + Environment.NewLine, s);
features = new RedisFeatures(new Version(2, 9, 5)); features = new RedisFeatures(new Version(2, 9, 5));
s = features.ToString(); s = features.ToString();
Assert.False(features.ExecAbort); Assert.False(features.ExecAbort);
Assert.StartsWith("Features in 2.9.5\r\n", s); Assert.StartsWith("Features in 2.9.5" + Environment.NewLine, s);
Assert.Contains("ExecAbort: False\r\n", s); Assert.Contains("ExecAbort: False" + Environment.NewLine, s);
features = new RedisFeatures(new Version(3, 0)); features = new RedisFeatures(new Version(3, 0));
s = features.ToString(); s = features.ToString();
Assert.True(features.ExecAbort); Assert.True(features.ExecAbort);
Assert.StartsWith("Features in 3.0\r\n", s); Assert.StartsWith("Features in 3.0" + Environment.NewLine, s);
Assert.Contains("ExecAbort: True\r\n", s); Assert.Contains("ExecAbort: True" + Environment.NewLine, s);
} }
} }
} }
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<Description>StackExchange.Redis.Tests</Description> <Description>StackExchange.Redis.Tests</Description>
<TargetFrameworks>net462;netcoreapp2.0</TargetFrameworks> <TargetFrameworks Condition="'$(OS)' == 'Windows_NT'">net462;netcoreapp2.0</TargetFrameworks>
<TargetFrameworks Condition="'$(OS)' != 'Windows_NT'">netcoreapp2.0</TargetFrameworks>
<AssemblyName>StackExchange.Redis.Tests</AssemblyName> <AssemblyName>StackExchange.Redis.Tests</AssemblyName>
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles> <GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
<GenerateDocumentationFile>false</GenerateDocumentationFile> <GenerateDocumentationFile>false</GenerateDocumentationFile>
......
...@@ -623,7 +623,7 @@ private void LogLockedWithThreadPoolStats(TextWriter log, string message, out in ...@@ -623,7 +623,7 @@ private void LogLockedWithThreadPoolStats(TextWriter log, string message, out in
{ {
var sb = new StringBuilder(); var sb = new StringBuilder();
sb.Append(message); sb.Append(message);
busyWorkerCount = GetThreadPoolStats(out string iocp, out string worker); busyWorkerCount = PerfCounterHelper.GetThreadPoolStats(out string iocp, out string worker);
sb.Append(", IOCP: ").Append(iocp).Append(", WORKER: ").Append(worker); sb.Append(", IOCP: ").Append(iocp).Append(", WORKER: ").Append(worker);
LogLocked(log, sb.ToString()); LogLocked(log, sb.ToString());
} }
...@@ -2177,16 +2177,16 @@ void add(string lk, string sk, string v) ...@@ -2177,16 +2177,16 @@ void add(string lk, string sk, string v)
// only add keyslot if its a valid cluster key slot // only add keyslot if its a valid cluster key slot
if (hashSlot != ServerSelectionStrategy.NoSlot) if (hashSlot != ServerSelectionStrategy.NoSlot)
{ {
add("Key-HashSlot", "keyHashSlot", message.GetHashSlot(this.ServerSelectionStrategy).ToString()); add("Key-HashSlot", "PerfCounterHelperkeyHashSlot", message.GetHashSlot(this.ServerSelectionStrategy).ToString());
} }
int busyWorkerCount = GetThreadPoolStats(out string iocp, out string worker); int busyWorkerCount = PerfCounterHelper.GetThreadPoolStats(out string iocp, out string worker);
add("ThreadPool-IO-Completion", "IOCP", iocp); add("ThreadPool-IO-Completion", "IOCP", iocp);
add("ThreadPool-Workers", "WORKER", worker); add("ThreadPool-Workers", "WORKER", worker);
data.Add(Tuple.Create("Busy-Workers", busyWorkerCount.ToString())); data.Add(Tuple.Create("Busy-Workers", busyWorkerCount.ToString()));
if (IncludePerformanceCountersInExceptions) if (IncludePerformanceCountersInExceptions)
{ {
add("Local-CPU", "Local-CPU", GetSystemCpuPercent()); add("Local-CPU", "Local-CPU", PerfCounterHelper.GetSystemCpuPercent());
} }
sb.Append(" (Please take a look at this article for some common client-side issues that can cause timeouts: "); sb.Append(" (Please take a look at this article for some common client-side issues that can cause timeouts: ");
...@@ -2222,33 +2222,6 @@ void add(string lk, string sk, string v) ...@@ -2222,33 +2222,6 @@ void add(string lk, string sk, string v)
} }
} }
internal static string GetThreadPoolAndCPUSummary(bool includePerformanceCounters)
{
GetThreadPoolStats(out string iocp, out string worker);
var cpu = includePerformanceCounters ? GetSystemCpuPercent() : "n/a";
return $"IOCP: {iocp}, WORKER: {worker}, Local-CPU: {cpu}";
}
private static string GetSystemCpuPercent()
{
return (PerfCounterHelper.TryGetSystemCPU(out float systemCPU))
? Math.Round(systemCPU, 2) + "%"
: "unavailable";
}
private static int GetThreadPoolStats(out string iocp, out string worker)
{
ThreadPool.GetMaxThreads(out int maxWorkerThreads, out int maxIoThreads);
ThreadPool.GetAvailableThreads(out int freeWorkerThreads, out int freeIoThreads);
ThreadPool.GetMinThreads(out int minWorkerThreads, out int minIoThreads);
int busyIoThreads = maxIoThreads - freeIoThreads;
int busyWorkerThreads = maxWorkerThreads - freeWorkerThreads;
iocp = $"(Busy={busyIoThreads},Free={freeIoThreads},Min={minIoThreads},Max={maxIoThreads})";
worker = $"(Busy={busyWorkerThreads},Free={freeWorkerThreads},Min={minWorkerThreads},Max={maxWorkerThreads})";
return busyWorkerThreads;
}
/// <summary> /// <summary>
/// Should exceptions include identifiable details? (key names, additional .Data annotations) /// Should exceptions include identifiable details? (key names, additional .Data annotations)
/// </summary> /// </summary>
......
...@@ -110,7 +110,7 @@ internal static Exception NoConnectionAvailable(bool includeDetail, bool include ...@@ -110,7 +110,7 @@ internal static Exception NoConnectionAvailable(bool includeDetail, bool include
if (includeDetail) if (includeDetail)
{ {
exceptionmessage.Append("; ").Append(ConnectionMultiplexer.GetThreadPoolAndCPUSummary(includePerformanceCounters)); exceptionmessage.Append("; ").Append(PerfCounterHelper.GetThreadPoolAndCPUSummary(includePerformanceCounters));
} }
var ex = new RedisConnectionException(ConnectionFailureType.UnableToResolvePhysicalConnection, exceptionmessage.ToString(), innerException, message?.Status ?? CommandStatus.Unknown); var ex = new RedisConnectionException(ConnectionFailureType.UnableToResolvePhysicalConnection, exceptionmessage.ToString(), innerException, message?.Status ?? CommandStatus.Unknown);
......
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading;
namespace StackExchange.Redis namespace StackExchange.Redis
{ {
...@@ -48,5 +49,33 @@ public static bool TryGetSystemCPU(out float value) ...@@ -48,5 +49,33 @@ public static bool TryGetSystemCPU(out float value)
} }
return false; return false;
} }
internal static string GetThreadPoolAndCPUSummary(bool includePerformanceCounters)
{
GetThreadPoolStats(out string iocp, out string worker);
var cpu = includePerformanceCounters ? GetSystemCpuPercent() : "n/a";
return $"IOCP: {iocp}, WORKER: {worker}, Local-CPU: {cpu}";
}
internal static string GetSystemCpuPercent()
{
return TryGetSystemCPU(out float systemCPU)
? Math.Round(systemCPU, 2) + "%"
: "unavailable";
}
internal static int GetThreadPoolStats(out string iocp, out string worker)
{
ThreadPool.GetMaxThreads(out int maxWorkerThreads, out int maxIoThreads);
ThreadPool.GetAvailableThreads(out int freeWorkerThreads, out int freeIoThreads);
ThreadPool.GetMinThreads(out int minWorkerThreads, out int minIoThreads);
int busyIoThreads = maxIoThreads - freeIoThreads;
int busyWorkerThreads = maxWorkerThreads - freeWorkerThreads;
iocp = $"(Busy={busyIoThreads},Free={freeIoThreads},Min={minIoThreads},Max={maxIoThreads})";
worker = $"(Busy={busyWorkerThreads},Free={freeWorkerThreads},Min={minWorkerThreads},Max={maxWorkerThreads})";
return busyWorkerThreads;
}
} }
} }
...@@ -213,7 +213,7 @@ public virtual bool SetResult(PhysicalConnection connection, Message message, Ra ...@@ -213,7 +213,7 @@ public virtual bool SetResult(PhysicalConnection connection, Message message, Ra
unableToConnectError = true; unableToConnectError = true;
err = $"Endpoint {endpoint} serving hashslot {hashSlot} is not reachable at this point of time. Please check connectTimeout value. If it is low, try increasing it to give the ConnectionMultiplexer a chance to recover from the network disconnect. "; err = $"Endpoint {endpoint} serving hashslot {hashSlot} is not reachable at this point of time. Please check connectTimeout value. If it is low, try increasing it to give the ConnectionMultiplexer a chance to recover from the network disconnect. ";
} }
err += ConnectionMultiplexer.GetThreadPoolAndCPUSummary(bridge.Multiplexer.IncludePerformanceCountersInExceptions); err += PerfCounterHelper.GetThreadPoolAndCPUSummary(bridge.Multiplexer.IncludePerformanceCountersInExceptions);
} }
} }
} }
......
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>$(LibraryTargetFrameworks)</TargetFrameworks> <TargetFrameworks>netstandard2.0</TargetFrameworks>
<Description>High performance Redis client, incorporating both synchronous and asynchronous usage.</Description> <Description>High performance Redis client, incorporating both synchronous and asynchronous usage.</Description>
<AssemblyName>StackExchange.Redis</AssemblyName> <AssemblyName>StackExchange.Redis</AssemblyName>
<AssemblyTitle>StackExchange.Redis</AssemblyTitle> <AssemblyTitle>StackExchange.Redis</AssemblyTitle>
......
image: Visual Studio 2017 image:
- Visual Studio 2017
- Ubuntu
init: init:
- git config --global core.autocrlf input - git config --global core.autocrlf input
...@@ -38,7 +40,18 @@ install: ...@@ -38,7 +40,18 @@ install:
redis-server.exe --service-install --service-name "redis-26380" "..\Sentinel\sentinel-26380.conf" --sentinel redis-server.exe --service-install --service-name "redis-26380" "..\Sentinel\sentinel-26380.conf" --sentinel
cd ..\.. cd ..\..
- ps: Start-Service redis-* - sh: >-
cd RedisConfigs
chmod +x start-all.sh
./start-all.sh
cd ..
- ps: >-
if (Get-Command "Start-Service" -errorAction SilentlyContinue) {
Start-Service redis-*
}
skip_branch_with_pr: true skip_branch_with_pr: true
skip_tags: true skip_tags: true
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment