| /* |
| * 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; |
| |
| import org.apache.jackrabbit.oak.api.CommitFailedException; |
| import org.apache.jackrabbit.oak.plugins.document.memory.MemoryDocumentStore; |
| import org.apache.jackrabbit.oak.spi.commit.CommitHook; |
| import org.apache.jackrabbit.oak.spi.commit.CommitInfo; |
| import org.apache.jackrabbit.oak.spi.commit.EmptyHook; |
| import org.apache.jackrabbit.oak.spi.state.NodeBuilder; |
| import org.apache.jackrabbit.oak.spi.state.NodeState; |
| import org.apache.jackrabbit.oak.stats.Clock; |
| import org.junit.AfterClass; |
| import org.junit.Before; |
| import org.junit.Rule; |
| import org.junit.Test; |
| import org.mockito.Mockito; |
| |
| import static org.apache.jackrabbit.oak.plugins.document.TestUtils.merge; |
| import static org.junit.Assert.assertTrue; |
| import static org.junit.Assert.fail; |
| import static org.mockito.ArgumentMatchers.any; |
| import static org.mockito.ArgumentMatchers.anyInt; |
| import static org.mockito.ArgumentMatchers.anyLong; |
| import static org.mockito.ArgumentMatchers.argThat; |
| import static org.mockito.ArgumentMatchers.eq; |
| import static org.mockito.Mockito.mock; |
| import static org.mockito.Mockito.times; |
| import static org.mockito.Mockito.verify; |
| |
| public class DocumentNodeStoreStatsCollectorIT { |
| @Rule |
| public DocumentMKBuilderProvider builderProvider = new DocumentMKBuilderProvider(); |
| |
| private DocumentNodeStoreStatsCollector statsCollector = mock(DocumentNodeStoreStatsCollector.class); |
| |
| private Clock clock; |
| |
| private DocumentStore store; |
| |
| private DocumentNodeStore nodeStore; |
| |
| @Before |
| public void setUp() throws Exception { |
| clock = new Clock.Virtual(); |
| clock.waitUntil(System.currentTimeMillis()); |
| Revision.setClock(clock); |
| ClusterNodeInfo.setClock(clock); |
| store = new MemoryDocumentStore(); |
| nodeStore = builderProvider.newBuilder() |
| .clock(clock) |
| .setDocumentStore(store) |
| .setAsyncDelay(0) |
| .setNodeStoreStatsCollector(statsCollector) |
| .setUpdateLimit(10) |
| .getNodeStore(); |
| // do not retry failed merges |
| nodeStore.setMaxBackOffMillis(0); |
| } |
| |
| @AfterClass |
| public static void reset() { |
| Revision.resetClockToDefault(); |
| ClusterNodeInfo.resetClockToDefault(); |
| } |
| |
| @Test |
| public void doneBackgroundRead() { |
| nodeStore.runBackgroundReadOperations(); |
| verify(statsCollector).doneBackgroundRead(any(BackgroundReadStats.class)); |
| } |
| |
| @Test |
| public void doneBackgroundUpdate() { |
| nodeStore.runBackgroundUpdateOperations(); |
| verify(statsCollector).doneBackgroundUpdate(any(BackgroundWriteStats.class)); |
| } |
| |
| @Test |
| public void doneMerge() throws Exception { |
| NodeBuilder nb = nodeStore.getRoot().builder(); |
| nb.child("a"); |
| nodeStore.merge(nb, EmptyHook.INSTANCE, CommitInfo.EMPTY); |
| verify(statsCollector).doneMerge(eq(0), anyLong(), eq(false), eq(false)); |
| } |
| |
| @Test |
| public void failedMerge() { |
| CommitHook failingHook = new CommitHook() { |
| @Override |
| public NodeState processCommit(NodeState before, NodeState after, |
| CommitInfo info) throws CommitFailedException { |
| |
| throw new CommitFailedException(CommitFailedException.MERGE, 0 , ""); |
| |
| } |
| }; |
| |
| NodeBuilder nb1 = nodeStore.getRoot().builder(); |
| nb1.child("a"); |
| |
| try { |
| nodeStore.merge(nb1, failingHook, CommitInfo.EMPTY); |
| fail(); |
| } catch (CommitFailedException ignore){ |
| |
| } |
| |
| verify(statsCollector).failedMerge(anyInt(), anyLong(), eq(false), eq(false)); |
| |
| //Should be called once more with exclusive lock |
| verify(statsCollector).failedMerge(anyInt(), anyLong(), eq(false), eq(true)); |
| } |
| |
| @Test |
| public void branchCommit() throws Exception { |
| int updateLimit = nodeStore.getUpdateLimit(); |
| NodeBuilder nb = nodeStore.getRoot().builder(); |
| for (int i = 0; i < updateLimit; i++) { |
| nb.child("node-" + i).setProperty("p", "v"); |
| } |
| merge(nodeStore, nb); |
| |
| verify(statsCollector, times(2)).doneBranchCommit(); |
| verify(statsCollector).doneMergeBranch(2); |
| } |
| |
| @Test |
| public void leaseUpdate() throws Exception { |
| clock.waitUntil(clock.getTime() + ClusterNodeInfo.DEFAULT_LEASE_UPDATE_INTERVAL_MILLIS * 2); |
| assertTrue(nodeStore.renewClusterIdLease()); |
| verify(statsCollector).doneLeaseUpdate(anyLong()); |
| } |
| |
| @Test |
| public void externalChangesLag() throws Exception { |
| // start a second cluster node |
| DocumentNodeStore ns2 = builderProvider.newBuilder() |
| .setDocumentStore(store) |
| .clock(clock) |
| .setClusterId(2) |
| .setAsyncDelay(0) |
| .getNodeStore(); |
| ns2.runBackgroundOperations(); |
| nodeStore.runBackgroundOperations(); |
| |
| NodeBuilder nb = ns2.getRoot().builder(); |
| nb.child("test"); |
| merge(ns2, nb); |
| ns2.runBackgroundOperations(); |
| |
| Mockito.reset(statsCollector); |
| |
| long lag = 2000; |
| // wait two seconds |
| clock.waitUntil(clock.getTime() + lag); |
| // then run background read |
| nodeStore.runBackgroundReadOperations(); |
| |
| verify(statsCollector).doneBackgroundRead(argThat( |
| // a bit more than 2000 ms |
| stats -> stats.externalChangesLag > lag |
| && stats.externalChangesLag - lag < 100 |
| )); |
| } |
| } |