From 941641f3574087d8ca9a4426d346ba500ca97061 Mon Sep 17 00:00:00 2001 From: Robert von Burg Date: Wed, 21 Nov 2018 12:47:26 +0100 Subject: [PATCH] [New] Added pruning of old Locator locks --- .../agent/impl/DefaultLockHandler.java | 46 +++++++++++++++---- .../agent/impl/DefaultRealmHandler.java | 2 +- .../agent/impl/InternalStrolchRealm.java | 22 ++------- 3 files changed, 44 insertions(+), 26 deletions(-) diff --git a/li.strolch.agent/src/main/java/li/strolch/agent/impl/DefaultLockHandler.java b/li.strolch.agent/src/main/java/li/strolch/agent/impl/DefaultLockHandler.java index 2f8a61a4a..4e0cc81c1 100644 --- a/li.strolch.agent/src/main/java/li/strolch/agent/impl/DefaultLockHandler.java +++ b/li.strolch.agent/src/main/java/li/strolch/agent/impl/DefaultLockHandler.java @@ -16,14 +16,17 @@ package li.strolch.agent.impl; import java.text.MessageFormat; +import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; import li.strolch.agent.api.LockHandler; +import li.strolch.agent.api.StrolchAgent; import li.strolch.agent.api.StrolchLockException; import li.strolch.model.Locator; +import li.strolch.utils.collections.TypedTuple; import li.strolch.utils.dbc.DBC; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -38,10 +41,11 @@ public class DefaultLockHandler implements LockHandler { private String realm; private TimeUnit tryLockTimeUnit; private long tryLockTime; - private Map lockMap; + private Map> lockMap; - public DefaultLockHandler(String realm, TimeUnit tryLockTimeUnit, long tryLockTime) { + public DefaultLockHandler(StrolchAgent agent, String realm, TimeUnit tryLockTimeUnit, long tryLockTime) { + DBC.PRE.assertNotNull("agent must be set!", agent); //$NON-NLS-1$ DBC.PRE.assertNotEmpty("Realm must be set!", realm); //$NON-NLS-1$ DBC.PRE.assertNotNull("TimeUnit must be set!", tryLockTimeUnit); //$NON-NLS-1$ DBC.PRE.assertNotEquals("try lock time must not be 0", 0, tryLockTime); //$NON-NLS-1$ @@ -50,6 +54,27 @@ public class DefaultLockHandler implements LockHandler { this.tryLockTimeUnit = tryLockTimeUnit; this.tryLockTime = tryLockTime; this.lockMap = new ConcurrentHashMap<>(); + + agent.getScheduledExecutor().scheduleAtFixedRate(this::cleanupOldLocks, 1, 1, TimeUnit.HOURS); + } + + private void cleanupOldLocks() { + + Map> lockMap; + synchronized (this.lockMap) { + lockMap = new HashMap<>(this.lockMap); + } + + long maxAge = System.currentTimeMillis() - TimeUnit.HOURS.toMillis(1); + long count = 0; + for (Map.Entry> entry : lockMap.entrySet()) { + if (!entry.getValue().getFirst().isLocked() && entry.getValue().getSecond() <= maxAge) { + this.lockMap.remove(entry.getKey()); + count++; + } + } + + logger.info("Pruned " + count + " Locator locks."); } public String getRealm() { @@ -58,13 +83,15 @@ public class DefaultLockHandler implements LockHandler { @Override public void lock(Locator locator) throws StrolchLockException { - ReentrantLock lock = this.lockMap.computeIfAbsent(locator, l -> new ReentrantLock(true)); - lock(this.tryLockTimeUnit, this.tryLockTime, lock, locator); + TypedTuple tuple = this.lockMap + .computeIfAbsent(locator, l -> new TypedTuple<>(new ReentrantLock(true), System.currentTimeMillis())); + lock(this.tryLockTimeUnit, this.tryLockTime, tuple, locator); } @Override public void unlock(Locator locator) throws StrolchLockException { - ReentrantLock lock = this.lockMap.get(locator); + TypedTuple tuple = this.lockMap.get(locator); + ReentrantLock lock = tuple.getFirst(); if (lock == null || !lock.isHeldByCurrentThread()) { logger.error(MessageFormat.format("Trying to unlock not locked element {0}", locator)); //$NON-NLS-1$ } else { @@ -75,7 +102,8 @@ public class DefaultLockHandler implements LockHandler { @Override public void releaseLock(Locator locator) throws StrolchLockException { - ReentrantLock lock = this.lockMap.get(locator); + TypedTuple tuple = this.lockMap.get(locator); + ReentrantLock lock = tuple.getFirst(); if (lock == null) { logger.error(MessageFormat.format("Trying to unlock not locked element {0}", locator)); } else if (!lock.isHeldByCurrentThread()) { @@ -92,16 +120,18 @@ public class DefaultLockHandler implements LockHandler { /** * @see java.util.concurrent.locks.ReentrantLock#tryLock(long, TimeUnit) */ - private void lock(TimeUnit timeUnit, long tryLockTime, ReentrantLock lock, Locator locator) + private void lock(TimeUnit timeUnit, long tryLockTime, TypedTuple tuple, Locator locator) throws StrolchLockException { try { - if (!lock.tryLock(tryLockTime, timeUnit)) { + if (!tuple.getFirst().tryLock(tryLockTime, timeUnit)) { String msg = "Failed to acquire lock after {0}s for {1}"; //$NON-NLS-1$ msg = MessageFormat.format(msg, timeUnit.toSeconds(tryLockTime), locator); throw new StrolchLockException(msg); } + tuple.setSecond(System.currentTimeMillis()); + // logger.debug("locked " + locator); //$NON-NLS-1$ } catch (InterruptedException e) { diff --git a/li.strolch.agent/src/main/java/li/strolch/agent/impl/DefaultRealmHandler.java b/li.strolch.agent/src/main/java/li/strolch/agent/impl/DefaultRealmHandler.java index a747eb079..eaf448ba9 100644 --- a/li.strolch.agent/src/main/java/li/strolch/agent/impl/DefaultRealmHandler.java +++ b/li.strolch.agent/src/main/java/li/strolch/agent/impl/DefaultRealmHandler.java @@ -75,7 +75,7 @@ public class DefaultRealmHandler extends StrolchComponent implements RealmHandle @Override public void setup(ComponentConfiguration configuration) { - this.realms = new HashMap<>(); + this.realms = new HashMap<>(1); String[] realms = configuration.getStringArray(PROP_REALMS, StrolchConstants.DEFAULT_REALM); for (String realmName : realms) { diff --git a/li.strolch.agent/src/main/java/li/strolch/agent/impl/InternalStrolchRealm.java b/li.strolch.agent/src/main/java/li/strolch/agent/impl/InternalStrolchRealm.java index 6233ade8c..e2bcee1ae 100644 --- a/li.strolch.agent/src/main/java/li/strolch/agent/impl/InternalStrolchRealm.java +++ b/li.strolch.agent/src/main/java/li/strolch/agent/impl/InternalStrolchRealm.java @@ -15,31 +15,19 @@ */ package li.strolch.agent.impl; -import static li.strolch.agent.impl.DefaultRealmHandler.PROP_ENABLED_DELAYED_OBSERVER_UPDATES; -import static li.strolch.agent.impl.DefaultRealmHandler.PROP_ENABLE_AUDIT_TRAIL; -import static li.strolch.agent.impl.DefaultRealmHandler.PROP_ENABLE_AUDIT_TRAIL_FOR_READ; -import static li.strolch.agent.impl.DefaultRealmHandler.PROP_ENABLE_OBSERVER_UPDATES; -import static li.strolch.agent.impl.DefaultRealmHandler.PROP_ENABLE_VERSIONING; +import static li.strolch.agent.impl.DefaultRealmHandler.*; import static li.strolch.runtime.StrolchConstants.makeRealmKey; import java.text.MessageFormat; import java.util.concurrent.TimeUnit; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import li.strolch.agent.api.ActivityMap; -import li.strolch.agent.api.AuditTrail; -import li.strolch.agent.api.ComponentContainer; -import li.strolch.agent.api.LockHandler; -import li.strolch.agent.api.ObserverHandler; -import li.strolch.agent.api.OrderMap; -import li.strolch.agent.api.ResourceMap; -import li.strolch.agent.api.StrolchRealm; +import li.strolch.agent.api.*; import li.strolch.model.Locator; import li.strolch.privilege.model.PrivilegeContext; import li.strolch.runtime.configuration.ComponentConfiguration; import li.strolch.utils.dbc.DBC; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * @author Robert von Burg @@ -115,7 +103,7 @@ public abstract class InternalStrolchRealm implements StrolchRealm { String propTryLockTime = makeRealmKey(this.realm, PROP_TRY_LOCK_TIME); TimeUnit timeUnit = TimeUnit.valueOf(configuration.getString(propTryLockTimeUnit, TimeUnit.SECONDS.name())); long time = configuration.getLong(propTryLockTime, 10L); - this.lockHandler = new DefaultLockHandler(this.realm, timeUnit, time); + this.lockHandler = new DefaultLockHandler(this.container.getAgent(), this.realm, timeUnit, time); // versioning String enableVersioningKey = makeRealmKey(getRealm(), PROP_ENABLE_VERSIONING);