Skip to content

Commit

Permalink
Reimplement MaxBatchSize as a pre-check
Browse files Browse the repository at this point in the history
For better perf on SQLite (#27681)

Also moves the handling of MaxBatchSize to
ReaderModificationCommandBatch and does some cleanup.
  • Loading branch information
roji committed Mar 24, 2022
1 parent 12bcdbd commit 5b0552e
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 22 deletions.
4 changes: 1 addition & 3 deletions src/EFCore.Relational/Update/ModificationCommandBatch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,5 @@ public abstract class ModificationCommandBatch
/// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
/// <returns>A task that represents the asynchronous save operation.</returns>
/// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
public abstract Task ExecuteAsync(
IRelationalConnection connection,
CancellationToken cancellationToken = default);
public abstract Task ExecuteAsync(IRelationalConnection connection, CancellationToken cancellationToken = default);
}
11 changes: 11 additions & 0 deletions src/EFCore.Relational/Update/ReaderModificationCommandBatch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@ protected ReaderModificationCommandBatch(ModificationCommandBatchFactoryDependen
/// </summary>
protected virtual IRelationalCommandBuilder RelationalCommandBuilder { get; }

/// <summary>
/// The maximum number of <see cref="ModificationCommand"/> instances that can be added to a single batch.
/// </summary>
protected virtual int MaxBatchSize
=> 1000;

/// <summary>
/// Gets the command text builder for the commands in the batch.
/// </summary>
Expand Down Expand Up @@ -98,6 +104,11 @@ public override bool TryAddCommand(IReadOnlyModificationCommand modificationComm
throw new InvalidOperationException(RelationalStrings.ModificationCommandBatchAlreadyComplete);
}

if (_modificationCommands.Count >= MaxBatchSize)
{
return false;
}

_sqlBuilderPosition = SqlBuilder.Length;
_commandResultSetCount = CommandResultSet.Count;
_pendingParameterNames.Clear();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ public SingularModificationCommandBatch(ModificationCommandBatchFactoryDependenc
{
}

/// <summary>
/// The maximum number of <see cref="ModificationCommand"/> instances that can be added to a single batch; always returns 1.
/// </summary>
protected override int MaxBatchSize
=> 1;

/// <summary>
/// Returns <see langword="true" /> only when the batch contains a single command.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ public class SqlServerModificationCommandBatch : AffectedCountModificationComman
private const int DefaultNetworkPacketSizeBytes = 4096;
private const int MaxScriptLength = 65536 * DefaultNetworkPacketSizeBytes / 2;
private const int MaxParameterCount = 2100;
private const int MaxRowCount = 1000;
private readonly int _maxBatchSize;
private readonly List<IReadOnlyModificationCommand> _pendingBulkInsertCommands = new();

Expand All @@ -30,16 +29,9 @@ public class SqlServerModificationCommandBatch : AffectedCountModificationComman
/// </summary>
public SqlServerModificationCommandBatch(
ModificationCommandBatchFactoryDependencies dependencies,
int? maxBatchSize)
int maxBatchSize)
: base(dependencies)
{
if (maxBatchSize is <= 0)
{
throw new ArgumentOutOfRangeException(nameof(maxBatchSize), RelationalStrings.InvalidMaxBatchSize(maxBatchSize.Value));
}

_maxBatchSize = Math.Min(maxBatchSize ?? 42, MaxRowCount);
}
=> _maxBatchSize = maxBatchSize;

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
Expand All @@ -50,6 +42,15 @@ public SqlServerModificationCommandBatch(
protected new virtual ISqlServerUpdateSqlGenerator UpdateSqlGenerator
=> (ISqlServerUpdateSqlGenerator)base.UpdateSqlGenerator;

/// <summary>
/// The maximum number of <see cref="ModificationCommand"/> instances that can be added to a single batch.
/// </summary>
/// <remarks>
/// For SQL Server, this is 42 by default, and cannot exceed 1000.
/// </remarks>
protected override int MaxBatchSize
=> _maxBatchSize;

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
Expand All @@ -74,8 +75,7 @@ protected override void RollbackLastCommand()
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
protected override bool IsValid()
=> ModificationCommands.Count <= _maxBatchSize
&& SqlBuilder.Length < MaxScriptLength
=> SqlBuilder.Length < MaxScriptLength
// A single implicit parameter for the command text itself
&& ParameterValues.Count + 1 < MaxParameterCount;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ namespace Microsoft.EntityFrameworkCore.SqlServer.Update.Internal;
/// </summary>
public class SqlServerModificationCommandBatchFactory : IModificationCommandBatchFactory
{
private readonly IDbContextOptions _options;
private const int DefaultMaxBatchSize = 42;
private const int MaxMaxBatchSize = 1000;
private readonly int _maxBatchSize;

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
Expand All @@ -26,7 +28,16 @@ public SqlServerModificationCommandBatchFactory(
IDbContextOptions options)
{
Dependencies = dependencies;
_options = options;

_maxBatchSize = Math.Min(
options.Extensions.OfType<SqlServerOptionsExtension>().FirstOrDefault()?.MaxBatchSize ?? DefaultMaxBatchSize,
MaxMaxBatchSize);

if (_maxBatchSize <= 0)
{
throw new ArgumentOutOfRangeException(
nameof(RelationalOptionsExtension.MaxBatchSize), RelationalStrings.InvalidMaxBatchSize(_maxBatchSize));
}
}

/// <summary>
Expand All @@ -41,9 +52,5 @@ public SqlServerModificationCommandBatchFactory(
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public virtual ModificationCommandBatch Create()
{
var optionsExtension = _options.Extensions.OfType<SqlServerOptionsExtension>().FirstOrDefault();

return new SqlServerModificationCommandBatch(Dependencies, optionsExtension?.MaxBatchSize);
}
=> new SqlServerModificationCommandBatch(Dependencies, _maxBatchSize);
}

0 comments on commit 5b0552e

Please sign in to comment.