You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

206 lines
9.4 KiB

/* Copyright 2019-present MongoDB Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Linq;
using System.Net;
using FluentAssertions;
using MongoDB.Bson;
using MongoDB.Driver.Core;
using MongoDB.Driver.Core.Bindings;
using MongoDB.Driver.Core.Clusters;
using MongoDB.Driver.Core.Events;
using MongoDB.Driver.Core.Misc;
using MongoDB.Driver.Core.Servers;
using MongoDB.Driver.Core.TestHelpers;
using MongoDB.Driver.Core.TestHelpers.XunitExtensions;
using MongoDB.TestHelpers.XunitExtensions;
using Xunit;
namespace MongoDB.Driver.Tests
{
[Trait("Category", "Integration")]
public class ConnectionsSurvivePrimaryStepDownTests
{
private readonly string _collectionName = "step-down";
private readonly string _databaseName = "step-down";
[Theory]
[ParameterAttributeData]
public void Connection_pool_should_be_cleared_when_Shutdown_exceptions(
[Values(
ServerErrorCode.ShutdownInProgress, // 91
ServerErrorCode.InterruptedAtShutdown)] // 11600
int errorCode)
{
RequireServer.Check().Supports(Feature.FailPointsFailCommand).ClusterType(ClusterType.ReplicaSet);
var eventCapturer = new EventCapturer()
.Capture<ConnectionPoolClearedEvent>()
.Capture<ConnectionCreatedEvent>();
using (var client = CreateMongoClient(eventCapturer))
{
var database = client.GetDatabase(_databaseName, new MongoDatabaseSettings { WriteConcern = WriteConcern.WMajority });
database.DropCollection(_databaseName);
var collection = database.GetCollection<BsonDocument>(_collectionName, new MongoCollectionSettings { WriteConcern = WriteConcern.WMajority });
eventCapturer.Clear();
using (ConfigureFailPoint(client, errorCode))
{
var exception = Record.Exception(() => { collection.InsertOne(new BsonDocument("test", 1)); });
var e = exception.Should().BeOfType<MongoNodeIsRecoveringException>().Subject;
e.Code.Should().Be(errorCode);
eventCapturer.Next().Should().BeOfType<ConnectionPoolClearedEvent>();
eventCapturer.Events.Should().BeEmpty();
collection.InsertOne(new BsonDocument("test", 1));
eventCapturer.Next().Should().BeOfType<ConnectionCreatedEvent>();
eventCapturer.Events.Should().BeEmpty();
}
}
}
[Theory]
[ParameterAttributeData]
public void Connection_pool_should_not_be_cleared_when_replSetStepDown_and_GetMore([Values(false, true)] bool async)
{
RequireServer.Check().Supports(Feature.KeepConnectionPoolWhenReplSetStepDown).ClusterType(ClusterType.ReplicaSet);
var eventCapturer = new EventCapturer().Capture<ConnectionPoolClearedEvent>();
using (var client = CreateMongoClient(eventCapturer))
{
var database = client.GetDatabase(_databaseName, new MongoDatabaseSettings { WriteConcern = WriteConcern.WMajority });
database.DropCollection(_databaseName);
var collection = database.GetCollection<BsonDocument>(_collectionName, new MongoCollectionSettings { WriteConcern = WriteConcern.WMajority });
var adminDatabase = client.GetDatabase("admin").WithWriteConcern(WriteConcern.W1);
collection.InsertMany(
new[]
{
new BsonDocument("x", 1),
new BsonDocument("x", 2),
new BsonDocument("x", 3),
new BsonDocument("x", 4),
new BsonDocument("x", 5),
});
eventCapturer.Clear();
var cursor = collection.FindSync(FilterDefinition<BsonDocument>.Empty, new FindOptions<BsonDocument> { BatchSize = 2 });
cursor.MoveNext();
foreach (var secondary in client.Cluster.Description.Servers.Where(c => c.Type == ServerType.ReplicaSetSecondary))
{
RunOnSecondary(client, secondary.EndPoint, BsonDocument.Parse("{ replSetFreeze : 0 }"));
}
var replSetStepDownCommand = BsonDocument.Parse("{ replSetStepDown : 30, force : true }");
BsonDocument replSetStepDownResult;
if (async)
{
replSetStepDownResult = adminDatabase.RunCommandAsync<BsonDocument>(replSetStepDownCommand).GetAwaiter().GetResult();
}
else
{
replSetStepDownResult = adminDatabase.RunCommand<BsonDocument>(replSetStepDownCommand);
}
replSetStepDownResult.Should().NotBeNull();
replSetStepDownResult.GetValue("ok", false).ToBoolean().Should().BeTrue();
cursor.MoveNext();
eventCapturer.Events.Should().BeEmpty(); // it also means that no new PoolClearedEvent
}
void RunOnSecondary(IMongoClient primaryClient, EndPoint secondaryEndpoint, BsonDocument command)
{
var secondarySettings = primaryClient.Settings.Clone();
secondarySettings.ClusterConfigurator = null;
secondarySettings.DirectConnection = true;
var secondaryDnsEndpoint = (DnsEndPoint)secondaryEndpoint;
secondarySettings.Server = new MongoServerAddress(secondaryDnsEndpoint.Host, secondaryDnsEndpoint.Port);
using (var secondaryClient = DriverTestConfiguration.CreateMongoClient(secondarySettings))
{
var adminDatabase = secondaryClient.GetDatabase(DatabaseNamespace.Admin.DatabaseName);
adminDatabase.RunCommand<BsonDocument>(command);
}
}
}
[Fact]
public void Connection_pool_should_work_as_expected_when_NonPrimary_exception()
{
RequireServer.Check().Supports(Feature.FailPointsFailCommand).ClusterType(ClusterType.ReplicaSet);
var shouldConnectionPoolBeCleared = !Feature.KeepConnectionPoolWhenNotPrimaryConnectionException.IsSupported(CoreTestConfiguration.MaxWireVersion);
var eventCapturer = new EventCapturer()
.Capture<ConnectionPoolClearedEvent>()
.Capture<ConnectionCreatedEvent>();
using (var client = CreateMongoClient(eventCapturer))
{
var database = client.GetDatabase(_databaseName, new MongoDatabaseSettings { WriteConcern = WriteConcern.WMajority });
database.DropCollection(_databaseName);
var collection = database.GetCollection<BsonDocument>(_collectionName, new MongoCollectionSettings { WriteConcern = WriteConcern.WMajority });
eventCapturer.Clear();
using (ConfigureFailPoint(client, 10107))
{
var exception = Record.Exception(() => { collection.InsertOne(new BsonDocument("test", 1)); });
var e = exception.Should().BeOfType<MongoNotPrimaryException>().Subject;
e.Code.Should().Be(10107);
if (shouldConnectionPoolBeCleared)
{
eventCapturer.Next().Should().BeOfType<ConnectionPoolClearedEvent>();
eventCapturer.Events.Should().BeEmpty();
}
else
{
eventCapturer.Events.Should().BeEmpty();
}
collection.InsertOne(new BsonDocument("test", 1));
if (shouldConnectionPoolBeCleared)
{
eventCapturer.Next().Should().BeOfType<ConnectionCreatedEvent>();
}
eventCapturer.Events.Should().BeEmpty();
}
}
}
// private methods
private FailPoint ConfigureFailPoint(IMongoClient client, int errorCode)
{
var session = NoCoreSession.NewHandle();
var args = BsonDocument.Parse($"{{ mode : {{ times : 1 }}, data : {{ failCommands : [\"insert\"], errorCode : {errorCode} }} }}");
return FailPoint.Configure(client.GetClusterInternal(), session, "failCommand", args);
}
private IMongoClient CreateMongoClient(EventCapturer capturedEvents) =>
DriverTestConfiguration.CreateMongoClient(
settings =>
{
settings.HeartbeatInterval = TimeSpan.FromMilliseconds(5); // the default value for spec tests
settings.RetryWrites = false;
settings.ClusterConfigurator = c => { c.Subscribe(capturedEvents); };
});
}
}