Skip to content

Commit

Permalink
Implement frontend throttle (#8183)
Browse files Browse the repository at this point in the history
Signed-off-by: Valentin Tronkov <[email protected]>
  • Loading branch information
vtronkov authored Aug 24, 2023
1 parent 13f953b commit a21ce59
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import com.hedera.hapi.node.transaction.Query;
import com.hedera.hapi.node.transaction.SignedTransaction;
import com.hedera.hapi.node.transaction.TransactionBody;
import com.hedera.node.app.hapi.utils.sysfiles.domain.throttling.ThrottleDefinitions;
import com.hedera.node.app.service.mono.pbj.PbjConverter;
import com.hedera.node.app.service.mono.throttling.FunctionalityThrottling;
import com.hedera.node.app.service.mono.throttling.annotations.HapiThrottle;
Expand All @@ -38,11 +39,16 @@
*/
@Singleton
public class MonoThrottleAccumulator implements ThrottleAccumulator {

private final FunctionalityThrottling hapiThrottling;

@Inject
public MonoThrottleAccumulator(@HapiThrottle final FunctionalityThrottling hapiThrottling) {
public MonoThrottleAccumulator(
@HapiThrottle final FunctionalityThrottling hapiThrottling, ThrottleManager throttleManager) {
this.hapiThrottling = hapiThrottling;

// adding the throttle definition configuration
this.hapiThrottling.rebuildFor(ThrottleDefinitions.fromProto(throttleManager.throttleDefinitionsProto()));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ public class ThrottleManager {
private static final ThrottleDefinitions DEFAULT_THROTTLE_DEFINITIONS = ThrottleDefinitions.DEFAULT;

private ThrottleDefinitions throttleDefinitions;
private com.hederahashgraph.api.proto.java.ThrottleDefinitions throttleDefinitionsProto;
private List<ThrottleBucket> throttleBuckets;

public ThrottleManager() {
Expand All @@ -53,6 +54,8 @@ public void update(@NonNull final Bytes bytes) {
// Parse the throttle file. If we cannot parse it, we just continue with whatever our previous rate was.
try {
throttleDefinitions = ThrottleDefinitions.PROTOBUF.parse(bytes.toReadableSequentialData());
throttleDefinitionsProto =
com.hederahashgraph.api.proto.java.ThrottleDefinitions.parseFrom(bytes.toByteArray());
} catch (final Exception e) {
// Not being able to parse the throttle file is not fatal, and may happen if the throttle file
// was too big for a single file update for example.
Expand Down Expand Up @@ -85,4 +88,8 @@ public List<ThrottleBucket> throttleBuckets() {
public ThrottleDefinitions throttleDefinitions() {
return throttleDefinitions;
}

public com.hederahashgraph.api.proto.java.ThrottleDefinitions throttleDefinitionsProto() {
return throttleDefinitionsProto;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,28 @@

import static com.hedera.hapi.node.base.HederaFunctionality.CRYPTO_TRANSFER;
import static com.hedera.node.app.service.mono.pbj.PbjConverter.fromPbj;
import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import com.google.protobuf.InvalidProtocolBufferException;
import com.hedera.hapi.node.base.AccountID;
import com.hedera.hapi.node.base.HederaFunctionality;
import com.hedera.hapi.node.base.TransactionID;
import com.hedera.hapi.node.token.CryptoTransferTransactionBody;
import com.hedera.hapi.node.transaction.Query;
import com.hedera.hapi.node.transaction.ThrottleBucket;
import com.hedera.hapi.node.transaction.ThrottleDefinitions;
import com.hedera.hapi.node.transaction.ThrottleGroup;
import com.hedera.hapi.node.transaction.TransactionBody;
import com.hedera.node.app.service.mono.throttling.FunctionalityThrottling;
import com.hedera.node.app.service.mono.utils.accessors.TxnAccessor;
import com.hedera.pbj.runtime.io.buffer.Bytes;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
Expand All @@ -40,28 +49,58 @@

@ExtendWith(MockitoExtension.class)
class MonoThrottleAccumulatorTest {

@Mock
private FunctionalityThrottling hapiThrottling;

private MonoThrottleAccumulator subject;

@Mock
private ThrottleManager throttleManager;

ThrottleGroup throttleGroup = ThrottleGroup.newBuilder()
.operations(List.of(HederaFunctionality.CRYPTO_CREATE, HederaFunctionality.FREEZE))
.milliOpsPerSec(100)
.build();

ThrottleBucket throttleBucket = ThrottleBucket.newBuilder()
.name("throttle1")
.burstPeriodMs(100L)
.throttleGroups(throttleGroup)
.build();

ThrottleDefinitions throttleDefinitions = com.hedera.hapi.node.transaction.ThrottleDefinitions.newBuilder()
.throttleBuckets(throttleBucket)
.build();
Bytes throttleDefinitionsByes =
com.hedera.hapi.node.transaction.ThrottleDefinitions.PROTOBUF.toBytes(throttleDefinitions);

@BeforeEach
void setUp() {
subject = new MonoThrottleAccumulator(hapiThrottling);
void setUp() throws InvalidProtocolBufferException {
com.hederahashgraph.api.proto.java.ThrottleDefinitions throttleDefinitionsProto =
com.hederahashgraph.api.proto.java.ThrottleDefinitions.parseFrom(throttleDefinitionsByes.toByteArray());
when(throttleManager.throttleDefinitionsProto()).thenReturn(throttleDefinitionsProto);

subject = new MonoThrottleAccumulator(hapiThrottling, throttleManager);
}

@Test
void verifyTheConstructorIsConfiguringTheThrottleDefinitions() {
verify(throttleManager, times(1)).throttleDefinitionsProto();
verify(hapiThrottling, times(1)).rebuildFor(any());
}

@Test
void delegatesToMonoThrottlingForTransactions() {
final ArgumentCaptor<TxnAccessor> captor = ArgumentCaptor.forClass(TxnAccessor.class);

final var txnFunction = CRYPTO_TRANSFER;
given(hapiThrottling.shouldThrottleTxn(any())).willReturn(true);

assertTrue(subject.shouldThrottle(TRANSACTION_BODY));

verify(hapiThrottling).shouldThrottleTxn(captor.capture());
final var throttledFunction = captor.getValue().getFunction();
assertEquals(fromPbj(txnFunction), throttledFunction);
assertEquals(fromPbj(CRYPTO_TRANSFER), throttledFunction);
}

@Test
Expand Down

0 comments on commit a21ce59

Please sign in to comment.