Author: mduerig
Date: Wed Feb 25 23:14:08 2015
New Revision: 1662323
URL: http://svn.apache.org/r1662323
Log:
OAK-2407: Auto-refresh sessions on revision gc
Register GCMonitor to detect compaction and auto-refresh
Added:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/gc/package-info.java
jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/RefreshOnGCTest.java
Modified:
jackrabbit/oak/trunk/oak-core/pom.xml
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/gc/GCMonitor.java
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/repository/RepositoryImpl.java
Modified: jackrabbit/oak/trunk/oak-core/pom.xml
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/pom.xml?rev=1662323&r1=1662322&r2=1662323&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/pom.xml (original)
+++ jackrabbit/oak/trunk/oak-core/pom.xml Wed Feb 25 23:14:08 2015
@@ -85,6 +85,7 @@
org.apache.jackrabbit.oak.plugins.value,
org.apache.jackrabbit.oak.plugins.version,
org.apache.jackrabbit.oak.spi.commit,
+ org.apache.jackrabbit.oak.spi.gc,
org.apache.jackrabbit.oak.spi.lifecycle,
org.apache.jackrabbit.oak.spi.query,
org.apache.jackrabbit.oak.spi.security,
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/gc/GCMonitor.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/gc/GCMonitor.java?rev=1662323&r1=1662322&r2=1662323&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/gc/GCMonitor.java
(original)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/gc/GCMonitor.java
Wed Feb 25 23:14:08 2015
@@ -83,4 +83,13 @@ public interface GCMonitor {
* @param currentSize number of bytes after garbage collection
*/
void cleaned(long reclaimedSize, long currentSize);
+
+ class Empty implements GCMonitor {
+ @Override public void info(String message, Object[] arguments) { }
+ @Override public void warn(String message, Object[] arguments) { }
+ @Override public void error(String message, Exception e) { }
+ @Override public void skipped(String reason, Object[] arguments) { }
+ @Override public void compacted() { }
+ @Override public void cleaned(long reclaimedSize, long currentSize) { }
+ }
}
Added:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/gc/package-info.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/gc/package-info.java?rev=1662323&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/gc/package-info.java
(added)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/gc/package-info.java
Wed Feb 25 23:14:08 2015
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+
+@Version("1.0")
+@Export(optional = "provide:=true")
+package org.apache.jackrabbit.oak.spi.gc;
+
+import aQute.bnd.annotation.Export;
+import aQute.bnd.annotation.Version;
\ No newline at end of file
Modified:
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/repository/RepositoryImpl.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/repository/RepositoryImpl.java?rev=1662323&r1=1662322&r2=1662323&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/repository/RepositoryImpl.java
(original)
+++
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/repository/RepositoryImpl.java
Wed Feb 25 23:14:08 2015
@@ -22,7 +22,6 @@ import static java.util.Collections.sing
import static
org.apache.jackrabbit.oak.spi.whiteboard.WhiteboardUtils.registerMBean;
import java.io.Closeable;
-import java.util.Collections;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ScheduledExecutorService;
@@ -44,7 +43,6 @@ import javax.jcr.Value;
import javax.security.auth.login.LoginException;
import com.google.common.collect.ImmutableMap;
-
import org.apache.commons.io.IOUtils;
import org.apache.jackrabbit.api.JackrabbitRepository;
import
org.apache.jackrabbit.api.security.authentication.token.TokenCredentials;
@@ -54,9 +52,11 @@ import org.apache.jackrabbit.oak.api.Con
import org.apache.jackrabbit.oak.api.jmx.SessionMBean;
import org.apache.jackrabbit.oak.jcr.delegate.SessionDelegate;
import org.apache.jackrabbit.oak.jcr.session.RefreshStrategy;
+import org.apache.jackrabbit.oak.jcr.session.RefreshStrategy.Composite;
import org.apache.jackrabbit.oak.jcr.session.SessionContext;
import org.apache.jackrabbit.oak.jcr.session.SessionStats;
import org.apache.jackrabbit.oak.plugins.observation.CommitRateLimiter;
+import org.apache.jackrabbit.oak.spi.gc.GCMonitor;
import org.apache.jackrabbit.oak.spi.security.SecurityProvider;
import org.apache.jackrabbit.oak.spi.whiteboard.Registration;
import org.apache.jackrabbit.oak.spi.whiteboard.Whiteboard;
@@ -244,7 +244,7 @@ public class RepositoryImpl implements J
@CheckForNull Map<String, Object> attributes) throws
RepositoryException {
try {
if (attributes == null) {
- attributes = Collections.emptyMap();
+ attributes = emptyMap();
}
Long refreshInterval = getRefreshInterval(credentials);
if (refreshInterval == null) {
@@ -254,7 +254,9 @@ public class RepositoryImpl implements J
}
boolean relaxedLocking = getRelaxedLocking(attributes);
- RefreshStrategy refreshStrategy =
createRefreshStrategy(refreshInterval);
+ RefreshStrategy refreshStrategy = refreshInterval == null
+ ? new RefreshStrategy.LogOnce(60)
+ : new RefreshStrategy.Timed(refreshInterval);
ContentSession contentSession =
contentRepository.login(credentials, workspaceName);
SessionDelegate sessionDelegate =
createSessionDelegate(refreshStrategy, contentSession);
SessionContext context = createSessionContext(
@@ -268,8 +270,12 @@ public class RepositoryImpl implements J
}
private SessionDelegate createSessionDelegate(
- final RefreshStrategy refreshStrategy,
- final ContentSession contentSession) {
+ RefreshStrategy refreshStrategy,
+ ContentSession contentSession) {
+
+ final RefreshOnGC refreshOnGC = new RefreshOnGC();
+ refreshStrategy = new Composite(refreshStrategy, refreshOnGC);
+
return new SessionDelegate(
contentSession, securityProvider, refreshStrategy,
threadSaveCount, statisticManager, clock) {
@@ -280,6 +286,7 @@ public class RepositoryImpl implements J
@Override
public void logout() {
+ refreshOnGC.close();
// Cancel session MBean registration
registrationTask.cancel();
scheduledTask.cancel(false);
@@ -444,31 +451,35 @@ public class RepositoryImpl implements J
}
}
- /**
- * Auto refresh logic for sessions, which is done to enhance backwards
compatibility with
- * Jackrabbit 2.
- * <p>
- * A sessions is automatically refreshed when
- * <ul>
- * <li>it has not been accessed for the number of seconds specified by
the
- * {@code refreshInterval} parameter,</li>
- * <li>an observation event has been delivered to a listener
registered from within this
- * session,</li>
- * <li>an updated occurred through a different session from <em>within
the same
- * thread.</em></li>
- * </ul>
- * In addition a warning is logged once per session if the session is
accessed after one
- * minute of inactivity.
- */
- private RefreshStrategy createRefreshStrategy(Long refreshInterval) {
- if (refreshInterval == null) {
- return new RefreshStrategy.LogOnce(60);
- } else {
- return new RefreshStrategy.Timed(refreshInterval);
+ private class RefreshOnGC implements RefreshStrategy {
+ private final GCMonitor gcMonitor = new GCMonitor.Empty() {
+ @Override
+ public void compacted() {
+ compactionCount++;
+ }
+ };
+
+ private final Registration reg = whiteboard.register(GCMonitor.class,
gcMonitor, emptyMap());
+
+ private volatile int compactionCount;
+ private int refreshCount;
+
+ public void close() {
+ reg.unregister();
+ }
+
+ @Override
+ public boolean needsRefresh(long secondsSinceLastAccess) {
+ if (compactionCount > refreshCount) {
+ refreshCount = compactionCount;
+ return true;
+ } else {
+ return false;
+ }
}
}
- static class RegistrationTask implements Runnable {
+ private static class RegistrationTask implements Runnable {
private final SessionStats sessionStats;
private final Whiteboard whiteboard;
private boolean cancelled;
Added:
jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/RefreshOnGCTest.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/RefreshOnGCTest.java?rev=1662323&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/RefreshOnGCTest.java
(added)
+++
jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/RefreshOnGCTest.java
Wed Feb 25 23:14:08 2015
@@ -0,0 +1,134 @@
+/*
+ * 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.jcr;
+
+import static java.io.File.createTempFile;
+import static
org.apache.jackrabbit.oak.plugins.segment.compaction.CompactionStrategy.CleanupType.CLEAN_NONE;
+import static
org.apache.jackrabbit.oak.plugins.segment.file.FileStore.newFileStore;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.FutureTask;
+
+import javax.annotation.Nonnull;
+import javax.jcr.Node;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.SimpleCredentials;
+
+import org.apache.jackrabbit.api.JackrabbitRepository;
+import org.apache.jackrabbit.oak.Oak;
+import org.apache.jackrabbit.oak.plugins.segment.SegmentNodeStore;
+import org.apache.jackrabbit.oak.plugins.segment.compaction.CompactionStrategy;
+import org.apache.jackrabbit.oak.plugins.segment.file.FileStore;
+import org.apache.jackrabbit.oak.spi.state.NodeStore;
+import org.apache.jackrabbit.oak.spi.whiteboard.DefaultWhiteboard;
+import org.apache.jackrabbit.oak.spi.whiteboard.Whiteboard;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class RefreshOnGCTest {
+ private FileStore fileStore;
+ private Repository repository;
+
+ @Before
+ public void setup() throws IOException {
+ File directory = createTempFile(getClass().getSimpleName(), "test",
new File("target"));
+ directory.delete();
+ directory.mkdir();
+
+ Whiteboard whiteboard = new DefaultWhiteboard();
+ fileStore = newFileStore(directory)
+ .withWhiteBoard(whiteboard)
+ .create()
+ .setCompactionStrategy(new CompactionStrategy(
+ false, false, CLEAN_NONE, 0,
CompactionStrategy.MEMORY_THRESHOLD_DEFAULT) {
+ @Override
+ public boolean compacted(@Nonnull Callable<Boolean>
setHead) throws Exception {
+ setHead.call();
+ return true;
+ }
+ });
+
+ NodeStore nodeStore = new SegmentNodeStore(fileStore);
+ Oak oak = new Oak(nodeStore);
+ oak.with(whiteboard);
+ repository = new Jcr(oak).createRepository();
+ }
+
+ @After
+ public void tearDown() {
+ if (repository instanceof JackrabbitRepository) {
+ ((JackrabbitRepository) repository).shutdown();
+ }
+ }
+
+ @Test
+ public void compactionCausesRefresh() throws RepositoryException,
InterruptedException, ExecutionException {
+ Session session = repository.login(new SimpleCredentials("admin",
"admin".toCharArray()));
+ try {
+ Node root = session.getRootNode();
+ root.addNode("one");
+ session.save();
+
+ addNode(repository, "two");
+
+ fileStore.compact();
+ assertTrue(root.hasNode("one"));
+ assertTrue("Node two must be visible as compaction should cause
the session to refresh",
+ root.hasNode("two"));
+ } finally {
+ session.logout();
+ }
+ }
+
+ private static void addNode(final Repository repository, final String name)
+ throws ExecutionException, InterruptedException {
+ // Execute on different threads to ensure same thread session
+ // refreshing doesn't come into our way
+ run(new Callable<Void>() {
+ @Override
+ public Void call() throws Exception {
+ Session session = repository.login(new
SimpleCredentials("admin", "admin".toCharArray()));
+ try {
+ Node root = session.getRootNode();
+ root.addNode(name);
+ session.save();
+ } finally {
+ session.logout();
+ }
+ return null;
+ }
+ });
+ }
+
+ private static void run(Callable<Void> callable) throws
InterruptedException, ExecutionException {
+ FutureTask<Void> task = new FutureTask<Void>(callable);
+ new Thread(task).start();
+ task.get();
+ }
+
+
+}