Skip to content

Commit f564ef9

Browse files
authored
Add Cosmos bulk execution (#37033)
Adds BulkExecutionEnabled to CosmosDbContextOptionsBuilder Execute non batchable document writes in parallel when bulk execution enabled Add a warning exception when batching is enabled when using bulk execution Fixes #36599
1 parent bbf6760 commit f564ef9

26 files changed

+684
-183
lines changed
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
namespace Microsoft.EntityFrameworkCore.Cosmos.Diagnostics;
5+
6+
/// <summary>
7+
/// A <see cref="DiagnosticSource" /> event payload class for Cosmos events related to AutoTransactionBehavior
8+
/// </summary>
9+
/// <remarks>
10+
/// See <see href="https://aka.ms/efcore-docs-diagnostics">Logging, events, and diagnostics</see> for more information and examples.
11+
/// </remarks>
12+
public class AutoTransactionBehaviorEventData : EventData
13+
{
14+
/// <summary>
15+
/// Constructs the event payload.
16+
/// </summary>
17+
/// <param name="eventDefinition">The event definition.</param>
18+
/// <param name="messageGenerator">A delegate that generates a log message for this event.</param>
19+
/// <param name="autoTransactionBehavior">The AutoTransactionBehavior that was used.</param>
20+
public AutoTransactionBehaviorEventData(
21+
EventDefinitionBase eventDefinition,
22+
Func<EventDefinitionBase, EventData, string> messageGenerator,
23+
AutoTransactionBehavior autoTransactionBehavior) : base(eventDefinition, messageGenerator)
24+
{
25+
AutoTransactionBehavior = autoTransactionBehavior;
26+
}
27+
28+
/// <summary>
29+
/// The AutoTransactionBehavior that was used.
30+
/// </summary>
31+
public virtual AutoTransactionBehavior AutoTransactionBehavior { get; }
32+
}

src/EFCore.Cosmos/Diagnostics/CosmosEventId.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ private enum Id
4040

4141
// Update events
4242
PrimaryKeyValueNotSet = CoreEventId.ProviderBaseId + 200,
43+
BulkExecutionWithTransactionalBatch,
4344

4445
// Model validation events
4546
NoPartitionKeyDefined = CoreEventId.ProviderBaseId + 600,
@@ -193,4 +194,19 @@ private static EventId MakeUpdateId(Id id)
193194
/// </para>
194195
/// </remarks>
195196
public static readonly EventId PrimaryKeyValueNotSet = MakeUpdateId(Id.PrimaryKeyValueNotSet);
197+
198+
/// <summary>
199+
/// SaveChanges was invoked with both bulk execution and batching being enabled. Transactional batches can not be run in bulk thus they
200+
/// will skip bulk execution. Use AutoTransactionBehavior.Never to leverage bulk execution. If batching was intended, suppress this warning
201+
/// using <c>DbContextOptionsBuilder.ConfigureWarnings(w => w.Ignore(CosmosEventId.BulkExecutionWithTransactionalBatch))</c>
202+
/// </summary>
203+
/// <remarks>
204+
/// <para>
205+
/// This event is in the <see cref="DbLoggerCategory.Update" /> category.
206+
/// </para>
207+
/// <para>
208+
/// This event uses the <see cref="Microsoft.EntityFrameworkCore.Cosmos.Diagnostics.AutoTransactionBehaviorEventData" /> payload when used with a <see cref="DiagnosticSource" />.
209+
/// </para>
210+
/// </remarks>
211+
public static readonly EventId BulkExecutionWithTransactionalBatch = MakeUpdateId(Id.BulkExecutionWithTransactionalBatch);
196212
}

src/EFCore.Cosmos/Diagnostics/CosmosTransactionalBatchExecutedEventData.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4-
using System.Collections.Generic;
5-
using Microsoft.EntityFrameworkCore.Cosmos.Storage.Internal;
6-
74
namespace Microsoft.EntityFrameworkCore.Diagnostics;
85

96
/// <summary>
10-
/// A <see cref="DiagnosticSource" /> event payload class for Cosmos item command executed events.
7+
/// A <see cref="DiagnosticSource" /> event payload class for Cosmos transactional batch executed events.
118
/// </summary>
129
/// <remarks>
1310
/// See <see href="https://aka.ms/efcore-docs-diagnostics">Logging, events, and diagnostics</see> for more information and examples.

