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

com.swirlds.common.utility.throttle.Throttle Maven / Gradle / Ivy

Go to download

Swirlds is a software platform designed to build fully-distributed applications that harness the power of the cloud without servers. Now you can develop applications with fairness in decision making, speed, trust and reliability, at a fraction of the cost of traditional server-based platforms.

There is a newer version: 0.56.6
Show newest version
/*
 * Copyright (C) 2019-2024 Hedera Hashgraph, LLC
 *
 * 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 com.swirlds.common.utility.throttle;

import static com.swirlds.base.units.UnitConstants.NANOSECONDS_TO_SECONDS;

/**
 * Each instance of this class can be used to throttle some kind of flow, to allow only a certain number of transactions
 * per second or bytes per second or events per second etc.
 * 

* For throttling transactions per second, the instance remembers how many transactions have occurred recently, and will * then answer whether a new transaction is allowed, or should be blocked. This also works for sets of transactions, * where the entire set is either accepted or rejected. So, to limit a flow of bytes per second, each byte can be * treated as a "transaction", and a block of 1kB can be considered a set of 1024 transactions. *

* Given the number of transactions per second (tps) and the max number of seconds worth of transactions that could ever * come in a single burst (burstPeriod), this uses a leaky bucket model to throttle it. Each allowed transaction * increases the contents of the bucket by 1. Each nanosecond decreases the contents of the bucket by a billionth of * tps. If the next transaction or block of transactions would fill the bucket to more than tps * burstPeriod, then * that transaction or block is disallowed. *

* For example, to throttle smart contract calls to 1.5 per second on this computer, it would be instantiated as: * *

{@code
 * Throttle contractThrottle = new Throttle(1.5);   //throttle to 1.5 tps for this node
 * }
*

* and then when a transaction is received, do this: * *

{@code
 * if (contractThrottle.allow()) {
 *    //accept the transaction
 * } else {
 *     //reject the transaction because BUSY
 * }
 * }
*/ public class Throttle { // allow a max of tps transactions per second, on average private volatile double tps; // after a long time of no transactions, allow a burst of tps * burstPeriod transactions all at once. This is how // long it takes the bucket to leak empty. private volatile double burstPeriod; // the size of the bucket private volatile double capacity; // amount of transaction traffic we have had recently. This is always in the range [0, capacity]. private volatile double traffic; // the last time a transaction was received private volatile long lastTime; /** * Start throttling a new flow, allowing tps transactions per second, and bursts of at most tps transactions at * once. * * @param tps the max transactions per second (on average) that is allowed (negative values treated as 0) */ public Throttle(double tps) { this(tps, 1); } /** * Start throttling a new flow, allowing tps transactions per second, and bursts of at most tps * burstPeriod * transactions at once. * * @param tps the max transactions per second (on average) that is allowed (negative values treated as 0) * @param burstPeriod bursts can allow at most this many seconds' worth of transactions at once (negative values * treated as 0) */ public Throttle(double tps, double burstPeriod) { this.tps = Math.max(0, tps); this.burstPeriod = Math.max(0, burstPeriod); this.capacity = tps * burstPeriod; this.traffic = 0; this.lastTime = System.nanoTime(); } /** * Gets the computed capacity of this {@link Throttle} based on the formula {@code tps * burstPeriod}. * * @return the computed capacity */ public synchronized double getCapacity() { return capacity; } /** get max transactions per second allowed, on average */ public synchronized double getTps() { return tps; } /** * Set max transactions per second allowed, on average. * * @param tps max transactions per second (negative values will be treated as 0) */ public synchronized void setTps(double tps) { this.tps = Math.max(0, tps); capacity = this.tps * this.burstPeriod; } /** get the number of seconds worth of traffic that can occur in a single burst */ public synchronized double getBurstPeriod() { return burstPeriod; } /** * set the number of seconds worth of traffic that can occur in a single burst * * @param burstPeriod bursts can allow at most this many seconds' worth of transactions at once (negative values * treated as 0) */ public synchronized void setBurstPeriod(double burstPeriod) { this.burstPeriod = Math.max(0, burstPeriod); this.capacity = this.tps * this.burstPeriod; } /** * Can one more transaction be allowed right now? If so, return true, and record that it was allowed (which will * reduce the number allowed in the near future) * * @return can this number of transactions be allowed right now? */ public synchronized boolean allow() { return allow(1); } /** * Can the given number of transactions be allowed right now? If so, return true, and record that they were allowed * (which will reduce the number allowed in the near future) * * @param amount the number of transactions in the block (must be nonnegative) * @return can this number of transactions be allowed right now? */ public synchronized boolean allow(double amount) { // when a new transaction comes in, do this: long t = System.nanoTime(); traffic = Math.min( capacity, Math.max(0, traffic - (t - lastTime) * tps * NANOSECONDS_TO_SECONDS)); // make the bucket // leak lastTime = t; if (amount < 0 || traffic + amount > capacity) { return false; } traffic += amount; return true; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy