All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.engineblock.activityapi.rates.AverageRateLimiter Maven / Gradle / Ivy

Go to download

The driver API for engineblock; Provides the interfaces needed to build drivers that can be loaded by engineblock core

There is a newer version: 2.12.65
Show newest version
/*
 *
 *    Copyright 2016 jshook
 *    Licensed 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 io.engineblock.activityapi.rates;

import com.codahale.metrics.Gauge;
import io.engineblock.activityapi.core.Startable;
import io.engineblock.activityimpl.ActivityDef;
import io.engineblock.metrics.ActivityMetrics;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.atomic.AtomicLong;

public class AverageRateLimiter implements Startable, RateLimiter {
    private static final Logger logger = LoggerFactory.getLogger(AverageRateLimiter.class);
    private String label;
    private ActivityDef activityDef;
    private RateSpec rateSpec;
    private long opTicks = 0L; // Number of nanos representing one grant at target rate
    protected final AtomicLong ticksTimeline = new AtomicLong(0L);
    private final AtomicLong accumulatedDelayNanos = new AtomicLong(0L);
    private volatile long lastSeenNanoTime;
    private volatile boolean isBursting = false;

    private State state = State.Idle;

    //private Counter fastpathCounter;
    //private Counter sleepCounter;
    private Gauge delayGauge;
    private Gauge avgRateGauge;
    //private Gauge burstRateGauge;

    protected AverageRateLimiter() {
    }

    /**
     * Create a rate limiter.
     *
     * @param def The activity definition for this rate limiter
     * @param label The label for the rate limiting facet within the activity
     * @param rateSpec the rate limiter configuration
     */
    public AverageRateLimiter(ActivityDef def, String label, RateSpec rateSpec) {
        setActivityDef(def);
        setLabel(label);
        this.setRateSpec(rateSpec);
        init();
    }

    protected void setLabel(String label) {
        this.label = label;
    }

    protected void setActivityDef(ActivityDef def) {
        this.activityDef = def;
    }

    protected void init() {
        this.delayGauge = ActivityMetrics.gauge(activityDef, label+".cco_delay_gauge", new RateLimiters.WaitTimeGuage(this));
        //this.sleepCounter = ActivityMetrics.counter(activityDef, label + "_ratelogic.sleep_counter");
        //this.fastpathCounter = ActivityMetrics.counter(activityDef, label + "_ratelogic.fast_counter");
        this.avgRateGauge = ActivityMetrics.gauge(activityDef, label + ".avg_targetrate_gauge", new RateLimiters.RateGauge(this));
        //this.burstRateGauge = ActivityMetrics.gauge(activityDef, label + "_ratelogic.burst_targetrate_gauge", new RateLimiters.BurstRateGauge(this));
        start();
    }

    protected long getNanoClockTime() {
        return System.nanoTime();
    }

    @Override
    public long acquire() {
        long opScheduleTime = ticksTimeline.getAndAdd(opTicks);
        long timelinePosition = lastSeenNanoTime;

        if (opScheduleTime < timelinePosition) {
            isBursting = true;
            return timelinePosition - opScheduleTime;
        }

        long delay = timelinePosition - opScheduleTime;
        if (delay <= 0) {

            timelinePosition = getNanoClockTime();
            lastSeenNanoTime = timelinePosition;
            delay = timelinePosition - opScheduleTime;

            // This only happens if the callers are ahead of schedule
            if (delay < 0) {
                isBursting = false;
                delay *= -1;
                try {
                    //sleepCounter.inc();
                    Thread.sleep(delay / 1000000, (int) (delay % 1000000L));
                } catch (InterruptedException ignored) {
                }
                return 0L;
            }
        }

        return delay;
    }


    public long getOpNanos() {
        return opTicks;
    }

    @Override
    public long getTotalWaitTime() {
        return getWaitTime() + accumulatedDelayNanos.get();    }

    @Override
    public long getWaitTime() {
        return getNanoClockTime() - ticksTimeline.get();    }


    public synchronized void start() {
        switch (state) {
            case Started:
                break;
            case Idle:
                sync();
                state = State.Started;
                break;
        }
    }

    private synchronized void sync() {
        long nanos = getNanoClockTime();
        this.lastSeenNanoTime = nanos;

        switch (this.state) {
            case Idle:
                this.ticksTimeline.set(nanos);
                this.accumulatedDelayNanos.set(0L);
                break;
            case Started:
                accumulatedDelayNanos.addAndGet(getWaitTime());
                break;
        }
    }

    public String toString() {
        return "spec=[" + label + "]:" + rateSpec.toString() +
                ", delay=" + this.getWaitTime() +
                ", total=" + this.getTotalWaitTime() +
                ", (used/seen)=(" + ticksTimeline.get() + "/" + lastSeenNanoTime + ")" +
                ", (clock,actual)=(" + getNanoClockTime() + "," + System.nanoTime() + ")";
    }

    @Override
    public RateSpec getRateSpec() {
        return this.rateSpec;
    }

    @Override
    public void setRateSpec(RateSpec updatingRateSpec) {
        RateSpec oldRateSpec = this.rateSpec;
        this.rateSpec=updatingRateSpec;

        if (oldRateSpec!=null && oldRateSpec.equals(this.rateSpec)) {
            return;
        }

        this.opTicks = updatingRateSpec.getCalculatedNanos();
        this.rateSpec = updatingRateSpec;
        switch (this.state) {
            case Started:
                sync();
            case Idle:
        }
    }

    /**
     * * visible for testing
     *
     * @return ticksTimeline value - the long value of the shared timeslice marker
     */
    public AtomicLong getTicksTimeline() {
        return this.ticksTimeline;
    }

    /**
     * visible for testing
     *
     * @return lastSeenNanoTime - the long value of the shared view of the clock
     */
    public long getLastSeenNanoTimeline() {
        return this.lastSeenNanoTime;
    }

    private enum State {
        Idle,
        Started
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy