-
Type: Bug
-
Resolution: Done
-
Priority: Major - P3
-
Affects Version/s: 1.3
-
Component/s: None
-
None
-
Fully Compatible
Test setup:
Start two mongod instances (plus an arbiter) in a replicaset.
Connect to the replicaset using the C# Driver.
Attempting db reads works correctly.
Shutdown the primary instance, wait for the secondary to be promoted to primary.
Attempting db reads now fails with a MongoConnectionException ("Unable to choose a server instance.").
Note that attempting a db write will temporarily fix the problem until the primary goes down again and a new secondary is promoted.
The reason is that MongoServer.Primary is null.
-Sample Code-
Create brand new Windows Forms project referencing MongoDB.Driver and MongoDB.Bson, and add the following form files:
// Form1.Designer.cs
namespace mongoDriverTest
{
partial class Form1
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
#endregion
private System.Windows.Forms.Button button1;
}
}
//
//
// Form1.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using MongoDB.Driver;
using MongoDB.Bson;
namespace mongoDriverTest
{
public partial class Form1 : Form
{
MongoServer server;
MongoDatabase database;
MongoCollection<BsonDocument> books;
public Form1()
private void Form1_Load(object sender, EventArgs e)
{
server = MongoServer.Create("mongodb://localhost:27021,localhost:27022/?replicaSet=foo;slaveOk=true");
database = server["bar"];
books = database["books"];
BsonDocument book = new BsonDocument {
//SafeModeResult result = books.Insert(book);
}
private void button1_Click(object sender, EventArgs e)
{
try
{
BsonDocument bookToInsert = new BsonDocument { { "Author", "Ernest Hemingway" }
,
{ "Title", "For Whom The Bell Tolls" } };//SafeModeResult result = books.Insert(bookToInsert); //If this is line is uncommented, everything works fine.
BsonDocument book = books.FindOne();
var poo = books.Count();
MessageBox.Show(book[1].ToString());
}
catch (Exception ex)
{ MessageBox.Show(ex.GetType().ToString() + "::" + ex.Message); }
}
}
}
-
When the primary goes down, the secondary becomes the new primary, but
the instance's "State" property never changes from connected, so the
State property setter's "if (state != value)" line prevents
InstanceStateChanged ever getting called on that instance after it
becomes the new primary.
It seems that once a write is attempted...
books.Insert(new BsonDocument { { "Author", "Ernest Hemingway" },{ "Title", "For Whom The Bell Tolls" }
});
...the below call stack happens...
MongoDB.Driver.MongoServer.InstanceStateChanged(object sender =
{MongoDB.Driver.MongoServerInstance}, object args = null)
MongoDB.Driver.MongoServerInstance.State.set(MongoDB.Driver.MongoServerState
value = Connected)
MongoDB.Driver.MongoServerInstance.VerifyState(MongoDB.Driver.Internal.MongoConnection
connection =
)
MongoDB.Driver.MongoServerInstance.Connect(bool slaveOk = true)
MongoDB.Driver.Internal.ReplicaSetConnector.ConnectWorkItem(object
argsObject =
)
[External Code]
...which flips the instance's state from connected to connecting (and
eventually back again to connected), causing Instances.Primary to be
set to this new primary instance, however, this call stack ONLY
happens when a write is attempted. If I just continue attempting to
read (since SlaveOk is true), the state remains "Connected"
indefinitely, and no new Primary gets set.
This may have been difficult to catch, because if there are
multiple secondaries when the primary goes down, one of them will
become the next primary and, although MongServer.Primary will continue
to be null, the following part of MongServer.ChooseServerInstance(bool
slaveOk) will ensure that reads continue to work fine against one of
the other secondaries...
if (instance.State == MongoServerState.Connected &&
(instance.IsSecondary || instance.IsPassive)) {
return instance;