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

io.nosqlbench.engine.api.activityapi.ratelimits.RateSpec Maven / Gradle / Ivy

Go to download

The engine API for nosqlbench; Provides the interfaces needed to build internal modules for the nosqlbench core engine

There is a newer version: 5.17.0
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.nosqlbench.engine.api.activityapi.ratelimits;

import io.nosqlbench.engine.api.activityimpl.ParameterMap;
import io.nosqlbench.engine.api.util.Unit;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;

/**
 * 

Rate Limiter Specifications

* *

A rate spec represent the configuration of a rate limiter. It is the event carrier * for applying changes to a rate limiter. For scripting purposes, rate limiters can be * controlled via assignment of a configuration string. A future version of the scripting * API will support direct method access. For now, the following semantics will apply * to the configuration value assigned to any of the rate parameters like cyclerate and striderate. *

* *

Controlling Rate Limiters

* * Rate limiters specifiers can be easily constructed programmatically. However, in scripting, * these will often be controlled by assigning a configuration string. * *

* When a rate limiter is configured * using the configuration String, it can be in one of the following forms: * *

    *
  • <rate>
  • *
  • <rate>,<burst ratio>
  • *
  • <rate>,<burst ratio>,<verb>
  • *
* * Where: * * rate is the ops per second, expressed as any positive floating point value. * burst ratio is a floating point value greater than 1.0 which determines how much faster * the rate limiter may go to catch up to the overall. * verb is one of configure, start, or restart, as explained below. * * For example: *
    *
  • 200 - allow up to 200 ops to start per second, with the default burst ratio of 1.1. * Start the rate limiter automatically.
  • *
  • 3.6,2 - Allow up to 3.6 ops to start per second, but allow up to 7.2 ops per second * if needed to catch up. Start the rate limiter automatically
  • *
  • 1000,1.05,restart - Allow up to 1000 ops per second on average, but allow 1050 ops per second * if the workload gets behind. If the rate limiter was already running, restart it, clearing any * previous backlog (wait time) and resource pools.
  • *
* *

Rate Limiter Life Cycle

* *

A rate limiter can be directly configured, started, restarted. Indirectly, rate * limiter construction and initialization occurs as needed.

* *
* *
(constructed)
*
Rate limiters are constructed and assigned automatically when they are first configured. A rate * limiter may not be explicitly constructed by the user.
* *
configure
*
When a rate limiter is configured, the rate spec is applied to it. If the rate limiters is * already running, then this new rate is applied to it. Applying the same rate parameters a * second time to a given rate limiter is a no-op.
* *
(initialization)
*
A rate limiter is initialized immediately before it is initially started. Rate limiters are also * re-initialized when they are restarted. When a rate limiter is initialized, the start time is set * to the current time, and the resource pools and accumulated wait time are zeroed. A rate limiter may * not be explicitly initialized by the user. *
* *
start
*
Starting a rate limiter activates the rate metering logic according to the current time. From * the time a rate limiter is started, any unused time will accumulate as wait time. A rate limiter is required * to be reset immediately before it is started for the first time.
* *
restart
*
Restarting a rate limiter is the same as starting it initially. The only difference is that * restarting forces a re-initialization as part of the configuration.
* *
*/ public class RateSpec { private final static Logger logger = LogManager.getLogger(RateSpec.class); public static final double DEFAULT_RATE_OPS_S = 1.0D; public static final double DEFAULT_BURST_RATIO = 1.1D; public static Verb DEFAULT_VERB = Verb.start; /** * Target rate in Operations Per Second */ public double opsPerSec = DEFAULT_RATE_OPS_S; public double burstRatio = DEFAULT_BURST_RATIO; public Verb verb = Verb.start; public enum Verb { /** * Specify that a rate limiter should only be configured without affecting its running state. * If the rate limiter is already running, then the configuration should take effect immediately. * A rate limiter will be created automatically if needed. Configurations that do not effectively * change the rate limiter are ignored. This does not automatically start the rate limiter. It * will need to be started explicitly before it is used. */ configure, /** * The default SetAction is start. This means that the rate limiter should be started at the time * this rate spec is applied. The start time of a rate limiter is significant in that it determines * both the calculated average rate as well as the accumulated wait time from slow callers. In order * to start, a rate limiter will be configured automatically, if the provided rate spec would cause * a change to the configuration. If a rate limiter is started that is already running, an error should * be thrown. If it is desired to ignore this condition, the restart should be used instead. */ start, /** * The restart action on a rate limiter causes it to be re-initialized as if it were just being * started for the first time. This causes any accumulated wait time or time resources to be zeroed. * This type of specifier can be useful, for example, when iterating on a workload with different * target rates, where each iteration is independent of the others. In order to restart, a rate * limiter will be configured if necessary. */ restart } public RateSpec(double opsPerSec, double burstRatio) { this(opsPerSec, burstRatio, DEFAULT_VERB); } public RateSpec(double opsPerSec, double burstRatio, Verb type) { this.opsPerSec = opsPerSec; this.burstRatio = burstRatio; this.verb = type; } public RateSpec(ParameterMap.NamedParameter tuple) { this(tuple.value); if (tuple.name.startsWith("co_")) { logger.warn("The co_ prefix on " + tuple.name + " is no longer needed. All rate limiters now provide standard coordinated omission metrics."); } } public RateSpec(String spec) { String[] specs = spec.split("[,:;]"); switch (specs.length) { case 3: verb = Verb.valueOf(specs[2].toLowerCase()); logger.debug("selected rate limiter type: " + verb); case 2: burstRatio = Double.valueOf(specs[1]); if (burstRatio < 1.0) { throw new RuntimeException("burst ratios less than 1.0 are invalid."); } case 1: opsPerSec = Unit.doubleCountFor(specs[0]).orElseThrow(() -> new RuntimeException("Unparsable:" + specs[0])); break; default: throw new RuntimeException("Rate specs must be either '' or ':' as in 5000.0 or 5000.0:1.0"); } } public String toString() { double ratePortion = Math.abs(opsPerSec - ((long) opsPerSec)); String ratefmt = (ratePortion > 0.001D) ? String.format("%,.3f", opsPerSec) : String.format("%,d", (long) opsPerSec); double br = burstRatio * opsPerSec; double burstPortion = Math.abs(br - ((long) br)); String burstfmt = (burstPortion > 0.001D) ? String.format("%,.3f", br) : String.format("%,d", (long) br); return String.format("{ rate:'%s', burstRatio:'%.3f', SOPSS:'%s', BOPSS:'%s', verb:'%s' }", ratefmt, burstRatio, ratefmt, burstfmt, verb); } public RateSpec withOpsPerSecond(double rate) { return new RateSpec(rate, this.burstRatio); } public RateSpec withBurstRatio(double burstRatio) { return new RateSpec(this.opsPerSec, burstRatio); } public RateSpec withVerb(Verb verb) { return new RateSpec(this.opsPerSec, this.burstRatio, verb); } public long getNanosPerOp() { return (long) (1E9 / opsPerSec); } public double getRate() { return this.opsPerSec; } public double getBurstRatio() { return this.burstRatio; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; RateSpec rateSpec = (RateSpec) o; if (Double.compare(rateSpec.opsPerSec, opsPerSec) != 0) return false; return Double.compare(rateSpec.burstRatio, burstRatio) == 0; } @Override public int hashCode() { int result; long temp; temp = Double.doubleToLongBits(opsPerSec); result = (int) (temp ^ (temp >>> 32)); temp = Double.doubleToLongBits(burstRatio); result = 31 * result + (int) (temp ^ (temp >>> 32)); return result; } public Verb getVerb() { return this.verb; } public boolean isAutoStart() { return this.verb == Verb.start || this.verb == Verb.restart; } public boolean isRestart() { return this.verb == Verb.restart; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy