Is catching MongoException necessary? - java

Suppose i have the following code:
...
this.mongoTemplate.getCollection(sessionId).remove(new BasicDBObject();
...
Hovering over remove, displays
Remove documents from a collection.
Parameters: query the deletion criteria using query operators. Omit
the query parameter or pass an empty document to delete all documents
in the collection. Returns: the result of the operation Throws:
com.mongodb.WriteConcernException - if the write failed due some other
failure specific to the delete command MongoException - if the
operation failed for some other reason #mongodb.driver.manual
tutorial/remove-documents/ Remove Documents
Compiler is not complaining about possible to be thrown exception, so i'm wondering if there's need to catch MongoException in these and many other calls of in mongodb java driver

Related

How should you handle MongoDB-Exceptions in Java?

I want to handle exceptions, which are thrown from a query (find(...).first()) to MongoDB (Driver 3.7) in Java (the database is not stored locally). However there are no possible exceptions named in the JavaDocs and also in the MongoDB documentaton itself. Can there really occur no exceptions? I doubt that, because I think there could occur e.g. some network errors.
My queries look something like this:
final MongoCollection<Document> collection = database.getCollection("my-collection");
final Bson bsonFilter = Filters.eq("someName", "test");
final Document result = collection.find(bsonFilter).first();
Consider the following code. It connects to a MongoDB instance locally and gets a collection named "test" from the database named "users".
final String connectionStr = "mongodb://localhost/";
MongoClient mongoClient = MongoClients.create("mongodb://localhost/");
MongoDatabase database = mongoClient.getDatabase("users");
MongoCollection<Document> collection = database.getCollection("test");
If you provide a wrong host name for the connectionStr value, like "mongodb://localhostXYZ/" (and no such host exists) the code will throw an exception, like:
com.mongodb.MongoSocketException: localhostXYZ},
caused by {java.net.UnknownHostException: localhostXYZ}}],
..., ...
com.mongodb.MongoSocketException is a MongoDB Java driver exception. It is a runtime exception. It is also a sub-class of MongoException. From the MongoDB Java API:
public class MongoException extends RuntimeException
Top level Exception for all Exceptions, server-side or client-side, that come
from the driver.
The documentation also lists the following are sub-classes (all are runtime exceptions)
MongoChangeStreamException, MongoClientException, MongoExecutionTimeoutException, MongoGridFSException, MongoIncompatibleDriverException, MongoInternalException, MongoInterruptedException, MongoServerException, MongoSocketException.
So, all the exceptions thrown by MongoDB Java driver APIs are runtime exceptions. These are, in general, not meant to be caught and handled (but, you know how to use try-catch, and a runtime exception can be caught and handled).
Let us consider your code:
final MongoCollection<Document> collection = database.getCollection("my-collection");
final Bson bsonFilter = Filters.eq("someName", "test");
final Document result = collection.find(bsonFilter).first();
The first statement database.getCollection("my-collection"), when it runs the code is looking for a collection named "my-collection".
If you want to make sure the collection exists in the database, then verify using the listCollectionNames​() and check the collection name exists in the returned list. In case the collection name doesn't exist, you can throw an exception (if you want to). This exception is what you have figure:
if you want to tell the user or the application that there was no
such collection named "my-collection", you can show or print a
message saying so (and then abort the program) or throw a runtime
exception with an appropriate message.
So, the code might look like this:
if listCollectionNames​() doesn't contain "my-collection"
then
print something and abort the program
-or-
throw a runtime exception
else
continue with program execution
Your code final Document result = collection.find(bsonFilter).first(); is not correct. collection.find returns a FindIterable<TDocument> not a Document. So, the query output can be determined by further examining the FindIterable object; it may have documents or none. And, the find method doesn't throw any exceptions.
Based on if there are any documents returned or not you can show a message to the client. This is not a case you throw an exception.

When can NoSuchResult be thrown

I have inherited a system with a rather wierd bug that occurs maybe once every 6 months, where the application suddenly loses track of Database data.
The system has redundancy with 2 servers that are scheduled to run the same function at the same time. They both get the same input data to the function and they both talk to the same postgres database, however the behaviours is different on the 2 machines.
The function that is being executed is calling the database and checking if there is a row with the specified id as supplied by the input parameter and if there is it executes A(), otherwise B()
Problem is that one server executes A() and the other B(). I have searched everywhere and there is no code writing to this table or deleting from it. So within all reason I think that they should both execute the same code.
This is the code that is fetching from the database:
#PersistenceContext(unitName = "backend-persistence")
private EntityManager em;
public Optional<OfferEntity> getOfferFromOfferId(final long offerId, final String countryAlias, final String langauageAlias) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<OfferEntity> cq = cb.createQuery(OfferEntity.class);
Root<OfferEntity> from = cq.from(OfferEntity.class);
cq.select(from);
cq.where(cb.and(cb.equal(from.get(OfferEntity_.offerId), offerId),
cb.equal(from.get(OfferEntity_.country), countryAlias),
cb.equal(from.get(OfferEntity_.language), langauageAlias)));
try {
return Optional.of(em.createQuery(cq).getSingleResult());
} catch (NoResultException nre) {
return Optional.empty();
}
}
And I am getting an empty optional from one of the servers but not the other.
So I guess as a tl;dr, am I missunderstanding NoResultException and in what concrete situations can this be thrown? besides if there are no rows matching the query.
You can only use getSingleResult() when you are sure that you will get exactly one result. In all other cases you have to use getResultList()
From the API doc of javax.persistence.Query getSingleResult():
java.lang.Object getSingleResult()
Execute a SELECT query that returns a single untyped result.
Returns:
the result
Throws:
NoResultException - if there is no result
NonUniqueResultException - if more than one result
IllegalStateException - if called for a Java Persistence query language UPDATE or DELETE statement
QueryTimeoutException - if the query execution exceeds the query timeout value set and only the statement is rolled back
TransactionRequiredException - if a lock mode has been set and there is no transaction
PessimisticLockException - if pessimistic locking fails and the transaction is rolled back
LockTimeoutException - if pessimistic locking fails and only the statement is rolled back
PersistenceException - if the query execution exceeds the query timeout value set and the transaction is rolled back

IncorrectResultSizeDataAccessException when no entity is found. First returned when multiple are found

I'm trying to find the reason for one strange behavior. Lets say I have Spring data repository which contains a few methods. One of them has a signature like this:
public Entity findByEntityKeyAndValidToDateAfterAndCorrectionDateAfter(BigInteger entityKey, Timestamp validToDate, Timestamp correctionDate);
I receive an issue with stacktrace with the following error:
org.springframework.dao.IncorrectResultSizeDataAccessException
I decided to add two records which have to be returned when I execute this query.
After calling this method instead of throwing the exception the query returns only one of the results. This was strange to my that is why I copy the query from the log and execute it on my SQL developer with the same parameters. The query return both results. I try to change the method signature to return list of entities:
public List<Entity> findByEntityKeyAndValidToDateAfterAndCorrectionDateAfter(BigInteger entityKey, Timestamp validToDate, Timestamp correctionDate);
In this case, the query returns two results.
Any idea why this may happen? And why this query does not throw this exception?
Spring data version is 1.10.1.RELEASE
When using a find method returning a singular type, the following semantics should apply:
0 elements found => return null or Optional.empty if applicable.
1 element found => return that element.
more than 1 element found => exception should be thrown.
Since this is not what you seem to see please upgrade to at least the current minor version (1.10.11), but preferably to the current GA release(1.11.7). If the problem still persists please create an issue at https://jira.spring.io/browse/DATAJPA/?selectedTab=com.atlassian.jira.jira-projects-plugin:summary-panel

Consistency level ALL used while statement has consistency level TWO defined

We use java datastax cassandra driver 2.1.2. Cassandra version we use is 2.0.9.
We have statement which we build with QueryBuilder and we are setting consistency level to statement on TWO explicitly.
Select selectStatement = QueryBuilder.select().from(ARTICLES);
selectStatement.where(eq(ORGANIZATION_ID, organizationId));
selectStatement.setConsistencyLevel(ConsistencyLevel.TWO);
final ResultSet rs = session.execute(selectStatement);
//call to all() will be removed since it is enough to iterate over result set
//and then you get pagination for free instead of loading everything in
//memory
List<Row> rows = rs.all();
for (final Row row : rows) {
//do something with Row, convert to POJO
}
We get exception like this:
com.datastax.driver.core.exceptions.ReadTimeoutException: Cassandra timeout during read query at consistency ALL (3 responses were required but only 2 replica responded)
com.datastax.driver.core.exceptions.ReadTimeoutException.copy (ReadTimeoutException.java:69)
com.datastax.driver.core.DefaultResultSetFuture.extractCauseFromExecutionException (DefaultResultSetFuture.java:259)
com.datastax.driver.core.ArrayBackedResultSet$MultiPage.prepareNextRow (ArrayBackedResultSet.java:279)
com.datastax.driver.core.ArrayBackedResultSet$MultiPage.isExhausted (ArrayBackedResultSet.java:239)
com.datastax.driver.core.ArrayBackedResultSet$1.hasNext (ArrayBackedResultSet.java:122)
com.datastax.driver.core.ArrayBackedResultSet.all (ArrayBackedResultSet.java:111)
I know that calling all() on ResultSet makes it load all articles for organization in memory and work with it and creates load on cassandra. This will be removed as noted in comments. This can cause read timeout but I am still puzzled why in exception message there is ALL.
Question is why exception is telling that consistency level ALL is used when we set it to TWO for original statement. Is all() internally doing something with query and using CL ALL by default?
Your problem is almost certainly https://issues.apache.org/jira/browse/CASSANDRA-7947 . You are seeing an error message from failing to perform read repair. It is unrelated to your original consistency level. This is fixed in 2.1.3+.

MongoDB handling failure of insert in Java

I would like to handle failure of insert to collection (using Java) in order to be sure that my insert was successful. In case that insert fails, I want to perform some fall-back action.
Assuming following code in Java and latest mongo driver (version 2.11.3):
BasicDBObject doc = new BasicDBObject("name", "MongoDB");
WriteResult result = coll.insert(WriteConcern.SAFE, doc);
I am confused by the fact that the insert returns WriteResult and it could throw MongoException. What am I supposed to do in order to safely detect any possible failure of insert? Please, provide code example. And if you can clarify when insert throws exception and when it just returns some error write result. I tried to search in java driver API docs at http://api.mongodb.org/java/2.11.3/ for it; howerever, this infromation is missing there.
WriteResult result;
try {
result = coll.insert(WriteConcern.SAFE, doc);
}
catch(MongoException ex){
logger.warn("Insert failed.", ex);
throw ex;
}
//Shall I check result here for additional errors?
If I should check, what type of error I am able to detect by checking insert result?
You need to take a look at "WriteConcern", it has the all behaviors you need.
You can use it per one write like this:
coll.insert(dbObj, WriteConcern.SAFE);
If you use WriteConcern.SAFE your operation will wait for an acknowledgement from the primary server, so if no exception is raised then you're ok.
Or you can set default behaviour for all write operations when you are creating MongoClient:
MongoClientOptions.Builder builder = new MongoClientOptions.Builder();
builder.writeConcern(WriteConcern.JOURNAL_SAFE);
MongoClient mongoClient = new MongoClient(
new ServerAddress("localhost"), builder.build());
[Based on Colin Morelli's comment] If you don't use a WriteConcern that raises exceptions, you can use the WriteResult.getLastError() to determine if it was successful or not. Similarly, if you use WriteConcern.SAFE, and the write succeeds, WriteResult will have useful information on it such as the number of records that were written.
Here you can read about WriteConcern in general.

Categories