Added: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/mongo/replica/ReplicaSetInfoMock.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/mongo/replica/ReplicaSetInfoMock.java?rev=1751441&view=auto ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/mongo/replica/ReplicaSetInfoMock.java (added) +++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/mongo/replica/ReplicaSetInfoMock.java Tue Jul 5 11:11:13 2016 @@ -0,0 +1,145 @@ +/* + * 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.replica; + +import com.google.common.base.Function; +import com.mongodb.BasicDBObject; +import com.mongodb.DB; +import org.apache.jackrabbit.oak.plugins.document.Revision; +import org.apache.jackrabbit.oak.plugins.document.RevisionVector; +import org.apache.jackrabbit.oak.stats.Clock; +import org.bson.BasicBSONObject; +import org.mockito.Mockito; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static com.google.common.collect.Maps.transformValues; +import static java.util.Collections.emptyList; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class ReplicaSetInfoMock extends ReplicaSetInfo { + + private final Clock clock; + + private Clock mongoClock; + + private ReplicaSetMock replicationSet; + + public static ReplicaSetInfoMock create(Clock clock) { + DB db = mock(DB.class); + when(db.getName()).thenReturn("oak-db"); + when(db.getSisterDB(Mockito.anyString())).thenReturn(db); + return new ReplicaSetInfoMock(clock, db); + } + + private ReplicaSetInfoMock(Clock clock, DB db) { + super(clock, db, null, 0, Long.MAX_VALUE, null); + + this.clock = clock; + this.mongoClock = clock; + this.hiddenMembers = emptyList(); + } + + public void setMongoClock(Clock mongoClock) { + this.mongoClock = mongoClock; + } + + public RevisionBuilder addInstance(MemberState state, String name) { + if (replicationSet == null) { + replicationSet = new ReplicaSetMock(); + } + return replicationSet.addInstance(state, name); + } + + public void updateRevisions() { + updateReplicaStatus(); + for (ReplicaSetInfoListener listener : listeners) { + listener.gotRootRevisions(rootRevisions); + } + } + + @Override + protected BasicDBObject getReplicaStatus() { + BasicDBObject obj = new BasicDBObject(); + obj.put("date", mongoClock.getDate()); + obj.put("members", replicationSet.members); + return obj; + } + + @Override + protected Map<String, Timestamped<RevisionVector>> getRootRevisions(Iterable<String> hosts) { + return transformValues(replicationSet.memberRevisions, + new Function<RevisionBuilder, Timestamped<RevisionVector>>() { + @Override + public Timestamped<RevisionVector> apply(RevisionBuilder input) { + return new Timestamped<RevisionVector>(input.revs, clock.getTime()); + } + }); + } + + private class ReplicaSetMock { + + private List<BasicBSONObject> members = new ArrayList<BasicBSONObject>(); + + private Map<String, RevisionBuilder> memberRevisions = new HashMap<String, RevisionBuilder>(); + + private RevisionBuilder addInstance(MemberState state, String name) { + BasicBSONObject member = new BasicBSONObject(); + member.put("stateStr", state.name()); + member.put("name", name); + members.add(member); + + RevisionBuilder builder = new RevisionBuilder(); + memberRevisions.put(name, builder); + return builder; + } + } + + public static class RevisionBuilder { + + private RevisionVector revs = new RevisionVector(); + + public RevisionBuilder addRevisions(long... timestamps) { + for (int i = 0; i < timestamps.length; i++) { + addRevision(timestamps[i], 0, i, false); + } + return this; + } + + public RevisionBuilder addRevision(long timestamp, int counter, int clusterId, boolean branch) { + Revision rev = new Revision(timestamp, counter, clusterId, branch); + revs = revs.update(rev); + return this; + } + + public RevisionBuilder set(RevisionVector revs) { + this.revs = revs; + return this; + } + + public RevisionVector build() { + return revs; + } + } + +}
Added: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/mongo/replica/ReplicaSetInfoTest.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/mongo/replica/ReplicaSetInfoTest.java?rev=1751441&view=auto ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/mongo/replica/ReplicaSetInfoTest.java (added) +++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/mongo/replica/ReplicaSetInfoTest.java Tue Jul 5 11:11:13 2016 @@ -0,0 +1,151 @@ +/* + * 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.replica; + +import static java.lang.Long.MAX_VALUE; +import static org.apache.jackrabbit.oak.plugins.document.mongo.replica.ReplicaSetInfo.MemberState.PRIMARY; +import static org.apache.jackrabbit.oak.plugins.document.mongo.replica.ReplicaSetInfo.MemberState.RECOVERING; +import static org.apache.jackrabbit.oak.plugins.document.mongo.replica.ReplicaSetInfo.MemberState.SECONDARY; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import org.apache.jackrabbit.oak.plugins.document.RevisionVector; +import org.apache.jackrabbit.oak.stats.Clock; +import org.junit.Before; +import org.junit.Test; + +public class ReplicaSetInfoTest { + + private ReplicaSetInfoMock replica; + + private Clock.Virtual clock; + + @Before + public void resetEstimator() { + clock = new Clock.Virtual(); + replica = ReplicaSetInfoMock.create(clock); + } + + @Test + public void testMinimumRevision() { + replica.addInstance(PRIMARY, "mp").addRevisions(20, 18, 19); + replica.addInstance(SECONDARY, "m1").addRevisions(20, 18, 3); + replica.addInstance(SECONDARY, "m2").addRevisions(20, 1, 17); + replica.updateRevisions(); + + assertEquals(20, replica.getMinimumRootRevisions().getRevision(0).getTimestamp()); + assertEquals( 1, replica.getMinimumRootRevisions().getRevision(1).getTimestamp()); + assertEquals( 3, replica.getMinimumRootRevisions().getRevision(2).getTimestamp()); + + clock.waitUntil(38); + assertEquals(20, replica.getLag()); + } + + @Test + public void testIsMoreRecentThan() { + replica.addInstance(PRIMARY, "mp").addRevisions(15, 21, 22); + replica.addInstance(SECONDARY, "m1").addRevisions(10, 21, 11); + replica.addInstance(SECONDARY, "m2").addRevisions(15, 14, 13); + replica.addInstance(SECONDARY, "m3").addRevisions(14, 13, 22); + replica.updateRevisions(); + + assertTrue(replica.isMoreRecentThan(lastRev(9, 13, 10))); + assertFalse(replica.isMoreRecentThan(lastRev(11, 14, 10))); + } + + @Test + public void testUnknownStateIsNotSafe() { + replica.addInstance(PRIMARY, "mp"); + replica.addInstance(SECONDARY, "m1").addRevisions(10, 21, 11); + replica.addInstance(RECOVERING, "m2"); + replica.updateRevisions(); + + assertNull(replica.getMinimumRootRevisions()); + assertFalse(replica.isMoreRecentThan(lastRev(1, 1, 1))); + assertEquals(MAX_VALUE, replica.getLag()); + } + + @Test + public void testEmptyIsNotSafe() { + replica.addInstance(PRIMARY, "m1"); + replica.updateRevisions(); + + assertNull(replica.getMinimumRootRevisions()); + assertFalse(replica.isMoreRecentThan(lastRev(1, 1, 1))); + assertEquals(MAX_VALUE, replica.getLag()); + } + + @Test + public void testOldestNotReplicated() { + replica.addInstance(PRIMARY, "mp").addRevisions(10, 30, 30); + replica.addInstance(SECONDARY, "m1").addRevisions(10, 5, 30); + replica.addInstance(SECONDARY, "m2").addRevisions(2, 30, 30); + replica.updateRevisions(); + + assertEquals(10, replica.secondariesSafeTimestamp); + + clock.waitUntil(40); + assertEquals(30, replica.getLag()); + clock.waitUntil(50); + assertEquals(40, replica.getLag()); + } + + @Test + public void testAllSecondariesUpToDate() { + replica.addInstance(PRIMARY, "mp").addRevisions(10, 30, 30); + replica.addInstance(SECONDARY, "m1").addRevisions(10, 30, 30); + replica.addInstance(SECONDARY, "m2").addRevisions(10, 30, 30); + + long before = clock.getTime(); + replica.updateRevisions(); + long after = clock.getTime(); + + assertBetween(before, after, replica.secondariesSafeTimestamp); + } + + @Test + public void testAllSecondariesUpToDateWithTimediff() throws InterruptedException { + replica.addInstance(PRIMARY, "mp").addRevisions(10, 30, 30); + replica.addInstance(SECONDARY, "m1").addRevisions(10, 30, 30); + replica.addInstance(SECONDARY, "m2").addRevisions(10, 30, 30); + + Clock mongoClock = new Clock.Virtual(); + replica.setMongoClock(mongoClock); + mongoClock.waitUntil(100); + + long before = clock.getTime(); + replica.updateRevisions(); + long after = clock.getTime(); + + assertBetween(before, after, replica.secondariesSafeTimestamp); + } + + private static RevisionVector lastRev(long... timestamps) { + return new ReplicaSetInfoMock.RevisionBuilder().addRevisions(timestamps).build(); + } + + private static void assertBetween(long from, long to, long actual) { + final String msg = String.format("%d <= %d <= %d", from, actual, to); + assertTrue(msg, from <= actual); + assertTrue(msg, actual <= to); + } + +} \ No newline at end of file
