<commit_before>package brooklyn.policy

import java.util.concurrent.atomic.AtomicInteger
import java.util.concurrent.locks.ReentrantLock

import org.slf4j.Logger
import org.slf4j.LoggerFactory

import brooklyn.entity.Entity
import brooklyn.entity.basic.EntityLocal;
import brooklyn.entity.group.DynamicCluster
import brooklyn.event.AttributeSensor
import brooklyn.event.SensorEvent
import brooklyn.event.SensorEventListener;
import brooklyn.policy.basic.AbstractPolicy

public class ResizerPolicy<T extends Number> extends AbstractPolicy implements SensorEventListener<T> {
    
    private static final Logger LOG = LoggerFactory.getLogger(ResizerPolicy.class)
    
    private DynamicCluster dynamicCluster
    private String[] metricName
    private double metricLowerBound
    private double metricUpperBound
    private int minSize
    private int maxSize = Integer.MAX_VALUE
    
    private final AtomicInteger desiredSize = new AtomicInteger(0)
    private final ReentrantLock resizeLock = new ReentrantLock()
    
    AttributeSensor<T> source
    
    public ResizerPolicy(AttributeSensor<T> source) {
        this.source = source
    }
    
    @Override
    public void setEntity(Entity entity) {
        super.setEntity(entity)
        assert entity instanceof DynamicCluster
        this.dynamicCluster = entity
        subscribe(entity, source, this)
    }
    
    public ResizerPolicy setMetricLowerBound(double val) {
        this.metricLowerBound = val
        this
    }
    
    public ResizerPolicy setMetricUpperBound(double val) {
        this.metricUpperBound = val
        this
    }
    
    public ResizerPolicy setMinSize(int val) {
        this.minSize = val
        this
    }
    
    public ResizerPolicy setMaxSize(int val) {
        this.maxSize = val
        this
    }
    
    private int resize() {
        if (resizeLock.tryLock()) {
            try {
                // Groovy does not support do .. while loops!
                int desire = desiredSize.get()
                dynamicCluster.resize(desire)
                while (desire != desiredSize.get()) {
                    desire = desiredSize.get()
                    dynamicCluster.resize(desire)
                }
            } finally {
                resizeLock.unlock()
            }
        }        
    }
    
    public void onEvent(SensorEvent<T> event) {
        def val = event.getValue()
        def currentSize = dynamicCluster.getCurrentSize()
        desiredSize.set(calculateDesiredSize(val))
        
        if (desiredSize.get() != currentSize) {
            LOG.info(String.format("policy resizer resizing: metric=%s, workrate=%s, lowerBound=%s, upperBound=%s; currentSize=%d, desiredSize=%d, minSize=%d, maxSize=%d", 
                    Arrays.toString(metricName), val, metricLowerBound, metricUpperBound, currentSize, desiredSize.get(), minSize, maxSize))
            resize()
        } else {
            LOG.debug(String.format("policy resizer doing nothing: metric=%s, workrate=%s, lowerBound=%s, upperBound=%s; currentSize=%d, minSize=%d, maxSize=%d", 
                    Arrays.toString(metricName), val, metricLowerBound, metricUpperBound, currentSize, minSize, maxSize))
        }
    }
    
    // TODO Could have throttling etc so don't repeatedly call grow; standard control theory stuff such as
    //      PID design (proportional-integral-derivative)
    // TODO Could show example of overriding this to do something smarter. For example, if metric is a number then
    //      grow/shrink by some scale, e.g. grow by (metric <= lowerBound ? 0 : (metric < lowerBound*2 ? 1 : (metric < lowerBound*4 ? 2 : 3))) <commit_msg>Use AtomicBoolean instead of lock<commit_after>package brooklyn.policy

import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.atomic.AtomicInteger

import org.slf4j.Logger
import org.slf4j.LoggerFactory

import brooklyn.entity.Entity
import brooklyn.entity.group.DynamicCluster
import brooklyn.event.AttributeSensor
import brooklyn.event.SensorEvent
import brooklyn.event.SensorEventListener
import brooklyn.policy.basic.AbstractPolicy

public class ResizerPolicy<T extends Number> extends AbstractPolicy implements SensorEventListener<T> {
    
    private static final Logger LOG = LoggerFactory.getLogger(ResizerPolicy.class)
    
    private DynamicCluster dynamicCluster
    private String[] metricName
    private double metricLowerBound
    private double metricUpperBound
    private int minSize
    private int maxSize = Integer.MAX_VALUE
    
    private final AtomicInteger desiredSize = new AtomicInteger(0)
    
    /**
     * Set this to true if we are in the process of resizing.
     */
    private final AtomicBoolean resizing = new AtomicBoolean(false)
    
    AttributeSensor<T> source
    
    public ResizerPolicy(AttributeSensor<T> source) {
        this.source = source
    }
    
    @Override
    public void setEntity(Entity entity) {
        super.setEntity(entity)
        assert entity instanceof DynamicCluster
        this.dynamicCluster = entity
        subscribe(entity, source, this)
    }
    
    public ResizerPolicy setMetricLowerBound(double val) {
        this.metricLowerBound = val
        this
    }
    
    public ResizerPolicy setMetricUpperBound(double val) {
        this.metricUpperBound = val
        this
    }
    
    public ResizerPolicy setMinSize(int val) {
        this.minSize = val
        this
    }
    
    public ResizerPolicy setMaxSize(int val) {
        this.maxSize = val
        this
    }
    
    private int resize() {
        if (resizing.compareAndSet(false, true)) {
            try {
                // Groovy does not support do .. while loops!
                int desire = desiredSize.get()
                dynamicCluster.resize(desire)
                while (desire != desiredSize.get()) {
                    desire = desiredSize.get()
                    dynamicCluster.resize(desire)
                }
            } finally {
                resizing.set(false)
            }
        }        
    }
    
    public void onEvent(SensorEvent<T> event) {
        def val = event.getValue()
        def currentSize = dynamicCluster.getCurrentSize()
        desiredSize.set(calculateDesiredSize(val))
        
        if (desiredSize.get() != currentSize) {
            LOG.info "policy resizer resizing: metric={}, workrate={}, lowerBound={}, upperBound={}; currentSize={}, desiredSize={}, minSize={}, maxSize={}", 
                    Arrays.toString(metricName), val, metricLowerBound, metricUpperBound, currentSize, desiredSize.get(), minSize, maxSize
            resize()
        } else {
            LOG.debug "policy resizer doing nothing: metric={}, workrate={}, lowerBound={}, upperBound={}; currentSize={}, minSize={}, maxSize={}", 
                    Arrays.toString(metricName), val, metricLowerBound, metricUpperBound, currentSize, minSize, maxSize
        }
    }
    
    // TODO Could have throttling etc so don't repeatedly call grow; standard control theory stuff such as
    //      PID design (proportional-integral-derivative)
    // TODO Could show example of overriding this to do something smarter. For example, if metric is a number then
    //      grow/shrink by some scale, e.g. grow by (metric <= lowerBound ? 0 : (metric < lowerBound*2 ? 1 : (metric < lowerBound*4 ? 2 : 3))) 
{'fraction_non_alphanumeric': 0.08359343194463292, 'fraction_numerical': 0.0024426652191613515, 'mean_word_length': 2.522944550669216, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6716967', 'n_tokens_mistral': 1995, 'n_tokens_neox': 1940, 'n_words': 523}