Skip to content

Commit

Permalink
SingleQuery benchmark
Browse files Browse the repository at this point in the history
  • Loading branch information
markrendle committed Jan 7, 2018
1 parent 069920e commit b37d4fb
Show file tree
Hide file tree
Showing 17 changed files with 386 additions and 3 deletions.
17 changes: 17 additions & 0 deletions Beeline.sln
Expand Up @@ -13,6 +13,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Beeline.Tests", "test\Beeli
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Beeline.Benchmarks", "test\Beeline.Benchmarks\Beeline.Benchmarks.csproj", "{0045104E-E9BC-4566-8324-3D1E4099CD6E}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{0A575C00-110D-46B7-A30D-E69A25961CCE}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Beeline.AspNetCoreBenchmarks", "samples\Beeline.AspNetCoreBenchmarks\Beeline.AspNetCoreBenchmarks.csproj", "{DF8CE83B-0BF0-412D-A5B6-BBE27F58E675}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -62,10 +66,23 @@ Global
{0045104E-E9BC-4566-8324-3D1E4099CD6E}.Release|x64.Build.0 = Release|x64
{0045104E-E9BC-4566-8324-3D1E4099CD6E}.Release|x86.ActiveCfg = Release|x86
{0045104E-E9BC-4566-8324-3D1E4099CD6E}.Release|x86.Build.0 = Release|x86
{DF8CE83B-0BF0-412D-A5B6-BBE27F58E675}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DF8CE83B-0BF0-412D-A5B6-BBE27F58E675}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DF8CE83B-0BF0-412D-A5B6-BBE27F58E675}.Debug|x64.ActiveCfg = Debug|x64
{DF8CE83B-0BF0-412D-A5B6-BBE27F58E675}.Debug|x64.Build.0 = Debug|x64
{DF8CE83B-0BF0-412D-A5B6-BBE27F58E675}.Debug|x86.ActiveCfg = Debug|x86
{DF8CE83B-0BF0-412D-A5B6-BBE27F58E675}.Debug|x86.Build.0 = Debug|x86
{DF8CE83B-0BF0-412D-A5B6-BBE27F58E675}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DF8CE83B-0BF0-412D-A5B6-BBE27F58E675}.Release|Any CPU.Build.0 = Release|Any CPU
{DF8CE83B-0BF0-412D-A5B6-BBE27F58E675}.Release|x64.ActiveCfg = Release|x64
{DF8CE83B-0BF0-412D-A5B6-BBE27F58E675}.Release|x64.Build.0 = Release|x64
{DF8CE83B-0BF0-412D-A5B6-BBE27F58E675}.Release|x86.ActiveCfg = Release|x86
{DF8CE83B-0BF0-412D-A5B6-BBE27F58E675}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{B7F309C8-75FC-41FE-BF10-32B727AF9EB4} = {FD1C9140-EAE4-4C8B-8B93-985C07992FF7}
{EBD6AEB9-1E0B-4651-83CD-5D4956C19EA3} = {D20967AB-999E-44C6-9F01-BF0638F12D1E}
{0045104E-E9BC-4566-8324-3D1E4099CD6E} = {D20967AB-999E-44C6-9F01-BF0638F12D1E}
{DF8CE83B-0BF0-412D-A5B6-BBE27F58E675} = {0A575C00-110D-46B7-A30D-E69A25961CCE}
EndGlobalSection
EndGlobal
11 changes: 11 additions & 0 deletions nuget.config
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<clear/>
<add key="dotnet.myget.org" value="https://dotnet.myget.org/F/dotnet-core/api/v3/index.json" />
<add key="AspNetCore" value="https://dotnet.myget.org/F/aspnetcore-dev/api/v3/index.json" />
<add key="AspNetCoreTools" value="https://dotnet.myget.org/F/aspnetcore-tools/api/v3/index.json" />
<add key="NuGet" value="https://api.nuget.org/v3/index.json" />
<add key="roslyn" value="https://dotnet.myget.org/F/roslyn/api/v3/index.json" />
</packageSources>
</configuration>
@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<RuntimeFrameworkVersion>2.1.0-preview1-26103-03</RuntimeFrameworkVersion>
<NETStandardImplicitPackageVersion>2.1.0-preview1-26103-03</NETStandardImplicitPackageVersion>
<LangVersion>7.2</LangVersion>
</PropertyGroup>
<PropertyGroup>
<MicrosoftNETPlatformLibrary>Microsoft.NETCore.App</MicrosoftNETPlatformLibrary>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="JetBrains.Annotations" Version="11.1.0" />
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.3" />
<PackageReference Include="Npgsql" Version="3.2.6" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Beeline\Beeline.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,7 @@
namespace Beeline.AspNetCoreBenchmarks.Configuration
{
public class AppSettings
{
public string ConnectionString { get; set; }
}
}
@@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;

