| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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. |
| */ |
| package org.apache.jackrabbit.oak.plugins.document.mongo; |
| |
| import java.util.List; |
| |
| import org.apache.jackrabbit.oak.plugins.document.AbstractMongoConnectionTest; |
| import org.apache.jackrabbit.oak.plugins.document.Collection; |
| import org.apache.jackrabbit.oak.plugins.document.Document; |
| import org.apache.jackrabbit.oak.plugins.document.DocumentMK; |
| import org.apache.jackrabbit.oak.plugins.document.DocumentStoreException; |
| import org.apache.jackrabbit.oak.plugins.document.MongoUtils; |
| import org.apache.jackrabbit.oak.plugins.document.NodeDocument; |
| import org.apache.jackrabbit.oak.plugins.document.util.Utils; |
| import org.jetbrains.annotations.NotNull; |
| import org.junit.Test; |
| |
| import com.mongodb.MongoClient; |
| import com.mongodb.MongoException; |
| import com.mongodb.client.MongoDatabase; |
| |
| import static org.apache.jackrabbit.oak.plugins.document.Collection.NODES; |
| import static org.hamcrest.collection.IsEmptyCollection.empty; |
| import static org.hamcrest.core.Is.is; |
| import static org.junit.Assert.assertNull; |
| import static org.junit.Assert.assertThat; |
| import static org.junit.Assert.fail; |
| |
| /** |
| * Tests retry logic in MongoDocumentStore (OAK-1641). |
| */ |
| public class RetryReadIT extends AbstractMongoConnectionTest { |
| |
| private TestStore store; |
| |
| @Override |
| public void setUpConnection() throws Exception { |
| mongoConnection = connectionFactory.getConnection(); |
| MongoUtils.dropCollections(mongoConnection.getDatabase()); |
| DocumentMK.Builder builder = new DocumentMK.Builder(); |
| builder.clock(getTestClock()); |
| store = new TestStore(mongoConnection.getMongoClient(), mongoConnection.getDatabase(), builder); |
| mk = builder.setDocumentStore(store).open(); |
| } |
| |
| @Test |
| public void retry() { |
| // must survive two consecutive failures. -> 2 retries |
| store.failRead = 2; |
| NodeDocument doc = store.find(NODES, Utils.getIdFromPath("/foo")); |
| assertNull(doc); |
| // previous result is cached and will not fail |
| store.failRead = 3; |
| doc = store.find(NODES, Utils.getIdFromPath("/foo")); |
| assertNull(doc); |
| // must fail with three consecutive failures on unknown path |
| try { |
| store.find(NODES, Utils.getIdFromPath("/bar")); |
| fail("must fail with DocumentStoreException"); |
| } catch (DocumentStoreException e) { |
| // expected |
| } |
| } |
| |
| @Test |
| public void retryQuery() { |
| String fromKey = Utils.getKeyLowerLimit("/foo"); |
| String toKey = Utils.getKeyUpperLimit("/foo"); |
| // must survive two consecutive failures. -> 2 retries |
| store.failRead = 2; |
| List<NodeDocument> docs = store.query(NODES, fromKey, toKey, 100); |
| assertThat(docs, is(empty())); |
| |
| fromKey = Utils.getKeyLowerLimit("/bar"); |
| toKey = Utils.getKeyUpperLimit("/bar"); |
| // must fail with three consecutive failures |
| store.failRead = 3; |
| try { |
| store.query(NODES, fromKey, toKey, 100); |
| fail("must fail with DocumentStoreException"); |
| } catch (DocumentStoreException e) { |
| // expected |
| } finally { |
| store.failRead = 0; |
| } |
| } |
| |
| private static class TestStore extends MongoDocumentStore { |
| |
| private int failRead = 0; |
| |
| public TestStore(MongoClient client, MongoDatabase db, DocumentMK.Builder builder) { |
| super(client, db, builder); |
| } |
| |
| @Override |
| protected <T extends Document> T findUncached(Collection<T> collection, |
| String key, |
| DocumentReadPreference docReadPref) { |
| maybeFail(); |
| return super.findUncached(collection, key, docReadPref); |
| } |
| |
| @NotNull |
| @Override |
| protected <T extends Document> List<T> queryInternal(Collection<T> collection, |
| String fromKey, |
| String toKey, |
| String indexedProperty, |
| long startValue, |
| int limit, |
| long maxQueryTime) { |
| maybeFail(); |
| return super.queryInternal(collection, fromKey, toKey, |
| indexedProperty, startValue, limit, maxQueryTime); |
| } |
| |
| private void maybeFail() { |
| if (failRead > 0) { |
| failRead--; |
| throw new MongoException("read failed"); |
| } |
| } |
| } |
| } |