Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OGM-270 [MongoDB] Expose the WriteConcern settings to the configuration #171

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -104,15 +104,12 @@
</row>

<row>
<entry>hibernate.ogm.mongodb.safe</entry>

<entry>Defines the so-called safe mode in MongoDB. When set to
<literal>false</literal>, the driver does not wait for the write
operation to be applied before returning. This could lead to loss
of writes. When set to <literal>true</literal>, the driver waits
for the operation to succeed before returning. Note that this is
slower than the unsafe mode. The default value is
<literal>true</literal>.</entry>
<entry>hibernate.ogm.mongodb.writeconcern</entry>

<entry>Defines the write concern setting used to control the
acknowledgment of write operations. Refer to
<xref linkend="omg-mongodb-writeconcern" /> to have the complete list of values.
The default value is <literal>ACKNOWLEDGED</literal></entry>
</row>

<row>
Expand Down Expand Up @@ -140,15 +137,30 @@
</tbody>
</tgroup>
</table>
</section>

<note>
<para>The general policy in Hibernate OGM is to not go against the
underlying NoSQL store. We have made an exception for MongoDB's write
concern default setting - which is unsafe out of the box. Based on the
typical use case of Hibernate OGM (domain models) and based on the
production feedback we have received, we do consider MongoDB's default
value to be dangerous.</para>
</note>
<section id="omg-mongodb-writeconcern">
<title>WriteConcern property</title>
<para>
The possible values for the <literal>hibernate.ogm.mongodb.writeconcern possible values</literal> option are:
<itemizedlist>
<listitem>ERRORS_IGNORED</listitem>
<listitem>ACKNOWLEDGED</listitem>
<listitem>UNACKNOWLEDGED</listitem>
<listitem>FSYNCED</listitem>
<listitem>JOURNALED</listitem>
<listitem>NONE</listitem>
<listitem>NORMAL</listitem>
<listitem>SAFE</listitem>
<listitem>MAJORITY</listitem>
<listitem>FSYNC_SAFE</listitem>
<listitem>JOURNAL_SAFE</listitem>
<listitem>REPLICAS_SAFE</listitem>
</itemizedlist>
For more information, please refer to the
<ulink href="http://api.mongodb.org/java/current/com/mongodb/WriteConcern.html">official documentation</ulink>
This option is case insensitive and the default value is <literal>ACKNOWLEDGED</literal>
</para>
</section>

<section>
Expand Down
Expand Up @@ -38,10 +38,10 @@ public interface Environment {
public static final String MONGODB_PORT = "hibernate.ogm.mongodb.port";

/**
* Run the driver in safe mode (use WriteConcern.SAFE for all operations)
* Define the acknowledgment of write operations
*/
public static final String MONGODB_SAFE = "hibernate.ogm.mongodb.safe";
public static final String MONGODB_WRITE_CONCERN = "hibernate.ogm.mongodb.writeconcern";

/**
* The hostname of the MongoDB instance.
*/
Expand Down Expand Up @@ -74,11 +74,6 @@ public interface Environment {
* property is not set, we'll try this port.
*/
public static final int MONGODB_DEFAULT_PORT = 27017;

/**
* The default value used to configure the safe mode {@link #MONGODB_SAFE}
*/
public static final boolean MONGODB_DEFAULT_SAFE = true;

/**
* Where to store associations.
Expand Down
Expand Up @@ -53,7 +53,7 @@ public class MongoDBDatastoreProvider implements DatastoreProvider, Startable, S

private Map<?, ?> cfg;
private boolean isCacheStarted;
private MongoClient mongo;
public MongoClient mongo;
private DB mongoDb;
private AssociationStorage associationStorage;

Expand Down Expand Up @@ -109,10 +109,13 @@ public void start() {
port = Environment.MONGODB_DEFAULT_PORT;
}

Object cfgSafe = this.cfg.get( Environment.MONGODB_SAFE );
boolean safe = Environment.MONGODB_DEFAULT_SAFE;
if ( cfgSafe != null ) {
safe = Boolean.parseBoolean( cfgSafe.toString() );
Object cfgWC = this.cfg.get( Environment.MONGODB_WRITE_CONCERN );
WriteConcern writeConcern = cfgWC != null ? WriteConcern.valueOf( cfgWC.toString() ) : WriteConcern.ACKNOWLEDGED;
if ( writeConcern == null ) {
throw log.unableToSetWriteConcern( cfgWC.toString() );
}
else {
log.useWriteConcern( writeConcern.toString() );
}

Object cfgTimeout = this.cfg.get( Environment.MONGODB_TIMEOUT );
Expand All @@ -132,13 +135,8 @@ public void start() {

MongoClientOptions.Builder optionsBuilder = new MongoClientOptions.Builder();
optionsBuilder.connectTimeout( timeout );
if ( safe ) {
optionsBuilder.writeConcern( WriteConcern.ACKNOWLEDGED );
}
else {
optionsBuilder.writeConcern( WriteConcern.NONE );
}
log.useSafe( safe );
optionsBuilder.writeConcern( writeConcern );

log.connectingToMongo( host, port, timeout );

ServerAddress serverAddress = new ServerAddress( host, port );
Expand Down
Expand Up @@ -20,6 +20,7 @@
*/
package org.hibernate.ogm.logging.mongodb.impl;

import static org.jboss.logging.Logger.Level.ERROR;
import static org.jboss.logging.Logger.Level.INFO;
import static org.jboss.logging.Logger.Level.TRACE;
import static org.jboss.logging.Logger.Level.WARN;
Expand Down Expand Up @@ -74,8 +75,8 @@ public interface Log extends org.hibernate.ogm.util.impl.Log {
void removedAssociation(int nAffected);

@LogMessage(level = INFO)
@Message(id = 1211, value = "The configuration property '" + Environment.MONGODB_SAFE + "' is set to %b")
void useSafe(boolean safe);
@Message(id = 1211, value = "The configuration property '" + Environment.MONGODB_WRITE_CONCERN + "' is set to %s")
void useWriteConcern(String writeConcern);

@Message(id = 1212, value = "Unknown association storage strategy: [%s]. Supported values in enum %s" )
HibernateException unknownAssociationStorageStrategy(String databaseName, Class<?> enumType);
Expand All @@ -88,4 +89,9 @@ public interface Log extends org.hibernate.ogm.util.impl.Log {

@Message( id = 1215, value = "The value set for the configuration property" + Environment.MONGODB_TIMEOUT +" must be a number greater than 0. Found '[%s]'.")
HibernateException mongoDBTimeOutIllegalValue(String value);

@Message(id = 1216, value = "'%s' cannot be set as an available value for " + Environment.MONGODB_WRITE_CONCERN +
" you must choose between [ACKNOWLEDGED, ERRORS_IGNORED, FSYNC_IGNORED, UNACKNOWLEDGED, FSYNCED, JOURNALED, REPLICA_ACKNOWLEDGED," +
"NONE, NORMAL, SAFE, MAJORITY, FSYNC_SAFE, JOURNAL_SAFE, REPLICAS_SAFE]")
HibernateException unableToSetWriteConcern(String value);
}
@@ -0,0 +1,88 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* JBoss, Home of Professional Open Source
* Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @authors tag. All rights reserved.
* See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License, v. 2.1.
* This program is distributed in the hope that it will be useful, but WITHOUT A
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public License,
* v.2.1 along with this distribution; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/

package org.hibernate.ogm.test.mongodb.datastore;

import java.util.Map;

import com.mongodb.WriteConcern;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;

import org.hibernate.HibernateException;
import org.hibernate.ogm.datastore.mongodb.Environment;
import org.hibernate.ogm.datastore.mongodb.impl.MongoDBDatastoreProvider;
import org.hibernate.ogm.test.utils.TestHelper;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.junit.internal.matchers.StringContains.containsString;

/**
* @author Guillaume Scheibel <guillaume.scheibel@gmail.com>
*/
public class WriteConcernTest {

@Rule
public ExpectedException error = ExpectedException.none();

private void runConfigurationTest(String property, WriteConcern expectedValue, String expectErrorCode) {
Map<String, String> cfg = TestHelper.getEnvironmentProperties();
cfg.put( Environment.MONGODB_HOST, "203.0.113.1" );
cfg.put( Environment.MONGODB_PORT, "27017");
cfg.put( Environment.MONGODB_DATABASE, "ogm_test_database" );
if ( property != null ) {
cfg.put( Environment.MONGODB_WRITE_CONCERN, property );
}
MongoDBDatastoreProvider provider = new MongoDBDatastoreProvider();
provider.configure( cfg );

// Because the host should not exist, we expect to get this exception
error.expect( HibernateException.class );
error.expectMessage( expectErrorCode );

provider.start();
try {
WriteConcern writeConcern = provider.mongo.getMongoOptions().getWriteConcern();
assertEquals( writeConcern, expectedValue );
} catch (HibernateException e) {
assertThat( e.getMessage(), containsString( "OGM001216" ) );
}
}

@Test
public void testNoConfiguration() {
this.runConfigurationTest( null, WriteConcern.ACKNOWLEDGED, "OGM001214" );
}

@Test
public void testWrongConfiguration() {
this.runConfigurationTest( "wrongValue", null, "OGM001203" );
}

@Test
public void testCorrectValue() {
this.runConfigurationTest( "JOURNAL_SAFE", WriteConcern.JOURNAL_SAFE, "OGM001214" );
}


}