namespace Beeline.AspNetCoreBenchmarks.Controllers
{
[Route("api/[controller]")]
public class ValuesController : Controller
{
// GET api/values
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}

// GET api/values/5
[HttpGet("{id}")]
public string Get(int id)
{
return "value";
}

// POST api/values
[HttpPost]
public void Post([FromBody]string value)
{
}

// PUT api/values/5
[HttpPut("{id}")]
public void Put(int id, [FromBody]string value)
{
}

// DELETE api/values/5
[HttpDelete("{id}")]
public void Delete(int id)
{
}
}
}
61 changes: 61 additions & 0 deletions samples/Beeline.AspNetCoreBenchmarks/Data/BeelineDb.cs
@@ -0,0 +1,61 @@
using System;
using System.Buffers;
using System.Data;
using System.Data.Common;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using Beeline.AspNetCoreBenchmarks.Configuration;
using Microsoft.Extensions.Options;

namespace Beeline.AspNetCoreBenchmarks.Data
{
public class BeelineDb
{
private readonly IRandom _random;
private readonly DbProviderFactory _dbProvider;
private readonly string _connectionString;
private ObjectWriter _objectWriter;

public BeelineDb(IRandom random, DbProviderFactory dbProvider, IOptions<AppSettings> appSettings)
{
_random = random;
_dbProvider = dbProvider;
_connectionString = appSettings.Value.ConnectionString;
}

public async Task<int> LoadSingleQueryRow(byte[] buffer, CancellationToken ct = default)
{
using (var db = _dbProvider.CreateConnection())
{
Debug.Assert(db != null);
db.ConnectionString = _connectionString;
using (var cmd = CreateSingleQueryCommand(db, _random.Next(1, 10001)))
{
await db.OpenAsync(ct);
using (var reader = await cmd.ExecuteReaderAsync(ct))
{
if (_objectWriter is null)
{
_objectWriter = new ObjectWriter(RowSerializer.For(reader, true), 1024);
}

return await _objectWriter.WriteSingle(reader, buffer, ct);
}
}
}
}

private static DbCommand CreateSingleQueryCommand(DbConnection db, int id)
{
var cmd = db.CreateCommand();
cmd.CommandText = "SELECT id, randomnumber FROM world WHERE id = @id";
var idParameter = cmd.CreateParameter();
idParameter.ParameterName = "id";
idParameter.DbType = DbType.Int32;
idParameter.Value = id;
cmd.Parameters.Add(idParameter);
return cmd;
}
}
}
44 changes: 44 additions & 0 deletions samples/Beeline.AspNetCoreBenchmarks/Data/DatabaseInitializer.cs
@@ -0,0 +1,44 @@
using System;
using Npgsql;
using NpgsqlTypes;

namespace Beeline.AspNetCoreBenchmarks.Data
{
public static class DatabaseInitializer
{
private const string DropTable = "drop table if exists world";
private const string CreateTable = @"create table world
(
id integer not null
constraint world_pkey
primary key,
randomnumber integer
)";

private const string Insert = "INSERT INTO world (id, randomnumber) VALUES (@id, @randomnumber)";

public static void Initialize(string connectionString)
{
var random = new Random(42);
using (var connection = new NpgsqlConnection(connectionString))
using (var cmd = connection.CreateCommand())
{
connection.Open();
cmd.CommandText = DropTable;
cmd.ExecuteNonQuery();
cmd.CommandText = CreateTable;
cmd.ExecuteNonQuery();
cmd.CommandText = Insert;
var @id = cmd.Parameters.Add("id", NpgsqlDbType.Integer);
var @randomnumber = cmd.Parameters.Add("randomnumber", NpgsqlDbType.Integer);
cmd.Prepare();
for (int i = 1; i < 10001; i++)
{
@id.Value = i;
@randomnumber.Value = random.Next(1, 999_999_999);
cmd.ExecuteNonQuery();
}
}
}
}
}
17 changes: 17 additions & 0 deletions samples/Beeline.AspNetCoreBenchmarks/Data/DefaultRandom.cs
@@ -0,0 +1,17 @@
using System;
using System.Threading;

