Uploaded image for project: 'C# Driver'
  1. C# Driver
  2. CSHARP-345

MongoServer.Primary remains null even when there is an active primary.

    • Type: Icon: Bug Bug
    • Resolution: Done
    • Priority: Icon: Major - P3 Major - P3
    • 1.3.1
    • 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))

      { 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;

            Assignee:
            robert@mongodb.com Robert Stam
            Reporter:
            mcassidy Mo Cassidy
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

              Created:
              Updated:
              Resolved: