/*
* Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. 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.synapse.commons.throttle.core;
import com.hazelcast.concurrent.atomiclong.AtomicLongService;
import com.hazelcast.core.DistributedObject;
import com.hazelcast.core.HazelcastInstance;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.synapse.commons.throttle.core.internal.ThrottleServiceDataHolder;
import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
/**
* This task is responsible for cleanup callers which has expired along with hazelcast shared params
*/
public class ThrottleDistributedInstancesCleanupTask {
private static final Log log = LogFactory.getLog(ThrottleDistributedInstancesCleanupTask.class);
private static final int CLEANUP_THREAD_POOL_SIZE = 1;
private static int cleanUpPoolSize = CLEANUP_THREAD_POOL_SIZE;
private boolean distributedCleanupEnabled = true;
private int maxNonAssociatedCounterCountToClear = 50000;
private long distributedInstanceExpiryMillis;
private long noOfTimestampObjectToBeCleared;
private ThrottleProperties throttleProperties;
public ThrottleDistributedInstancesCleanupTask() {
throttleProperties = ThrottleServiceDataHolder.getInstance().getThrottleProperties();
cleanUpPoolSize = Integer.parseInt(throttleProperties.getThrottleDistributedCleanupPoolSize());
noOfTimestampObjectToBeCleared = Long.parseLong(throttleProperties.getThrottleDistributedCleanupAmount());
distributedCleanupEnabled = Boolean.parseBoolean(throttleProperties.getThrottleDistributedCleanupTaskEnable());
maxNonAssociatedCounterCountToClear = Integer.parseInt(throttleProperties.getMaxNonAssociatedCounterCleanupAmount());
if (log.isDebugEnabled()) {
log.debug("Throttle window replicator pool size set to " + cleanUpPoolSize);
}
if (distributedCleanupEnabled) {
ScheduledExecutorService executor = Executors.newScheduledThreadPool(cleanUpPoolSize,
new ThreadFactory() {
public Thread newThread(
Runnable r) {
Thread t = new Thread(r);
t.setName(
"Throttle " +
"Distributed Cleanup" +
" Task");
return t;
}
});
String throttleFrequency = throttleProperties.getThrottleContextDistributedCleanupTaskFrequency();
String distributedInstanceExpiry = throttleProperties.getThrottleContextDistributedExpiredInstanceTime();
if (log.isDebugEnabled()) {
log.debug("Throttling Cleanup Task Frequency set to " + throttleFrequency);
}
executor.scheduleAtFixedRate(new CleanupTask(), Integer.parseInt(throttleFrequency),
Integer.parseInt(throttleFrequency), TimeUnit.MILLISECONDS);
distributedInstanceExpiryMillis = Long.parseLong(distributedInstanceExpiry);
}
}
private class CleanupTask implements Runnable {
public void run() {
Map<String, String> timestamps = new HashMap<String, String>();
Map<String, String> removedTimestamps = new HashMap<String, String>();
List<String> counters = new ArrayList<String>();
long removedCounterCount = 0;
long atomicLongCount = 0;
long removedAtomicTimestampCount = 0;
long start = 0;
long end;
if (log.isDebugEnabled()) {
log.debug("Running the distributed counter cleanup task");
start = System.currentTimeMillis();
}
String serviceName;
String name;
String counterKey;
HazelcastInstance hazelcastInstance = getHazelcastInstance();
if (hazelcastInstance != null && hazelcastInstance.getCluster().getMembers().iterator().next().localMember()) {
Collection<DistributedObject> distributedObjects = hazelcastInstance.getDistributedObjects();
if (log.isDebugEnabled()) {
log.debug("TOTAL NUMBER OF DISTRIBUTED OBJECTS BEFORE CLEAN-UP " + distributedObjects.size());
}
long currentTime = System.currentTimeMillis();
long timestamp;
for (DistributedObject distributedObject : distributedObjects) {
serviceName = distributedObject.getServiceName();
if (AtomicLongService.SERVICE_NAME.equals(serviceName)) {
name = distributedObject.getName();
if (name.contains(ThrottleConstants.THROTTLE_TIMESTAMP_KEY)) {
counterKey = name.split(ThrottleConstants.THROTTLE_TIMESTAMP_KEY)[1];
timestamp = SharedParamManager.getSharedTimestampWithFullId(name);
timestamps.put(counterKey, counterKey);
log.debug("ADDING TIMESTAMP:============" + counterKey);
log.debug("TIMESTAMP VALUE:============" + timestamp);
if (timestamp < (currentTime - distributedInstanceExpiryMillis) &&
(removedAtomicTimestampCount < noOfTimestampObjectToBeCleared)) {
log.debug("REMOVING TIMESTAMP:============" + counterKey);
SharedParamManager.removeTimestampWithFullId(name);
removedTimestamps.put(counterKey, counterKey);
removedAtomicTimestampCount++;
}
} else {
if (name.contains(ThrottleConstants.THROTTLE_SHARED_COUNTER_KEY)) {
log.debug("ADDING COUNTER:============" + name.split(ThrottleConstants.THROTTLE_SHARED_COUNTER_KEY)[1]);
counters.add(name.split(ThrottleConstants.THROTTLE_SHARED_COUNTER_KEY)[1]);
}
}
atomicLongCount++;
}
}
if (log.isDebugEnabled()) {
log.debug("TOTAL NUMBER OF DISTRIBUTED TIMESTAMP OBJECTS CLEARED " + removedTimestamps.size());
log.debug("TOTAL NUMBER OF DISTRIBUTED TIMESTAMP OBJECTS " + timestamps.size());
log.debug("TOTAL NUMBER OF DISTRIBUTED COUNTER OBJECTS " + counters.size());
}
int nonTimestampAssociatedCountersCount = 0;
for (String key : counters) {
if (timestamps.containsKey(key)) {
if (removedTimestamps.containsKey(key)) {
log.debug("REMOVING COUNTER:============" + key);
SharedParamManager.removeCounter(key);
removedCounterCount++;
}
} else {
if (nonTimestampAssociatedCountersCount < maxNonAssociatedCounterCountToClear) {
if (key.contains(ThrottleConstants.THROTTLE_SHARED_COUNTER_KEY)) {
SharedParamManager.removeCounter(key);
log.debug("NON MATCHING COUNTER:============" + key);
nonTimestampAssociatedCountersCount++;
removedCounterCount++;
}
}
}
}
if (log.isDebugEnabled()) {
log.debug("TOTAL NUMBER OF ATOMIC LONGS BEFORE CLEANUP" + atomicLongCount);
log.debug("TOTAL NUMBER OF DISTRIBUTED COUNTER OBJECTS CLEARED " + removedCounterCount);
log.debug("TOTAL NUMBER OF DISTRIBUTED TIMESTAMP OBJECTS CLEARED " + removedAtomicTimestampCount);
log.debug("TOTAL NUMBER OF DISTRIBUTED OBJECTS AFTER CLEAN-UP" + hazelcastInstance.getDistributedObjects().size());
log.debug("TOTAL NUMBER OF NON ASSOCIATED OBJECTS CLEANED" + nonTimestampAssociatedCountersCount);
end = System.currentTimeMillis();
log.debug("TIME TAKEN FOR CLEANUP" + (end - start));
}
}
}
}
private static HazelcastInstance getHazelcastInstance() {
return ThrottleServiceDataHolder.getInstance().getHazelCastInstance();
}
}