namespace Beeline.AspNetCoreBenchmarks.Data
{
public class DefaultRandom : IRandom
{
private static int _nextSeed = 0;
// Random isn't thread safe
private static readonly ThreadLocal<Random> Random = new ThreadLocal<Random>(() => new Random(Interlocked.Increment(ref _nextSeed)));

public int Next(int minValue, int maxValue)
{
return Random.Value.Next(minValue, maxValue);
}
}
}
7 changes: 7 additions & 0 deletions samples/Beeline.AspNetCoreBenchmarks/Data/IRandom.cs
@@ -0,0 +1,7 @@
namespace Beeline.AspNetCoreBenchmarks.Data
{
public interface IRandom
{
int Next(int minValue, int maxValue);
}
}
@@ -0,0 +1,58 @@
using System;
using System.Buffers;
using System.Threading.Tasks;
using Beeline.AspNetCoreBenchmarks.Data;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;

namespace Beeline.AspNetCoreBenchmarks.Middleware
{
public class SingleQueryBeelineMiddleware
{
private static readonly PathString Path = new PathString("/db/beeline");

private readonly RequestDelegate _next;

public SingleQueryBeelineMiddleware(RequestDelegate next)
{
_next = next;
}

public async Task Invoke(HttpContext httpContext)
{
if (httpContext.Request.Path.StartsWithSegments(Path, StringComparison.Ordinal))
{
var db = httpContext.RequestServices.GetService<BeelineDb>();
var buffer = ArrayPool<byte>.Shared.Rent(128);
try
{
var length = await db.LoadSingleQueryRow(buffer, httpContext.RequestAborted);

httpContext.Response.StatusCode = StatusCodes.Status200OK;
httpContext.Response.ContentType = "application/json";
httpContext.Response.ContentLength = length;
await httpContext.Response.Body.WriteAsync(buffer, 0, length, httpContext.RequestAborted);
}
finally
{
ArrayPool<byte>.Shared.Return(buffer);
}

return;
}

await _next(httpContext);
}
}

public static class SingleQueryDapperMiddlewareExtensions
{
public static IApplicationBuilder UseSingleQueryBeeline(this IApplicationBuilder builder)
{
return builder.UseMiddleware<SingleQueryBeelineMiddleware>();
}
}
}
27 changes: 27 additions & 0 deletions samples/Beeline.AspNetCoreBenchmarks/Program.cs
@@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;

namespace Beeline.AspNetCoreBenchmarks
{
[UsedImplicitly]
public class Program
{
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}

private static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.Build();
}
}
44 changes: 44 additions & 0 deletions samples/Beeline.AspNetCoreBenchmarks/Startup.cs
@@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
using System.Threading.Tasks;
using Beeline.AspNetCoreBenchmarks.Configuration;
using Beeline.AspNetCoreBenchmarks.Data;
using Beeline.AspNetCoreBenchmarks.Middleware;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Npgsql;

namespace Beeline.AspNetCoreBenchmarks
{
[UsedImplicitly]
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}

private IConfiguration Configuration { get; }

public void ConfigureServices(IServiceCollection services)
{
DatabaseInitializer.Initialize(Configuration.GetValue<string>("ConnectionString"));
services.Configure<AppSettings>(Configuration);
services.AddSingleton<IRandom, DefaultRandom>();
services.AddSingleton<DbProviderFactory>(NpgsqlFactory.Instance);
services.AddSingleton<BeelineDb>();
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseSingleQueryBeeline();
}
}
}

0 comments on commit b37d4fb

Please sign in to comment.