Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
12 commits
Select commit Hold shift + click to select a range
0982b90
Добавил дефолтный метод контроллера, возвращающий все каунтеры
AleXKarasev May 9, 2015
092a1c7
При создании нового каунтера возвращается 0
AleXKarasev May 9, 2015
14f8530
Обновил референсы до последней версии, убрал binginRedirect
AleXKarasev May 10, 2015
a97be84
Поправил замечания:
AleXKarasev May 12, 2015
66b3fa2
Поправил табуляцию у теста
AleXKarasev May 12, 2015
e33c72c
Переделал тест многопоточной работы. Добавил проверку конечного резул…
AleXKarasev May 15, 2015
e85cc29
Переделал механизм обновления каунтера.
AleXKarasev May 15, 2015
07090b8
Удалил неиспользуемую переменную
AleXKarasev May 17, 2015
0869d78
Редиректы в конфиге появляються из за Ninject (Ninject.Web.WebApi, Ni…
AleXKarasev May 17, 2015
73bf5a2
Т.к. в монге нет таймаута на операцию, то сделал запуск параллельно р…
AleXKarasev May 17, 2015
d911583
Нужно было добавить Microsoft.Owin.Host.SystemWeb, это реализация Owi…
AleXKarasev May 17, 2015
9761830
Добавил подписку на резолвы библиотек доменом. Что бы подписка вызыва…
AleXKarasev May 17, 2015
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
23 changes: 15 additions & 8 deletions BuildRevisionCounter.Tests/App.config
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>

<configuration>

<connectionStrings>
<add
name="MongoDBStorage"
connectionString="mongodb://localhost/brCounterTest" />
</connectionStrings>

<connectionStrings>
<add name="MongoDBStorage" connectionString="mongodb://localhost/brCounterTest" />
</connectionStrings>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Microsoft.Owin" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.Http.Owin" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.2.3.0" newVersion="5.2.3.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
15 changes: 12 additions & 3 deletions BuildRevisionCounter.Tests/BuildRevisionCounter.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,24 @@
<Reference Include="MongoDB.Driver.Core">
<HintPath>..\packages\MongoDB.Driver.Core.2.0.0\lib\net45\MongoDB.Driver.Core.dll</HintPath>
</Reference>
<Reference Include="nunit.framework">
<Reference Include="Newtonsoft.Json, Version=6.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Newtonsoft.Json.6.0.8\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="nunit.framework, Version=2.6.4.14350, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\NUnit.2.6.4\lib\nunit.framework.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Configuration" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Web.Http, Version=5.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<Reference Include="System.Net.Http.Formatting, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll</HintPath>
</Reference>
<Reference Include="System.Web.Http, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Microsoft.AspNet.WebApi.Core.5.0.0\lib\net45\System.Web.Http.dll</HintPath>
<HintPath>..\packages\Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45\System.Web.Http.dll</HintPath>
</Reference>
</ItemGroup>
<Choose>
Expand Down
102 changes: 98 additions & 4 deletions BuildRevisionCounter.Tests/Controllers/CounterControllerTest.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
using System.Net;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using System.Web.Http;
using BuildRevisionCounter.Controllers;
Expand Down Expand Up @@ -33,7 +36,7 @@ public async Task CurrentThrowsExceptionIfRevisionNotFound()
{
try
{
var rev = await _controller.Current("CurrentThrowsExceptionIfRevisionNotFound");
await _controller.Current("CurrentThrowsExceptionIfRevisionNotFound");
Assert.Fail();
}
catch (HttpResponseException ex)
Expand All @@ -43,10 +46,30 @@ public async Task CurrentThrowsExceptionIfRevisionNotFound()
}

[Test]
public async Task BumpingNewRevisionReturnsOne()
public async Task BumpingNewRevisionReturnsZero()
{
var rev = await _controller.Bumping("BumpingNewRevisionReturnsZero");
Assert.AreEqual(1, rev);
Assert.AreEqual(0, rev);
}

[Test]
public async Task BumpingNewRevisionMayBeInvokedMultipleTimes()
{
string revName = "BumpingNewRevisionMayBeInvokedMultipleTimes" + Guid.NewGuid().ToString();
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Почему это решение не распространено на остальные случаи?


// запустим сразу n потоков вставки
const int n = 5;
List<Task> taskList = new List<Task>();
for (int i = 0; i < n; i++)
{
taskList.Add(Task.Run(async () => await _controller.Bumping(revName)));
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Напрасно усложненный код. Должно быть так:

taskList.Add(_controller.Bumping(revName));

}
// дождемся окончания всех потококв
Task.WaitAll(taskList.ToArray());

// проверим результатнашего каунтера
var res = await _controller.Current(revName);
Assert.AreEqual(n - 1, res);
}

[Test]
Expand All @@ -64,5 +87,76 @@ public async Task CurrentReturnsSameValueAsPreviousBumping()
var rev2 = await _controller.Current("CurrentReturnSameValueAsPreviousBumping");
Assert.AreEqual(rev1, rev2);
}

