[CSHARP-345] MongoServer.Primary remains null even when there is an active primary. Created: 24/Oct/11  Updated: 02/Apr/15  Resolved: 04/Nov/11

Status: Closed
Project: C# Driver
Component/s: None
Affects Version/s: 1.3
Fix Version/s: 1.3.1

Type: Bug Priority: Major - P3
Reporter: Mo Cassidy Assignee: Robert Stam
Resolution: Done Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Backwards Compatibility: Fully Compatible

 Description   

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))

{ components.Dispose(); }

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()

{ this.button1 = new System.Windows.Forms.Button(); this.SuspendLayout(); // // button1 // this.button1.Location = new System.Drawing.Point(13, 13); this.button1.Name = "button1"; this.button1.Size = new System.Drawing.Size(75, 23); this.button1.TabIndex = 0; this.button1.Text = "button1"; this.button1.UseVisualStyleBackColor = true; this.button1.Click += new System.EventHandler(this.button1_Click); // // Form1 // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(284, 266); this.Controls.Add(this.button1); this.Name = "Form1"; this.Text = "Form1"; this.Load += new System.EventHandler(this.Form1_Load); this.ResumeLayout(false); }

#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()

{ InitializeComponent(); }

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 {

{ "Author", "Ernest Hemingway" }, { "Title", "For Whom The Bell Tolls" } };
//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); }
}
}
}






-Investigation-

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.Internal.MongoConnection}

)
MongoDB.Driver.MongoServerInstance.Connect(bool slaveOk = true)
MongoDB.Driver.Internal.ReplicaSetConnector.ConnectWorkItem(object
argsObject =

{MongoDB.Driver.Internal.ReplicaSetConnector.ConnectArgs}

)
[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;



 Comments   
Comment by Robert Stam [ 04/Nov/11 ]

Resolved by making sure StateChanged event is raised when any of the state related properties (State, IsPrimary, IsSecondary, IsPassive and IsArbiter) changes value, not just when State changes value.

Comment by Mo Cassidy [ 24/Oct/11 ]

Original google groups thread at: http://groups.google.com/group/mongodb-csharp/browse_thread/thread/3e11574e2f2a8f08

Generated at Wed Feb 07 21:36:33 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.