src/EFCore.Cosmos/Diagnostics/Internal/CosmosLoggerExtensions.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -547,6 +547,33 @@ public static void PrimaryKeyValueNotSet(
547547
}
548548
}
549549

550+
/// <summary>
551+
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
552+
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
553+
/// any release. You should only use it directly in your code with extreme caution and knowing that
554+
/// doing so can result in application failures when updating to a new Entity Framework Core release.
555+
/// </summary>
556+
public static void BulkExecutionWithTransactionalBatch(
557+
this IDiagnosticsLogger<DbLoggerCategory.Update> diagnostics,
558+
AutoTransactionBehavior autoTransactionBehavior)
559+
{
560+
var definition = CosmosResources.LogBulkExecutionWithTransactionalBatch(diagnostics);
561+
562+
if (diagnostics.ShouldLog(definition))
563+
{
564+
definition.Log(diagnostics);
565+
}
566+
567+
if (diagnostics.NeedsEventData(definition, out var diagnosticSourceEnabled, out var simpleLogEnabled))
568+
{
569+
var eventData = new AutoTransactionBehaviorEventData(
570+
definition,
571+
(d, b) => ((EventDefinition)d).GenerateMessage(),
572+
autoTransactionBehavior);
573+
diagnostics.DispatchEventData(definition, eventData, diagnosticSourceEnabled, simpleLogEnabled);
574+
}
575+
}
576+
550577
private static string FormatParameters(IReadOnlyList<(string Name, object? Value)> parameters, bool shouldLogParameterValues)
551578
=> FormatParameters(parameters.Select(p => new SqlParameter(p.Name, p.Value)).ToList(), shouldLogParameterValues);
552579

src/EFCore.Cosmos/Diagnostics/Internal/CosmosLoggingDefinitions.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,4 +90,12 @@ public class CosmosLoggingDefinitions : LoggingDefinitions
9090
/// doing so can result in application failures when updating to a new Entity Framework Core release.
9191
/// </summary>
9292
public EventDefinitionBase? LogPrimaryKeyValueNotSet;
93+
94+
/// <summary>
95+
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
96+
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
97+
/// any release. You should only use it directly in your code with extreme caution and knowing that
98+
/// doing so can result in application failures when updating to a new Entity Framework Core release.
99+
/// </summary>
100+
public EventDefinitionBase? LogBulkExecutionWithTransactionalBatch;
93101
}