[Test]
public async Task GetAllRevisionReturnsAllRevision()
{
const string revName1 = "BumpingIncrementsRevisionNumber1";
const string revName2 = "BumpingIncrementsRevisionNumber2";
const string revName3 = "BumpingIncrementsRevisionNumber3";

await _controller.Bumping(revName1);
await _controller.Bumping(revName1);
await _controller.Bumping(revName2);
await _controller.Bumping(revName3);

var rev1 = await _controller.Current(revName1);
var rev2 = await _controller.Current(revName2);
var rev3 = await _controller.Current(revName3);

var result = await _controller.GetAllRevision();

Assert.IsTrue(result.Any(x => x.Id == revName1 && x.NextNumber == rev1));
Assert.IsTrue(result.Any(x => x.Id == revName2 && x.NextNumber == rev2));
Assert.IsTrue(result.Any(x => x.Id == revName3 && x.NextNumber == rev3));
}

[Test]
public async Task GetAllRevisionReturnsSecondPage()
{
// пересоздадим БД перед этим тестом,
// он должен проводиться на чистой БД
SetUpAsync().Wait();
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Блокирующее ожидание в асинхронном коде.


for (int i = 0; i < 10; i++)
{
string revName = "GetAllRevisionReturnsSecondPage" + i.ToString();
await _controller.Bumping(revName);
}

var result = await _controller.GetAllRevision(3, 2);

Assert.IsTrue(result.Any(x => x.Id == "GetAllRevisionReturnsSecondPage3"));
Assert.IsTrue(result.Any(x => x.Id == "GetAllRevisionReturnsSecondPage4"));
Assert.IsTrue(result.Any(x => x.Id == "GetAllRevisionReturnsSecondPage5"));
}

[Test]
public async Task GetAllRevisionArgumentExceptionPageSize()
{
try
{
await _controller.GetAllRevision(0, 2);
Assert.Fail();
}
catch (HttpResponseException ex)
{
Assert.AreEqual(HttpStatusCode.BadRequest, ex.Response.StatusCode);
}
}

