Skip to content

Commit

Permalink
Guard against duplication key error from MongoDB (#3358)
Browse files Browse the repository at this point in the history
Due to https://jira.mongodb.org/browse/SERVER-14322 concurrent upserts can fail leading to NPEs during session updates.
These are generally harmless, as one of the concurrent updates will succeed (and all end up expiring the session anyway), but to at least handle the problem, we'll retry the request as suggested in the MongoDB bug report.

Fixes #3335
  • Loading branch information
kroepke authored and joschi committed Jan 16, 2017
1 parent 1540671 commit d46d047
Showing 1 changed file with 29 additions and 3 deletions.
Expand Up @@ -18,17 +18,28 @@

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

import com.github.rholder.retry.RetryException;
import com.github.rholder.retry.Retryer;
import com.github.rholder.retry.RetryerBuilder;
import com.github.rholder.retry.StopStrategies;
import com.github.rholder.retry.WaitStrategies;
import com.mongodb.DuplicateKeyException;

import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.SimpleSession;
import org.apache.shiro.session.mgt.eis.CachingSessionDAO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Inject;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

import javax.inject.Inject;

public class MongoDbSessionDAO extends CachingSessionDAO {
private static final Logger LOG = LoggerFactory.getLogger(MongoDbSessionDAO.class);
Expand Down Expand Up @@ -107,8 +118,23 @@ protected void doUpdate(Session session) {
} else {
throw new RuntimeException("Unsupported session type: " + session.getClass().getCanonicalName());
}

mongoDBSessionService.saveWithoutValidation(dbSession);
// Due to https://jira.mongodb.org/browse/SERVER-14322 upserts can fail under concurrency.
// We need to retry the update, and stagger them a bit, so no all of the retries attempt it at the same time again.
// Usually this should succeed the first time, though
final Retryer<Object> retryer = RetryerBuilder.newBuilder()
.retryIfExceptionOfType(DuplicateKeyException.class)
.withWaitStrategy(WaitStrategies.randomWait(5, TimeUnit.MILLISECONDS))
.withStopStrategy(StopStrategies.stopAfterAttempt(10))
.build();
try {
retryer.call(() -> mongoDBSessionService.saveWithoutValidation(dbSession));
} catch (ExecutionException e) {
LOG.warn("Unexpected exception when saving session to MongoDB. Failed to update session.", e);
throw new RuntimeException(e.getCause());
} catch (RetryException e) {
LOG.warn("Tried to update session 10 times, but still failed. This is likely because of https://jira.mongodb.org/browse/SERVER-14322", e);
throw new RuntimeException(e.getCause());
}
}

@Override
Expand Down

0 comments on commit d46d047

Please sign in to comment.