src/EFCore.Cosmos/Extensions/CosmosDbContextOptionsExtensions.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ public static DbContextOptionsBuilder UseCosmos(
5252
Check.NotNull(optionsBuilder);
5353
Check.NotNull(cosmosOptionsAction);
5454

55+
ConfigureWarnings(optionsBuilder);
56+
5557
cosmosOptionsAction.Invoke(new CosmosDbContextOptionsBuilder(optionsBuilder));
5658

5759
return optionsBuilder;
@@ -118,6 +120,8 @@ public static DbContextOptionsBuilder UseCosmos(
118120
.WithAccountKey(accountKey)
119121
.WithDatabaseName(databaseName);
120122

123+
ConfigureWarnings(optionsBuilder);
124+
121125
((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension);
122126

123127
cosmosOptionsAction?.Invoke(new CosmosDbContextOptionsBuilder(optionsBuilder));
@@ -186,6 +190,8 @@ public static DbContextOptionsBuilder UseCosmos(
186190
.WithTokenCredential(tokenCredential)
187191
.WithDatabaseName(databaseName);
188192

193+
ConfigureWarnings(optionsBuilder);
194+
189195
((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension);
190196

191197
cosmosOptionsAction?.Invoke(new CosmosDbContextOptionsBuilder(optionsBuilder));
@@ -247,6 +253,8 @@ public static DbContextOptionsBuilder UseCosmos(
247253
.WithConnectionString(connectionString)
248254
.WithDatabaseName(databaseName);
249255

256+
ConfigureWarnings(optionsBuilder);
257+
250258
((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension);
251259

252260
cosmosOptionsAction?.Invoke(new CosmosDbContextOptionsBuilder(optionsBuilder));
@@ -260,6 +268,10 @@ var coreOptionsExtension
260268
= optionsBuilder.Options.FindExtension<CoreOptionsExtension>()
261269
?? new CoreOptionsExtension();
262270

271+
coreOptionsExtension = coreOptionsExtension.WithWarningsConfiguration(
272+
coreOptionsExtension.WarningsConfiguration.TryWithExplicit(
273+
CosmosEventId.BulkExecutionWithTransactionalBatch, WarningBehavior.Throw));
274+
263275
((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(coreOptionsExtension);
264276
}
265277
}

src/EFCore.Cosmos/Infrastructure/CosmosDbContextOptionsBuilder.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,22 @@ public virtual CosmosDbContextOptionsBuilder ContentResponseOnWriteEnabled(bool
229229
public virtual CosmosDbContextOptionsBuilder SessionTokenManagementMode(SessionTokenManagementMode mode)
230230
=> WithOption(e => e.WithSessionTokenManagementMode(mode));
231231

232+
/// <summary>
233+
/// Sets the boolean to enable the <see href="https://learn.microsoft.com/en-us/azure/cosmos-db/bulk-executor-overview">Cosmos DB SDK bulk execution feature</see>.
234+
/// Enabling this feature can improve throughput for small write operations but may increase latency.
235+
/// It is recommended only for high-throughput, non-latency-sensitive scenarios.
236+
/// Because <see href="https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/transactional-batch">Transactional Batches</see> cannot be executed in bulk mode,
237+
/// any operations batched by EF will not use bulk execution. To ensure operations are executed in bulk, consider disabling batching by setting <see cref="AutoTransactionBehavior.Never"/>.
238+
/// For more information, see <see href="https://learn.microsoft.com/en-us/ef/core/providers/cosmos/saving">Saving Data - Azure Cosmos DB Provider</see>.
239+
/// </summary>
240+
/// <remarks>
241+
/// See <see href="https://aka.ms/efcore-docs-dbcontext-options">Using DbContextOptions</see>, and
242+
/// <see href="https://aka.ms/efcore-docs-cosmos">Accessing Azure Cosmos DB with EF Core</see> for more information and examples.
243+
/// </remarks>
244+
/// <param name="enabled"><see langword="true" /> to enable the Cosmos DB SDK bulk feature.</param>
245+
public virtual CosmosDbContextOptionsBuilder BulkExecutionEnabled(bool enabled = true)
246+
=> WithOption(e => e.BulkExecutionEnabled(enabled));
247+
232248
/// <summary>
233249
/// Sets an option by cloning the extension used to store the settings. This ensures the builder
234250
/// does not modify options that are already in use elsewhere.

src/EFCore.Cosmos/Infrastructure/Internal/CosmosDbOptionExtension.cs

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ public class CosmosOptionsExtension : IDbContextOptionsExtension
3737
private DbContextOptionsExtensionInfo? _info;
3838
private Func<HttpClient>? _httpClientFactory;
3939
private SessionTokenManagementMode _sessionTokenManagementMode = SessionTokenManagementMode.FullyAutomatic;
40+
private bool? _enableBulkExecution;
4041

4142
/// <summary>
4243
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -75,6 +76,7 @@ protected CosmosOptionsExtension(CosmosOptionsExtension copyFrom)
7576
_maxRequestsPerTcpConnection = copyFrom._maxRequestsPerTcpConnection;
7677
_httpClientFactory = copyFrom._httpClientFactory;
7778
_sessionTokenManagementMode = copyFrom._sessionTokenManagementMode;
79+
_enableBulkExecution = copyFrom._enableBulkExecution;
7880
}
7981

8082
/// <summary>
@@ -590,6 +592,29 @@ public virtual CosmosOptionsExtension WithSessionTokenManagementMode(SessionToke
590592
return clone;
591593
}
592594

595+
/// <summary>
596+
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
597+
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
598+
/// any release. You should only use it directly in your code with extreme caution and knowing that
599+
/// doing so can result in application failures when updating to a new Entity Framework Core release.
600+
/// </summary>
601+
public virtual bool? EnableBulkExecution => _enableBulkExecution;
602+
603+
/// <summary>
604+
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
605+
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
606+
/// any release. You should only use it directly in your code with extreme caution and knowing that
607+
/// doing so can result in application failures when updating to a new Entity Framework Core release.
608+
/// </summary>
609+
public virtual CosmosOptionsExtension BulkExecutionEnabled(bool enabled)
610+
{
611+
var clone = Clone();
612+
613+
clone._enableBulkExecution = enabled;
614+
615+
return clone;
616+
}
617+
593618
/// <summary>
594619
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
595620
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
@@ -659,6 +684,7 @@ public override int GetServiceProviderHashCode()
659684
hashCode.Add(Extension._maxRequestsPerTcpConnection);
660685
hashCode.Add(Extension._httpClientFactory);
661686
hashCode.Add(Extension._sessionTokenManagementMode);
687+
hashCode.Add(Extension._enableBulkExecution);
662688

663689
_serviceProviderHash = hashCode.ToHashCode();
664690
}
@@ -684,7 +710,8 @@ public override bool ShouldUseSameServiceProvider(DbContextOptionsExtensionInfo
684710
&& Extension._maxTcpConnectionsPerEndpoint == otherInfo.Extension._maxTcpConnectionsPerEndpoint
685711
&& Extension._maxRequestsPerTcpConnection == otherInfo.Extension._maxRequestsPerTcpConnection
686712
&& Extension._httpClientFactory == otherInfo.Extension._httpClientFactory
687-
&& Extension._sessionTokenManagementMode == otherInfo.Extension._sessionTokenManagementMode;
713+
&& Extension._sessionTokenManagementMode == otherInfo.Extension._sessionTokenManagementMode
714+
&& Extension._enableBulkExecution == otherInfo.Extension._enableBulkExecution;
688715

689716
public override void PopulateDebugInfo(IDictionary<string, string> debugInfo)
690717
{

src/EFCore.Cosmos/Infrastructure/Internal/CosmosSingletonOptions.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ public class CosmosSingletonOptions : ICosmosSingletonOptions
157157
/// any release. You should only use it directly in your code with extreme caution and knowing that
158158
/// doing so can result in application failures when updating to a new Entity Framework Core release.
159159
/// </summary>
160-
public virtual SessionTokenManagementMode SessionTokenManagementMode { get; private set; }
160+
public virtual bool? EnableBulkExecution { get; private set; }
161161

162162
/// <summary>
163163
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -186,6 +186,7 @@ public virtual void Initialize(IDbContextOptions options)
186186
MaxTcpConnectionsPerEndpoint = cosmosOptions.MaxTcpConnectionsPerEndpoint;
187187
MaxRequestsPerTcpConnection = cosmosOptions.MaxRequestsPerTcpConnection;
188188
HttpClientFactory = cosmosOptions.HttpClientFactory;
189+
EnableBulkExecution = cosmosOptions.EnableBulkExecution;
189190
}
190191
}
191192

@@ -216,6 +217,7 @@ public virtual void Validate(IDbContextOptions options)
216217
|| MaxTcpConnectionsPerEndpoint != cosmosOptions.MaxTcpConnectionsPerEndpoint
217218
|| MaxRequestsPerTcpConnection != cosmosOptions.MaxRequestsPerTcpConnection
218219
|| HttpClientFactory != cosmosOptions.HttpClientFactory
220+
|| EnableBulkExecution != cosmosOptions.EnableBulkExecution
219221
))
220222
{
221223
throw new InvalidOperationException(

src/EFCore.Cosmos/Infrastructure/Internal/ICosmosSingletonOptions.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,4 +155,12 @@ public interface ICosmosSingletonOptions : ISingletonOptions
155155
/// doing so can result in application failures when updating to a new Entity Framework Core release.
156156
/// </summary>
157157
Func<HttpClient>? HttpClientFactory { get; }
158+
159+
/// <summary>
160+
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
161+
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
162+
/// any release. You should only use it directly in your code with extreme caution and knowing that
163+
/// doing so can result in application failures when updating to a new Entity Framework Core release.
164+
/// </summary>
165+
bool? EnableBulkExecution { get; }
158166
}

0 commit comments

Comments
 (0)