[Test]
public async Task GetAllRevisionArgumentExceptionPageNumber()
{
try
{
await _controller.GetAllRevision(3, 0);
Assert.Fail();
}
catch (HttpResponseException ex)
{
Assert.AreEqual(HttpStatusCode.BadRequest, ex.Response.StatusCode);
}
}
}
}
3 changes: 3 additions & 0 deletions BuildRevisionCounter.Tests/packages.config
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.AspNet.WebApi.Client" version="5.2.3" targetFramework="net451" />
<package id="Microsoft.AspNet.WebApi.Core" version="5.2.3" targetFramework="net451" />
<package id="MongoDB.Bson" version="2.0.0" targetFramework="net451" />
<package id="MongoDB.Driver" version="2.0.0" targetFramework="net451" />
<package id="MongoDB.Driver.Core" version="2.0.0" targetFramework="net451" />
<package id="Newtonsoft.Json" version="6.0.8" targetFramework="net451" />
<package id="NUnit" version="2.6.4" targetFramework="net451" />
</packages>
33 changes: 19 additions & 14 deletions BuildRevisionCounter/BuildRevisionCounter.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@
<ItemGroup>
<Reference Include="Microsoft.CSharp" />
<Reference Include="Microsoft.Owin">
<HintPath>..\packages\Microsoft.Owin.3.0.0\lib\net45\Microsoft.Owin.dll</HintPath>
<HintPath>..\packages\Microsoft.Owin.3.0.1\lib\net45\Microsoft.Owin.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Owin.Host.SystemWeb">
<HintPath>..\packages\Microsoft.Owin.Host.SystemWeb.3.0.0\lib\net45\Microsoft.Owin.Host.SystemWeb.dll</HintPath>
<HintPath>..\packages\Microsoft.Owin.Host.SystemWeb.3.0.1\lib\net45\Microsoft.Owin.Host.SystemWeb.dll</HintPath>
</Reference>
<Reference Include="MongoDB.Bson, Version=2.0.0.828, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
Expand All @@ -57,9 +57,9 @@
<Reference Include="MongoDB.Driver.Core">
<HintPath>..\packages\MongoDB.Driver.Core.2.0.0\lib\net45\MongoDB.Driver.Core.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=4.5.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<Reference Include="Newtonsoft.Json, Version=6.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Newtonsoft.Json.4.5.11\lib\net40\Newtonsoft.Json.dll</HintPath>
<HintPath>..\packages\Newtonsoft.Json.6.0.8\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="Ninject, Version=3.2.0.0, Culture=neutral, PublicKeyToken=c7192dc5380945e7, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
Expand All @@ -71,23 +71,26 @@
<Reference Include="Ninject.Web.Common">
<HintPath>..\packages\Ninject.Web.Common.3.2.3.0\lib\net45-full\Ninject.Web.Common.dll</HintPath>
</Reference>
<Reference Include="Ninject.Web.Common.OwinHost">
<Reference Include="Ninject.Web.Common.OwinHost, Version=3.2.0.0, Culture=neutral, PublicKeyToken=c7192dc5380945e7, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Ninject.Web.Common.OwinHost.3.2.3.0\lib\net45-full\Ninject.Web.Common.OwinHost.dll</HintPath>
</Reference>
<Reference Include="Ninject.Web.WebApi">
<Reference Include="Ninject.Web.WebApi, Version=3.2.0.0, Culture=neutral, PublicKeyToken=c7192dc5380945e7, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Ninject.Web.WebApi.3.2.4.0\lib\net45-full\Ninject.Web.WebApi.dll</HintPath>
</Reference>
<Reference Include="Ninject.Web.WebApi.OwinHost">
<Reference Include="Ninject.Web.WebApi.OwinHost, Version=3.2.0.0, Culture=neutral, PublicKeyToken=c7192dc5380945e7, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Ninject.Web.WebApi.OwinHost.3.2.4.0\lib\net45-full\Ninject.Web.WebApi.OwinHost.dll</HintPath>
</Reference>
<Reference Include="Owin">
<HintPath>..\packages\Owin.1.0\lib\net40\Owin.dll</HintPath>
</Reference>
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Net.Http.Formatting, Version=5.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<Reference Include="System.Net.Http.Formatting, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Microsoft.AspNet.WebApi.Client.5.0.0\lib\net45\System.Net.Http.Formatting.dll</HintPath>
<HintPath>..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll</HintPath>
</Reference>
<Reference Include="System.Web.DynamicData" />
<Reference Include="System.Web.Entity" />
Expand All @@ -98,13 +101,13 @@
<Reference Include="System.Web.Extensions" />
<Reference Include="System.Drawing" />
<Reference Include="System.Web" />
<Reference Include="System.Web.Http, Version=5.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<Reference Include="System.Web.Http, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Microsoft.AspNet.WebApi.Core.5.0.0\lib\net45\System.Web.Http.dll</HintPath>
<HintPath>..\packages\Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45\System.Web.Http.dll</HintPath>
</Reference>
<Reference Include="System.Web.Http.Owin, Version=5.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<Reference Include="System.Web.Http.Owin, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Microsoft.AspNet.WebApi.Owin.5.0.0\lib\net45\System.Web.Http.Owin.dll</HintPath>
<HintPath>..\packages\Microsoft.AspNet.WebApi.Owin.5.2.3\lib\net45\System.Web.Http.Owin.dll</HintPath>
</Reference>
<Reference Include="System.Xml" />
<Reference Include="System.Configuration" />
Expand All @@ -116,7 +119,9 @@
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<Content Include="Web.config" />
<Content Include="Web.config">
<SubType>Designer</SubType>
</Content>
</ItemGroup>
<ItemGroup>
<Compile Include="Controllers\CounterController.cs" />
Expand Down
76 changes: 73 additions & 3 deletions BuildRevisionCounter/Controllers/CounterController.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http;
using BuildRevisionCounter.Interfaces;
Expand Down Expand Up @@ -27,6 +29,39 @@ public CounterController(IMongoDBStorage mongoDbStorage)
_mongoDbStorage = mongoDbStorage;
}

