diff --git a/src/MongoDB.Driver/GridFS/GridFSBucket.cs b/src/MongoDB.Driver/GridFS/GridFSBucket.cs index a7296fa7436..b4d1e4ba0ae 100644 --- a/src/MongoDB.Driver/GridFS/GridFSBucket.cs +++ b/src/MongoDB.Driver/GridFS/GridFSBucket.cs @@ -897,6 +897,11 @@ private GridFSUploadStream CreateUploadStream(IReadWriteBindingHandle b private void EnsureIndexes(OperationContext operationContext, IReadWriteBindingHandle binding) { + if (_options.AssumeIndexesExist) + { + return; + } + _ensureIndexesSemaphore.Wait(operationContext.RemainingTimeout, operationContext.CancellationToken); try { @@ -926,6 +931,11 @@ private void EnsureIndexes(OperationContext operationContext, IReadWriteBindingH private async Task EnsureIndexesAsync(OperationContext operationContext, IReadWriteBindingHandle binding) { + if (_options.AssumeIndexesExist) + { + return; + } + await _ensureIndexesSemaphore.WaitAsync(operationContext.RemainingTimeout, operationContext.CancellationToken).ConfigureAwait(false); try { diff --git a/src/MongoDB.Driver/GridFS/GridFSBucketOptions.cs b/src/MongoDB.Driver/GridFS/GridFSBucketOptions.cs index 1ef76936f0f..437b1c4e933 100644 --- a/src/MongoDB.Driver/GridFS/GridFSBucketOptions.cs +++ b/src/MongoDB.Driver/GridFS/GridFSBucketOptions.cs @@ -24,6 +24,7 @@ namespace MongoDB.Driver.GridFS public class GridFSBucketOptions { // fields + private bool _assumeIndexesExist; private string _bucketName; private int _chunkSizeBytes; private ReadConcern _readConcern; @@ -46,6 +47,7 @@ public GridFSBucketOptions() public GridFSBucketOptions(GridFSBucketOptions other) { Ensure.IsNotNull(other, nameof(other)); + _assumeIndexesExist = other.AssumeIndexesExist; _bucketName = other.BucketName; _chunkSizeBytes = other.ChunkSizeBytes; _readConcern = other.ReadConcern; @@ -60,6 +62,7 @@ public GridFSBucketOptions(GridFSBucketOptions other) public GridFSBucketOptions(ImmutableGridFSBucketOptions other) { Ensure.IsNotNull(other, nameof(other)); + _assumeIndexesExist = other.AssumeIndexesExist; _bucketName = other.BucketName; _chunkSizeBytes = other.ChunkSizeBytes; _readConcern = other.ReadConcern; @@ -68,6 +71,21 @@ public GridFSBucketOptions(ImmutableGridFSBucketOptions other) } // properties + /// + /// Gets or sets whether to assume the GridFS indexes already exist, skipping the index check and creation before the first upload. + /// + /// + /// When true, the driver never verifies or creates the GridFS indexes, so the caller is responsible for ensuring they exist. + /// + /// + /// true if the GridFS indexes are assumed to exist; otherwise, false. + /// + public bool AssumeIndexesExist + { + get { return _assumeIndexesExist; } + set { _assumeIndexesExist = value; } + } + /// /// Gets or sets the bucket name. /// @@ -160,6 +178,7 @@ public static ImmutableGridFSBucketOptions Defaults #endregion // fields + private readonly bool _assumeIndexesExist; private readonly string _bucketName; private readonly int _chunkSizeBytes; private readonly ReadConcern _readConcern; @@ -183,6 +202,7 @@ public ImmutableGridFSBucketOptions() public ImmutableGridFSBucketOptions(GridFSBucketOptions other) { Ensure.IsNotNull(other, nameof(other)); + _assumeIndexesExist = other.AssumeIndexesExist; _bucketName = other.BucketName; _chunkSizeBytes = other.ChunkSizeBytes; _readConcern = other.ReadConcern; @@ -191,6 +211,20 @@ public ImmutableGridFSBucketOptions(GridFSBucketOptions other) } // properties + /// + /// Gets whether to assume the GridFS indexes already exist, skipping the index check and creation before the first upload. + /// + /// + /// When true, the driver never verifies or creates the GridFS indexes, so the caller is responsible for ensuring they exist. + /// + /// + /// true if the GridFS indexes are assumed to exist; otherwise, false. + /// + public bool AssumeIndexesExist + { + get { return _assumeIndexesExist; } + } + /// /// Gets the bucket name. /// diff --git a/tests/MongoDB.Driver.Tests/GridFS/GridFSBucketOptionsTests.cs b/tests/MongoDB.Driver.Tests/GridFS/GridFSBucketOptionsTests.cs index d80fcef35a2..0bc09d4ec9a 100644 --- a/tests/MongoDB.Driver.Tests/GridFS/GridFSBucketOptionsTests.cs +++ b/tests/MongoDB.Driver.Tests/GridFS/GridFSBucketOptionsTests.cs @@ -27,6 +27,32 @@ namespace MongoDB.Driver.Tests.GridFS { public class GridFSBucketOptionsTests { + [Theory] + [ParameterAttributeData] + public void AssumeIndexesExist_get_should_return_expected_result( + [Values(false, true)] + bool value) + { + var subject = new GridFSBucketOptions { AssumeIndexesExist = value }; + + var result = subject.AssumeIndexesExist; + + result.Should().Be(value); + } + + [Theory] + [ParameterAttributeData] + public void AssumeIndexesExist_set_should_have_expected_result( + [Values(false, true)] + bool value) + { + var subject = new GridFSBucketOptions(); + + subject.AssumeIndexesExist = value; + + subject.AssumeIndexesExist.Should().Be(value); + } + [Fact] public void BucketName_get_should_return_expected_result() { @@ -103,11 +129,12 @@ public void ChunkSizeBytes_set_should_throw_when_value_is_invalid( [Fact] public void constructor_with_immutable_other_should_initialize_instance() { - var mutable = new GridFSBucketOptions { BucketName = "bucket", ChunkSizeBytes = 123, ReadConcern = ReadConcern.Majority, ReadPreference = ReadPreference.Secondary, WriteConcern = WriteConcern.WMajority }; + var mutable = new GridFSBucketOptions { AssumeIndexesExist = true, BucketName = "bucket", ChunkSizeBytes = 123, ReadConcern = ReadConcern.Majority, ReadPreference = ReadPreference.Secondary, WriteConcern = WriteConcern.WMajority }; var other = new ImmutableGridFSBucketOptions(mutable); var result = new GridFSBucketOptions(other); + result.AssumeIndexesExist.Should().Be(other.AssumeIndexesExist); result.BucketName.Should().Be(other.BucketName); result.ChunkSizeBytes.Should().Be(other.ChunkSizeBytes); result.ReadConcern.Should().Be(other.ReadConcern); @@ -118,10 +145,11 @@ public void constructor_with_immutable_other_should_initialize_instance() [Fact] public void constructor_with_mutable_other_should_initialize_instance() { - var other = new GridFSBucketOptions { BucketName = "bucket", ChunkSizeBytes = 123, ReadConcern = ReadConcern.Majority, ReadPreference = ReadPreference.Secondary, WriteConcern = WriteConcern.WMajority }; + var other = new GridFSBucketOptions { AssumeIndexesExist = true, BucketName = "bucket", ChunkSizeBytes = 123, ReadConcern = ReadConcern.Majority, ReadPreference = ReadPreference.Secondary, WriteConcern = WriteConcern.WMajority }; var result = new GridFSBucketOptions(other); + result.AssumeIndexesExist.Should().Be(other.AssumeIndexesExist); result.BucketName.Should().Be(other.BucketName); result.ChunkSizeBytes.Should().Be(other.ChunkSizeBytes); result.ReadConcern.Should().Be(other.ReadConcern); @@ -134,6 +162,7 @@ public void constructor_with_no_arguments_should_initialize_instance_with_defaul { var result = new GridFSBucketOptions(); + result.AssumeIndexesExist.Should().BeFalse(); result.BucketName.Should().Be("fs"); result.ChunkSizeBytes.Should().Be(255 * 1024); result.ReadPreference.Should().BeNull(); @@ -203,6 +232,19 @@ public void WriteConcern_set_should_have_expected_result() public class ImmutableGridFSBucketOptionsTests { + [Theory] + [ParameterAttributeData] + public void AssumeIndexesExist_get_should_return_expected_result( + [Values(false, true)] + bool value) + { + var subject = new ImmutableGridFSBucketOptions(new GridFSBucketOptions { AssumeIndexesExist = value }); + + var result = subject.AssumeIndexesExist; + + result.Should().Be(value); + } + [Fact] public void BucketName_get_should_return_expected_result() { @@ -228,6 +270,7 @@ public void constructor_with_arguments_should_initialize_instance() { var mutable = new GridFSBucketOptions { + AssumeIndexesExist = true, BucketName = "bucket", ChunkSizeBytes = 123, ReadConcern = ReadConcern.Majority, @@ -237,6 +280,7 @@ public void constructor_with_arguments_should_initialize_instance() var result = new ImmutableGridFSBucketOptions(mutable); + result.AssumeIndexesExist.Should().BeTrue(); result.BucketName.Should().Be("bucket"); result.ChunkSizeBytes.Should().Be(123); result.ReadConcern.Should().Be(ReadConcern.Majority); @@ -247,8 +291,9 @@ public void constructor_with_arguments_should_initialize_instance() [Fact] public void constructor_with_no_arguments_should_initialize_instance_with_default_values() { - var result = new GridFSBucketOptions(); + var result = new ImmutableGridFSBucketOptions(); + result.AssumeIndexesExist.Should().BeFalse(); result.BucketName.Should().Be("fs"); result.ChunkSizeBytes.Should().Be(255 * 1024); result.ReadConcern.Should().BeNull(); @@ -270,6 +315,7 @@ public void Defaults_get_should_return_expected_result() { var result = ImmutableGridFSBucketOptions.Defaults; + result.AssumeIndexesExist.Should().BeFalse(); result.BucketName.Should().Be("fs"); result.ChunkSizeBytes.Should().Be(255 * 1024); result.ReadConcern.Should().BeNull(); diff --git a/tests/MongoDB.Driver.Tests/GridFS/GridFSBucketTests.cs b/tests/MongoDB.Driver.Tests/GridFS/GridFSBucketTests.cs index c7403469eb6..8e7dd35bb0b 100644 --- a/tests/MongoDB.Driver.Tests/GridFS/GridFSBucketTests.cs +++ b/tests/MongoDB.Driver.Tests/GridFS/GridFSBucketTests.cs @@ -18,6 +18,7 @@ using System.IO; using System.Linq; using System.Text; +using System.Threading.Tasks; using FluentAssertions; using MongoDB.Bson; using MongoDB.Driver.Core.Clusters; @@ -703,6 +704,52 @@ public void GridFS_should_work_with_strict_stable_api( } } + [Theory] + [ParameterAttributeData] + [Trait("Category", "Integration")] + public async Task Upload_should_create_indexes_unless_AssumeIndexesExist_is_true( + [Values(false, true)] bool assumeIndexesExist, + [Values(false, true)] bool async) + { + RequireServer.Check(); + var client = DriverTestConfiguration.Client; + var database = client.GetDatabase(DriverTestConfiguration.DatabaseNamespace.DatabaseName); + var options = new GridFSBucketOptions { AssumeIndexesExist = assumeIndexesExist }; + var subject = new GridFSBucket(database, options); + await DropBucketAsync(subject, async); + + try + { + if (async) + { + await subject.UploadFromBytesAsync("filename", new byte[] { 0 }); + } + else + { + subject.UploadFromBytes("filename", new byte[] { 0 }); + } + + var filesIndexes = database.GetCollection("fs.files").Indexes.List().ToList(); + var chunksIndexes = database.GetCollection("fs.chunks").Indexes.List().ToList(); + if (assumeIndexesExist) + { + filesIndexes.Should().NotContain(index => index["name"] == "filename_1_uploadDate_1"); + chunksIndexes.Should().NotContain(index => index["name"] == "files_id_1_n_1"); + } + else + { + filesIndexes.Should().Contain(index => index["name"] == "filename_1_uploadDate_1"); + chunksIndexes.Should().Contain(index => index["name"] == "files_id_1_n_1"); + } + } + finally + { + // restore shared state: with AssumeIndexesExist the upload leaves fs.* non-empty and without the + // GridFS indexes, which would suppress index creation for later tests reusing the default bucket + await DropBucketAsync(subject, async); + } + } + // private methods private GridFSBucket CreateSubject(GridFSBucketOptions options = null) { @@ -723,5 +770,17 @@ private void EnsureBucketExists(IGridFSBucket bucket) { bucket.UploadFromBytes("filename", new byte[0]); } + + private async Task DropBucketAsync(IGridFSBucket bucket, bool async) + { + if (async) + { + await bucket.DropAsync(); + } + else + { + bucket.Drop(); + } + } } }