Browse Source

Fixed CSHARP-269. MongoGridFS.EnsureIndexes now checks actual server instance (if a RequestStart is in scope) to see if it is a primary. MongoGridFS.Download now actually uses slaveOk from database.Settings. Added overload of RequestStart with slaveOk parameter. Added RequestConnection property to MongoServer that can be used to get the actual connection reserved by RequestStart. Made get accessors of MongoConnection properties public so client code can get information about a connection.

pull/63/head
rstam 14 years ago
parent
commit
23346cd0f4
  1. 2
      Bson/IO/JsonWriter.cs
  2. 15
      Driver/Core/MongoDatabase.cs
  3. 52
      Driver/Core/MongoServer.cs
  4. 18
      Driver/GridFS/MongoGridFS.cs
  5. 2
      Driver/GridFS/MongoGridFSFileInfo.cs
  6. 56
      Driver/Internal/MongoConnection.cs
  7. 1
      DriverOnlineTests/DriverOnlineTests.csproj
  8. 60
      DriverOnlineTests/Jira/CSharp269Tests.cs

2
Bson/IO/JsonWriter.cs

@ -104,7 +104,7 @@ namespace MongoDB.Bson.IO {
throw new ArgumentException("GuidRepresentation for binary subtype UuidLegacy must not be Standard.");
}
if (subType == BsonBinarySubType.UuidStandard && guidRepresentation != GuidRepresentation.Standard) {
var message = string.Format("GuidRepresentation for binary subtype UuidStandard must Standard, not {0}.", guidRepresentation);
var message = string.Format("GuidRepresentation for binary subtype UuidStandard must be Standard, not {0}.", guidRepresentation);
throw new ArgumentException(message);
}
if (settings.ShellVersion >= new Version(2, 0, 0)) {

15
Driver/Core/MongoDatabase.cs

@ -754,7 +754,20 @@ namespace MongoDB.Driver {
/// </summary>
/// <returns>A helper object that implements IDisposable and calls <see cref="RequestDone"/> from the Dispose method.</returns>
public virtual IDisposable RequestStart() {
return server.RequestStart(this);
return RequestStart(false); // not slaveOk
}
/// <summary>
/// Lets the server know that this thread is about to begin a series of related operations that must all occur
/// on the same connection. The return value of this method implements IDisposable and can be placed in a
/// using statement (in which case RequestDone will be called automatically when leaving the using statement).
/// </summary>
/// <param name="slaveOk">Whether queries should be sent to secondary servers.</param>
/// <returns>A helper object that implements IDisposable and calls <see cref="RequestDone"/> from the Dispose method.</returns>
public virtual IDisposable RequestStart(
bool slaveOk
) {
return server.RequestStart(this, slaveOk);
}
// TODO: mongo shell has ResetError at the database level

52
Driver/Core/MongoServer.cs

@ -293,6 +293,23 @@ namespace MongoDB.Driver {
internal set { replicaSetName = value; }
}
/// <summary>
/// Gets the connection reserved by the current RequestStart scope (null if not in the scope of a RequestStart).
/// </summary>
public virtual MongoConnection RequestConnection {
get {
int threadId = Thread.CurrentThread.ManagedThreadId;
lock (requestsLock) {
Request request;
if (requests.TryGetValue(threadId, out request)) {
return request.Connection;
} else {
return null;
}
}
}
}
/// <summary>
/// Gets the RequestStart nesting level for the current thread.
/// </summary>
@ -757,21 +774,39 @@ namespace MongoDB.Driver {
/// <returns>A helper object that implements IDisposable and calls <see cref="RequestDone"/> from the Dispose method.</returns>
public virtual IDisposable RequestStart(
MongoDatabase initialDatabase
) {
return RequestStart(initialDatabase, false); // not slaveOk
}
/// <summary>
/// Lets the server know that this thread is about to begin a series of related operations that must all occur
/// on the same connection. The return value of this method implements IDisposable and can be placed in a
/// using statement (in which case RequestDone will be called automatically when leaving the using statement).
/// </summary>
/// <param name="initialDatabase">One of the databases involved in the related operations.</param>
/// <param name="slaveOk">Whether queries should be sent to secondary servers.</param>
/// <returns>A helper object that implements IDisposable and calls <see cref="RequestDone"/> from the Dispose method.</returns>
public virtual IDisposable RequestStart(
MongoDatabase initialDatabase,
bool slaveOk
) {
int threadId = Thread.CurrentThread.ManagedThreadId;
lock (requestsLock) {
Request request;
if (requests.TryGetValue(threadId, out request)) {
if (!slaveOk && request.SlaveOk) {
throw new InvalidOperationException("A nested call to RequestStart with slaveOk false is not allowed when the original call to RequestStart was made with slaveOk true.");
}
request.NestingLevel++;
return new RequestStartResult(this);
}
}
// get the connection outside of the lock
var connection = AcquireConnection(initialDatabase, false); // not slaveOk
var connection = AcquireConnection(initialDatabase, slaveOk);
lock (requestsLock) {
var request = new Request(connection);
var request = new Request(connection, slaveOk);
requests.Add(threadId, request);
return new RequestStartResult(this);
}
@ -882,6 +917,9 @@ namespace MongoDB.Driver {
lock (requestsLock) {
Request request;
if (requests.TryGetValue(threadId, out request)) {
if (!slaveOk && request.SlaveOk) {
throw new InvalidOperationException("A call to AcquireConnection with slaveOk false is not allowed when the current RequestStart was made with slaveOk true.");
}
request.Connection.CheckAuthentication(database); // will throw exception if authentication fails
return request.Connection;
}
@ -1041,14 +1079,17 @@ namespace MongoDB.Driver {
private class Request {
#region private fields
private MongoConnection connection;
private bool slaveOk;
private int nestingLevel;
#endregion
#region constructors
public Request(
MongoConnection connection
MongoConnection connection,
bool slaveOk
) {
this.connection = connection;
this.slaveOk = slaveOk;
this.nestingLevel = 1;
}
#endregion
@ -1063,6 +1104,11 @@ namespace MongoDB.Driver {
get { return nestingLevel; }
set { nestingLevel = value; }
}
public bool SlaveOk {
get { return slaveOk; }
internal set { slaveOk = value; }
}
#endregion
}

18
Driver/GridFS/MongoGridFS.cs

@ -269,7 +269,7 @@ namespace MongoDB.Driver.GridFS {
Stream stream,
MongoGridFSFileInfo fileInfo
) {
using (database.RequestStart()) {
using (database.RequestStart(database.Settings.SlaveOk)) {
EnsureIndexes();
string md5Client;
@ -441,8 +441,18 @@ namespace MongoDB.Driver.GridFS {
int maxFiles
) {
// don't try to create indexes on secondaries
if (files.Settings.SlaveOk) {
return;
var requestConnection = database.Server.RequestConnection;
if (requestConnection != null) {
// check whether the actual server instance we are using is a primary
var serverInstance = requestConnection.ServerInstance;
if (!serverInstance.IsPrimary) {
return;
}
} else {
// check whether we are guaranteed to use a primary
if (database.Settings.SlaveOk) {
return;
}
}
// avoid round trip to count files if possible
@ -796,7 +806,7 @@ namespace MongoDB.Driver.GridFS {
string remoteFileName,
MongoGridFSCreateOptions createOptions
) {
using (database.RequestStart()) {
using (database.RequestStart(false)) { // not slaveOk
EnsureIndexes();
var files_id = createOptions.Id ?? BsonObjectId.GenerateNewId();

2
Driver/GridFS/MongoGridFSFileInfo.cs

@ -315,7 +315,7 @@ namespace MongoDB.Driver.GridFS {
/// </summary>
public void Delete() {
if (Exists) {
using (gridFS.Database.RequestStart()) {
using (gridFS.Database.RequestStart(false)) { // not slaveOk
gridFS.EnsureIndexes();
gridFS.Files.Remove(Query.EQ("_id", id), gridFS.Settings.SafeMode);
gridFS.Chunks.Remove(Query.EQ("files_id", id), gridFS.Settings.SafeMode);

56
Driver/Internal/MongoConnection.cs

@ -26,14 +26,32 @@ using MongoDB.Bson.IO;
using MongoDB.Bson.Serialization;
namespace MongoDB.Driver.Internal {
internal enum MongoConnectionState {
/// <summary>
/// Represents the state of a connection.
/// </summary>
public enum MongoConnectionState {
/// <summary>
/// The connection has not yet been initialized.
/// </summary>
Initial,
/// <summary>
/// The connection is open.
/// </summary>
Open,
/// <summary>
/// The connection is damaged.
/// </summary>
Damaged,
/// <summary>
/// The connection is closed.
/// </summary>
Closed
}
internal class MongoConnection {
/// <summary>
/// Represents a connection to a MongoServerInstance.
/// </summary>
public class MongoConnection {
#region private fields
private object connectionLock = new object();
private MongoServerInstance serverInstance;
@ -57,29 +75,47 @@ namespace MongoDB.Driver.Internal {
}
#endregion
#region internal properties
internal MongoConnectionPool ConnectionPool {
#region public properties
/// <summary>
/// Gets the connection pool that this connection belongs to.
/// </summary>
public MongoConnectionPool ConnectionPool {
get { return connectionPool; }
}
internal DateTime CreatedAt {
/// <summary>
/// Gets the DateTime that this connection was created at.
/// </summary>
public DateTime CreatedAt {
get { return createdAt; }
}
internal DateTime LastUsedAt {
/// <summary>
/// Gets the DateTime that this connection was last used at.
/// </summary>
public DateTime LastUsedAt {
get { return lastUsedAt; }
set { lastUsedAt = value; }
internal set { lastUsedAt = value; }
}
internal int MessageCounter {
/// <summary>
/// Gets a count of the number of messages that have been sent using this connection.
/// </summary>
public int MessageCounter {
get { return messageCounter; }
}
internal MongoServerInstance ServerInstance {
/// <summary>
/// Gets the server instance this connection is connected to.
/// </summary>
public MongoServerInstance ServerInstance {
get { return serverInstance; }
}
internal MongoConnectionState State {
/// <summary>
/// Gets the state of this connection.
/// </summary>
public MongoConnectionState State {
get { return state; }
}
#endregion

1
DriverOnlineTests/DriverOnlineTests.csproj

@ -106,6 +106,7 @@
<Compile Include="Jira\CSharp253Tests.cs" />
<Compile Include="Jira\CSharp258Tests.cs" />
<Compile Include="Jira\CSharp265Tests.cs" />
<Compile Include="Jira\CSharp269Tests.cs" />
<Compile Include="Jira\CSharp77Tests.cs" />
<Compile Include="Jira\CSharp92Tests.cs" />
<Compile Include="Jira\CSharp93Tests.cs" />

60
DriverOnlineTests/Jira/CSharp269Tests.cs

@ -0,0 +1,60 @@
/* Copyright 2010-2011 10gen 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.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using NUnit.Framework;
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
using MongoDB.Driver;
using MongoDB.Driver.Builders;
namespace MongoDB.DriverOnlineTests.Jira.CSharp269 {
[TestFixture]
public class CSharp269Tests {
private MongoServer server;
private MongoDatabase database;
[TestFixtureSetUp]
public void TestFixtureSetup() {
server = MongoServer.Create("mongodb://localhost/?safe=true;slaveOk=true");
database = server["onlinetests"];
database.GridFS.Files.Drop();
database.GridFS.Chunks.Drop();
}
[Test]
public void TestUploadAndDownload() {
var text = "HelloWorld";
var bytes = Encoding.UTF8.GetBytes(text);
using (var stream = new MemoryStream(bytes)) {
database.GridFS.Upload(stream, "HelloWorld.txt");
}
using (var stream = new MemoryStream()) {
database.GridFS.Download(stream, "HelloWorld.txt");
var downloadedBytes = stream.ToArray();
var downloadedText = Encoding.UTF8.GetString(downloadedBytes);
Assert.AreEqual("HelloWorld", downloadedText);
}
}
}
}
Loading…
Cancel
Save