[HttpGet]
[Route("")]
[Authorize(Roles = "admin, editor, anonymous")]
public async Task<IReadOnlyCollection<RevisionModel>> GetAllRevision([FromUri] Int32 pageSize=20, [FromUri] Int32 pageNumber=1)
{
if (pageSize < 1 || pageNumber < 1)
throw new HttpResponseException(HttpStatusCode.BadRequest);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Надо просто граничить максимальный размер страницы до 1000 например.


CancellationTokenSource cts = new CancellationTokenSource();
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CancellationToken можно разместить в аргументе метода.
А для ограничения времени работы метода реализовать фильтр.
Пример есть здесь: http://stackoverflow.com/questions/24403832/timeout-a-web-api-request

var task = _mongoDbStorage.Revisions
.Find(r => true)
.Skip(pageSize * (pageNumber - 1))
.Limit(pageSize)
.ToListAsync(cts.Token);

// запустим паралельно 2 таска, второй таск в роли таймаута (30 сек)
const int timeout = 30*1000;
if (await Task.WhenAny(task, Task.Delay(timeout, cts.Token)) != task)
{
// посылаем команду прервать поток поиска
cts.Cancel();
// генерируем сообщение о том что не дождались ответа от монги
throw new HttpResponseException(HttpStatusCode.GatewayTimeout);
}
// прерываем поток таймаута
cts.Cancel();

if (task.Result == null)
throw new HttpResponseException(HttpStatusCode.NotFound);

return task.Result;
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

А если... клиент укажет pageSize = 2147483647 ?


[HttpGet]
[Route("{revisionName}")]
[Authorize(Roles = "admin, editor, anonymous")]
Expand All @@ -46,6 +81,42 @@ public async Task<long> Current([FromUri] string revisionName)
[Route("{revisionName}")]
[Authorize(Roles = "buildserver")]
public async Task<long> Bumping([FromUri] string revisionName)
{
// попробуем обновить документ
var result = await FindOneAndUpdateRevisionModelAsync(revisionName);

if (result == null)
{
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

В общем случае - плохое решение.
Данный сервис, скорее всего будет запущен в одном экземпляре.
Но, если он работает в условиях балансировки запросов, то эта блокировка останется на одном сервере.

// если не получилось, значит документ еще не был создан
// создадим его с начальным значением 0
try
{
await _mongoDbStorage.Revisions
.InsertOneAsync(new RevisionModel
{
Id = revisionName,
NextNumber = 0,
Created = DateTime.UtcNow
});
return 0;
}
catch (MongoWriteException)
{
// если прои вставке произошла ошибка значит мы не успели и запись там уже есть
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MongoWriteException может произойти по разным причинам. Пропущено определение нарушение unique индекса.

// и теперь попытка обновления должна пройти без ошибок
result = FindOneAndUpdateRevisionModelAsync(revisionName).Result;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Блокирующее ожидание в асинхронном методе.
В данном случае, если поток сюда доберется, то он тут зависнет.
Почему?

}
}

return result.NextNumber;
}

/// <summary>
/// Инкриментит каунтер в БД
/// </summary>
/// <param name="revisionName"></param>
/// <returns></returns>
private async Task<RevisionModel> FindOneAndUpdateRevisionModelAsync(string revisionName)
{
var result = await _mongoDbStorage.Revisions
.FindOneAndUpdateAsync<RevisionModel>(
Expand All @@ -56,12 +127,11 @@ public async Task<long> Bumping([FromUri] string revisionName)
.Set(r => r.Updated, DateTime.UtcNow),
new FindOneAndUpdateOptions<RevisionModel>
{
IsUpsert = true,
IsUpsert = false,
ReturnDocument = ReturnDocument.After
});


return result.NextNumber;
return result;
}
}
}
Loading