Skip to content

Commit 48ce9e5

Browse files
RiverPhillipsasosMikeGore
authored andcommitted
Allow Initialise with shared throughput (#40)
* Update to use Microsoft.Azure.DocumentDb 2.4.0 * Use Microsoft.Azure.DocumentDB for .net 4.5.2 * Refactor to remove unnescessary awaits and usings * Revert "Refactor to remove unnescessary awaits and usings" This reverts commit e2a974f. * Allow setting the cosmos throughput at a database level Validate Collection and Database requests units are compatible Set throughput of database and collection when StorageEngine is initialised Update readme to reflect provisioning database with shared throughput Update to latest cosmos libraries (#39) * Update to use Microsoft.Azure.DocumentDb 2.4.0 * Use Microsoft.Azure.DocumentDB for .net 4.5.2 * Refactor to remove unnescessary awaits and usings * Revert "Refactor to remove unnescessary awaits and usings" This reverts commit e2a974f. Allowing setting throughput at either the database or the collection level * Remove unnecessary awaits * Add CONTRIBUTING.md
1 parent 801f630 commit 48ce9e5

12 files changed

Lines changed: 237 additions & 41 deletions

CONTRIBUTING.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Contributing
2+
3+
## Building Simple Event Store
4+
Clone repository
5+
```sh
6+
git clone git@github.com:ASOS/SimpleEventStore.git
7+
```
8+
9+
Enter the repository and build the provider and run the tests using cake
10+
```sh
11+
cd build
12+
./build.ps1
13+
```

README.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,11 @@ DocumentClient client; // Set this up as required
4545
4646
// If UseCollection isn't specified, sensible defaults for development are used.
4747
// If UseSubscriptions isn't supplied the subscription feature is disabled.
48+
// If UseSharedThroughput isn't supplied the throughput is set only at a collection level
4849
return await new AzureDocumentDbStorageEngineBuilder(client, databaseName)
50+
.UseSharedThroughput(o => {
51+
o.DatabaseRequestUnits = 400;
52+
})
4953
.UseCollection(o =>
5054
{
5155
o.ConsistencyLevel = consistencyLevelEnum;
@@ -64,10 +68,17 @@ return await new AzureDocumentDbStorageEngineBuilder(client, databaseName)
6468
.Build()
6569
.Initialise();
6670
```
71+
### Database Options
72+
Use this only if you want throughput to be set at a database level
73+
74+
Allows you to specify
75+
- The number of RUs for the database
76+
77+
6778
### CollectionOptions
6879
Allows you to specify
6980
- The consistency level of the database
70-
- The default number of RUs when the collection is created
81+
- The number of RUs for the collection - if throughput is set at a database level this cannot be greater than database throughput
7182
- The collection name
7283

7384
Only use one of the following consistency levels

src/SimpleEventStore/SimpleEventStore.AzureDocumentDb.Tests/AzureDocumentDBEventStoreInitializing.cs

Lines changed: 84 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,24 +13,24 @@ namespace SimpleEventStore.AzureDocumentDb.Tests
1313
public class AzureDocumentDBEventStoreInitializing
1414
{
1515
private const string DatabaseName = "InitializeTests";
16+
private readonly Uri databaseUri = UriFactory.CreateDatabaseUri(DatabaseName);
17+
private readonly DocumentClient client = DocumentClientFactory.Create();
1618

17-
[OneTimeTearDown]
18-
public async Task TearDownDatabase()
19+
[TearDown]
20+
public Task TearDownDatabase()
1921
{
20-
var client = DocumentClientFactory.Create(DatabaseName);
21-
await client.DeleteDatabaseAsync(UriFactory.CreateDatabaseUri(DatabaseName));
22+
return client.DeleteDatabaseAsync(databaseUri);
2223
}
2324

2425
[Test]
2526
public async Task when_initializing_all_expected_resources_are_created()
2627
{
27-
var client = DocumentClientFactory.Create(DatabaseName);
2828
var collectionName = "AllExpectedResourcesAreCreated_" + Guid.NewGuid();
2929
var storageEngine = await StorageEngineFactory.Create(DatabaseName, o => o.CollectionName = collectionName);
30-
30+
3131
await storageEngine.Initialise();
3232

33-
var database = (await client.ReadDatabaseAsync(UriFactory.CreateDatabaseUri(DatabaseName))).Resource;
33+
var database = (await client.ReadDatabaseAsync(databaseUri)).Resource;
3434
var collection = (await client.ReadDocumentCollectionAsync(UriFactory.CreateDocumentCollectionUri(DatabaseName, collectionName))).Resource;
3535
var storedProcedure = (await client.ReadStoredProcedureAsync(UriFactory.CreateStoredProcedureUri(DatabaseName, collectionName, TestConstants.AppendStoredProcedureName))).Resource;
3636
var offer = client.CreateOfferQuery()
@@ -55,17 +55,91 @@ public async Task when_initializing_with_a_time_to_live_it_is_set()
5555
{
5656
var ttl = 60;
5757
var collectionName = "TimeToLiveIsSet_" + Guid.NewGuid();
58-
var client = DocumentClientFactory.Create(DatabaseName);
59-
var storageEngine = await StorageEngineFactory.Create(DatabaseName, o =>
58+
var storageEngine = await StorageEngineFactory.Create(DatabaseName, o =>
6059
{
6160
o.CollectionName = collectionName;
6261
o.DefaultTimeToLive = ttl;
6362
});
6463

6564
await storageEngine.Initialise();
6665

67-
var collection = (await client.ReadDocumentCollectionAsync(UriFactory.CreateDocumentCollectionUri(DatabaseName, collectionName))).Resource;
66+
var collection =
67+
(await client.ReadDocumentCollectionAsync(
68+
UriFactory.CreateDocumentCollectionUri(DatabaseName, collectionName))).Resource;
6869
Assert.That(collection.DefaultTimeToLive, Is.EqualTo(ttl));
6970
}
71+
72+
[Test]
73+
public async Task when_using_shared_throughput_it_is_set_at_a_database_level()
74+
{
75+
const int throughput = 800;
76+
var collectionName = "SharedCollection_" + Guid.NewGuid();
77+
78+
var storageEngine = await StorageEngineFactory.Create(DatabaseName,
79+
collectionOptions =>
80+
{
81+
collectionOptions.CollectionName = collectionName;
82+
collectionOptions.CollectionRequestUnits = null;
83+
},
84+
databaseOptions => { databaseOptions.DatabaseRequestUnits = throughput; });
85+
86+
await storageEngine.Initialise();
87+
88+
Assert.AreEqual(throughput, await GetDatabaseThroughput());
89+
}
90+
91+
[Test]
92+
public async Task when_throughput_is_set_offer_is_updated()
93+
{
94+
var dbThroughput = 800;
95+
var collectionThroughput = 400;
96+
var collectionName = "UpdateThroughput_" + Guid.NewGuid();
97+
98+
await InitialiseStorageEngine(collectionName, collectionThroughput, dbThroughput);
99+
100+
Assert.AreEqual(dbThroughput, await GetDatabaseThroughput());
101+
Assert.AreEqual(collectionThroughput, await GetCollectionThroughput(collectionName));
102+
103+
dbThroughput = 1600;
104+
collectionThroughput = 800;
105+
106+
await InitialiseStorageEngine(collectionName, collectionThroughput, dbThroughput);
107+
108+
Assert.AreEqual(dbThroughput, await GetDatabaseThroughput());
109+
Assert.AreEqual(collectionThroughput, await GetCollectionThroughput(collectionName));
110+
}
111+
112+
private static async Task InitialiseStorageEngine(string collectionName, int collectionThroughput,
113+
int dbThroughput)
114+
{
115+
var storageEngine = await StorageEngineFactory.Create(DatabaseName,
116+
collectionOptions =>
117+
{
118+
collectionOptions.CollectionName = collectionName;
119+
collectionOptions.CollectionRequestUnits = collectionThroughput;
120+
},
121+
databaseOptions => { databaseOptions.DatabaseRequestUnits = dbThroughput; });
122+
123+
await storageEngine.Initialise();
124+
}
125+
126+
public async Task<int> GetCollectionThroughput(string collectionName)
127+
{
128+
var collection = await client.ReadDocumentCollectionAsync(UriFactory.CreateDocumentCollectionUri(DatabaseName, collectionName));
129+
130+
var collectionOffer = client.CreateOfferQuery().Where(x => x.ResourceLink == collection.Resource.SelfLink)
131+
.AsEnumerable().First();
132+
133+
return ((OfferV2)collectionOffer).Content.OfferThroughput;
134+
}
135+
136+
public async Task<int> GetDatabaseThroughput()
137+
{
138+
var db = await client.ReadDatabaseAsync(databaseUri);
139+
var dbOffer = client.CreateOfferQuery().Where(x => x.ResourceLink == db.Resource.SelfLink).AsEnumerable()
140+
.First();
141+
142+
return ((OfferV2)dbOffer).Content.OfferThroughput;
143+
}
70144
}
71145
}

src/SimpleEventStore/SimpleEventStore.AzureDocumentDb.Tests/AzureDocumentDbEventStoreLogging.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@ public async Task when_a_write_operation_is_successful_the_log_callback_is_calle
2121
await sut.AppendToStream(streamId, 0, new EventData(Guid.NewGuid(), new OrderCreated("TEST-ORDER")));
2222

2323
Assert.NotNull(response);
24-
TestContext.Out.WriteLine($"Charge: {response.RequestCharge}");
25-
TestContext.Out.WriteLine($"Quota Usage: {response.CurrentResourceQuotaUsage}");
26-
TestContext.Out.WriteLine($"Max Resource Quote: {response.MaxResourceQuota}");
27-
TestContext.Out.WriteLine($"Response headers: {response.ResponseHeaders}");
24+
await TestContext.Out.WriteLineAsync($"Charge: {response.RequestCharge}");
25+
await TestContext.Out.WriteLineAsync($"Quota Usage: {response.CurrentResourceQuotaUsage}");
26+
await TestContext.Out.WriteLineAsync($"Max Resource Quote: {response.MaxResourceQuota}");
27+
await TestContext.Out.WriteLineAsync($"Response headers: {response.ResponseHeaders}");
2828
}
2929

3030
[Test]
@@ -40,7 +40,7 @@ public async Task when_a_read_operation_is_successful_the_log_callback_is_called
4040
Assert.That(logCount, Is.EqualTo(2));
4141
}
4242

43-
private static async Task<IStorageEngine> CreateStorageEngine(Action<ResponseInformation> onSuccessCallback, string databaseName = "LoggingTests")
43+
private static Task<IStorageEngine> CreateStorageEngine(Action<ResponseInformation> onSuccessCallback, string databaseName = "LoggingTests")
4444
{
4545
var config = new ConfigurationBuilder()
4646
.AddJsonFile("appsettings.json")
@@ -57,7 +57,7 @@ private static async Task<IStorageEngine> CreateStorageEngine(Action<ResponseInf
5757

5858
DocumentClient client = new DocumentClient(new Uri(documentDbUri), authKey);
5959

60-
return await new AzureDocumentDbStorageEngineBuilder(client, databaseName)
60+
return new AzureDocumentDbStorageEngineBuilder(client, databaseName)
6161
.UseCollection(o =>
6262
{
6363
o.ConsistencyLevel = consistencyLevelEnum;

src/SimpleEventStore/SimpleEventStore.AzureDocumentDb.Tests/AzureDocumentDbEventStoreReadingPartiallyDeletedStreams.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public async Task when_reading_a_stream_that_has_deleted_events_the_stream_can_s
1717
const string databaseName = "ReadingPartialStreamTests";
1818
const string collectionName = "Commits";
1919

20-
var client = DocumentClientFactory.Create(databaseName);
20+
var client = DocumentClientFactory.Create();
2121
var storageEngine = await StorageEngineFactory.Create(databaseName, o => o.CollectionName = collectionName);
2222
var eventStore = new EventStore(storageEngine);
2323
var streamId = Guid.NewGuid().ToString();
@@ -31,9 +31,9 @@ public async Task when_reading_a_stream_that_has_deleted_events_the_stream_can_s
3131
Assert.That(stream.First().EventNumber, Is.EqualTo(2));
3232
}
3333

34-
private static async Task SimulateTimeToLiveExpiration(string databaseName, string collectionName, DocumentClient client, string streamId)
34+
private static Task SimulateTimeToLiveExpiration(string databaseName, string collectionName, DocumentClient client, string streamId)
3535
{
36-
await client.DeleteDocumentAsync(
36+
return client.DeleteDocumentAsync(
3737
UriFactory.CreateDocumentUri(databaseName, collectionName, $"{streamId}:1"),
3838
new RequestOptions() { PartitionKey = new PartitionKey(streamId) });
3939
}

src/SimpleEventStore/SimpleEventStore.AzureDocumentDb.Tests/AzureDocumentDbStorageEngineBuilderTests.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,26 @@ public void when_setting_the_jsonserializationsettings_it_must_be_supplied()
5454
Assert.Throws<ArgumentNullException>(() => builder.UseJsonSerializerSettings(null));
5555
}
5656

57+
[Test]
58+
public void throughput_must_be_set_in_one_location()
59+
{
60+
var builder = new AzureDocumentDbStorageEngineBuilder(CreateClient(), "Test")
61+
.UseSharedThroughput(o => { o.DatabaseRequestUnits = null; })
62+
.UseCollection(o => o.CollectionRequestUnits = null);
63+
64+
Assert.Throws<ArgumentException>(() => builder.Build());
65+
}
66+
67+
[Test]
68+
public void collection_throughput_cannot_be_greater_than_database_throughput()
69+
{
70+
var builder = new AzureDocumentDbStorageEngineBuilder(CreateClient(), "Test")
71+
.UseSharedThroughput(o => { o.DatabaseRequestUnits = 400; })
72+
.UseCollection(o => o.CollectionRequestUnits = 500);
73+
74+
Assert.Throws<ArgumentException>(() => builder.Build());
75+
}
76+
5777
private static DocumentClient CreateClient()
5878
{
5979
var client = new DocumentClient(new Uri("https://localhost:8081/"), "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==");

src/SimpleEventStore/SimpleEventStore.AzureDocumentDb.Tests/DocumentClientFactory.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@ namespace SimpleEventStore.AzureDocumentDb.Tests
77
{
88
internal static class DocumentClientFactory
99
{
10-
internal static DocumentClient Create(string databaseName)
10+
internal static DocumentClient Create()
1111
{
12-
return Create(databaseName, new JsonSerializerSettings());
12+
return Create(new JsonSerializerSettings());
1313
}
1414

15-
internal static DocumentClient Create(string databaseName, JsonSerializerSettings settings)
15+
internal static DocumentClient Create(JsonSerializerSettings settings)
1616
{
1717
var config = new ConfigurationBuilder()
1818
.AddJsonFile("appsettings.json")

src/SimpleEventStore/SimpleEventStore.AzureDocumentDb.Tests/StorageEngineFactory.cs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@ namespace SimpleEventStore.AzureDocumentDb.Tests
1010
{
1111
internal static class StorageEngineFactory
1212
{
13-
internal static async Task<IStorageEngine> Create(string databaseName, Action<CollectionOptions> collectionOverrides = null)
13+
internal static Task<IStorageEngine> Create(string databaseName, Action<CollectionOptions> collectionOverrides = null, Action<DatabaseOptions> databaseOverrides = null)
1414
{
15-
return await Create(databaseName, new JsonSerializerSettings(), collectionOverrides);
15+
return Create(databaseName, new JsonSerializerSettings(), collectionOverrides, databaseOverrides);
1616
}
1717

18-
internal static async Task<IStorageEngine> Create(string databaseName, JsonSerializerSettings settings, Action<CollectionOptions> collectionOverrides = null)
18+
internal static Task<IStorageEngine> Create(string databaseName, JsonSerializerSettings settings, Action<CollectionOptions> collectionOverrides = null, Action<DatabaseOptions> databaseOverrides = null)
1919
{
2020
var config = new ConfigurationBuilder()
2121
.AddJsonFile("appsettings.json")
@@ -24,19 +24,23 @@ internal static async Task<IStorageEngine> Create(string databaseName, JsonSeria
2424
var consistencyLevel = config["ConsistencyLevel"];
2525
ConsistencyLevel consistencyLevelEnum;
2626

27-
if(!Enum.TryParse(consistencyLevel, true, out consistencyLevelEnum))
27+
if (!Enum.TryParse(consistencyLevel, true, out consistencyLevelEnum))
2828
{
2929
throw new Exception($"The ConsistencyLevel value {consistencyLevel} is not supported");
3030
}
3131

32-
var client = DocumentClientFactory.Create(databaseName, settings);
32+
var client = DocumentClientFactory.Create(settings);
3333

34-
return await new AzureDocumentDbStorageEngineBuilder(client, databaseName)
34+
return new AzureDocumentDbStorageEngineBuilder(client, databaseName)
35+
.UseSharedThroughput(o =>
36+
{
37+
databaseOverrides?.Invoke(o);
38+
})
3539
.UseCollection(o =>
3640
{
3741
o.ConsistencyLevel = consistencyLevelEnum;
3842
o.CollectionRequestUnits = TestConstants.RequestUnits;
39-
if(collectionOverrides != null) collectionOverrides(o);
43+
if (collectionOverrides != null) collectionOverrides(o);
4044
})
4145
.UseTypeMap(new ConfigurableSerializationTypeMap()
4246
.RegisterTypes(

0 commit comments

Comments
 (0)