forked from ravendb/ravendb
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Parallel shard access using .NET 4.0 Tasks
- Loading branch information
Showing
4 changed files
with
124 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
58 changes: 58 additions & 0 deletions
58
Raven.Client.Tests/Shard/When_Using_Parallel_Access_Strategy.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
using System; | ||
using System.IO; | ||
using System.Reflection; | ||
using System.Threading; | ||
using Raven.Database; | ||
using Raven.Server; | ||
using Xunit; | ||
using System.Collections.Generic; | ||
using Raven.Client.Shard; | ||
using Rhino.Mocks; | ||
using Raven.Client.Shard.ShardStrategy.ShardAccess; | ||
|
||
namespace Raven.Client.Tests | ||
{ | ||
public class When_Using_Parallel_Access_Strategy : BaseTest | ||
{ | ||
[Fact] | ||
public void Can_get_complete_result_list() | ||
{ | ||
var shard1 = MockRepository.GenerateStub<IDocumentSession>(); | ||
shard1.Stub(x => x.GetAll<Company>()) | ||
//.Callback(() => { Thread.Sleep(500); return true; }) | ||
.Return(new[] { new Company { Name = "Company1" } }); | ||
|
||
var shard2 = MockRepository.GenerateStub<IDocumentSession>(); | ||
shard2.Stub(x => x.GetAll<Company>()) | ||
//.Callback(() => { Thread.Sleep(100); return true; }) | ||
.Return(new[] { new Company { Name = "Company2" } }); | ||
|
||
var results = new ParallelShardAccessStrategy().Apply(new[] { shard1, shard2 }, x => x.GetAll<Company>()); | ||
|
||
Assert.Equal(2, results.Count); | ||
} | ||
|
||
[Fact] | ||
public void Null_result_is_not_an_exception() | ||
{ | ||
var shard1 = MockRepository.GenerateStub<IDocumentSession>(); | ||
shard1.Stub(x => x.GetAll<Company>()).Return(null); | ||
|
||
var results = new ParallelShardAccessStrategy().Apply(new[] { shard1 }, x => x.GetAll<Company>()); | ||
|
||
Assert.Equal(0, results.Count); | ||
} | ||
|
||
[Fact] | ||
public void Execution_exceptions_are_rethrown() | ||
{ | ||
var shard1 = MockRepository.GenerateStub<IDocumentSession>(); | ||
shard1.Stub(x => x.GetAll<Company>()).Throw(new ApplicationException("Oh noes!")); | ||
|
||
Assert.Throws(typeof(ApplicationException), () => | ||
{ | ||
new ParallelShardAccessStrategy().Apply(new[] { shard1 }, x => x.GetAll<Company>()); | ||
}); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
64 changes: 64 additions & 0 deletions
64
Raven.Client/Shard/ShardStrategy/ShardAccess/ParallelShardAccessStrategy.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
|
||
namespace Raven.Client.Shard.ShardStrategy.ShardAccess | ||
{ | ||
public class ParallelShardAccessStrategy: IShardAccessStrategy | ||
{ | ||
public IList<T> Apply<T>(IList<IDocumentSession> shardSessions, Func<IDocumentSession, IList<T>> operation) | ||
{ | ||
var returnList = new List<T>(); | ||
|
||
//List.AddRange not threadsafe, make sure addrange calls don't happen concurrently | ||
object lockObject = new object(); | ||
|
||
shardSessions | ||
.Select(shardSession => | ||
Task.Factory | ||
.StartNew(() => operation(shardSession)) | ||
.AddToListOnComplete(lockObject, returnList) | ||
) | ||
.WaitAll() | ||
; | ||
|
||
return returnList; | ||
} | ||
} | ||
|
||
internal static class ParallelExtensions | ||
{ | ||
public static Task AddToListOnComplete<T>(this Task<IList<T>> task, object lockObject, List<T> returnList) | ||
{ | ||
return task.ContinueWith(x => { | ||
lock (lockObject) | ||
{ | ||
if (x.Result != null) | ||
returnList.AddRange(x.Result); | ||
} | ||
}); | ||
} | ||
|
||
public static void WaitAll(this IEnumerable<Task> tasks) | ||
{ | ||
try | ||
{ | ||
Task.WaitAll(tasks.ToArray()); | ||
} | ||
catch (Exception ex) | ||
{ | ||
//when task takes exception it wraps in aggregate exception, if in continuation | ||
//then could be double wrapped, etc. This should always get us the original | ||
while (true) | ||
{ | ||
if (ex.InnerException == null || !(ex is AggregateException)) | ||
throw ex; | ||
else | ||
ex = ex.InnerException; | ||
} | ||
} | ||
} | ||
} | ||
} |